Flutter开发并编写了一个前沿的星级检查器
工作中的需求之一是显示游戏评分,但Flutter不提供现成的评分。您还可以在线找到有关自定义评级控制的相关文章。有点违背我自己的需求,所以这里再写一篇文章。国际惯例,上图
要点
关键是要有三点才能达到好的效果
- 未选星显示
- 满星显示
- 未满星显示
星级评定原则是使用未选星星作为背景,顶层使用满星和未满星覆盖显示,达到百分比效果
- 使用堆叠布局来控制两种样式的重叠
- 使用ClipRect裁剪显示未满星效果
详细实现
首先我们实现静态评分效果。
我们可以预先设计别人使用时可以修改的内容,比如星星数量、星星间距、星星大小等。这里我把我先写的组件参数提取出来。大家可以根据自己的需要进行扩展
不多说了,这里是代码
class RatingBar extends StatefulWidget {
final int count;
final double maxRating;
final double value;
final double size;
final double padding;
final String nomalImage;
final String selectImage;
final bool selectAble;
final ValueChanged<String> onRatingUpdate;
RatingBar({
this.maxRating = 10.0,
this.count = 5,
this.value = 10.0,
this.size = 20,
this.nomalImage,
this.selectImage,
this.padding,
this.selectAble = false,
@required this.onRatingUpdate
}) : assert(nomalImage != null),
assert(selectImage != null);
@override
_RatingBarState createState() => _RatingBarState();
}复制代码
1.背景星星
我们只做横向效果,所以可以用Row来打造星星排列效果
List<Widget> buildNomalRow() {
List<Widget> children = [];
for(int i = 0; i < widget.count; i ++) {
children.add(Image.asset(widget.nomalImage,height: widget.size,width: widget.size,));
if(i < widget.count - 1) {
children.add(SizedBox(width: widget.padding,));
}
}
return children;
}复制代码
每个添加 SizedBox 作为星星之间的间隔
2。满星和未满星
我们根据分数和最大星星数计算出每个星星对应的分数比例,然后根据分数比例计算满星和未满星的数量之间的裁剪比例
int fullStars() {
if(value != null) {
return (value /(widget.maxRating/widget.count)).floor();
}
return 0;
}
double star() {
if(value != null) {
if(widget.count / fullStars() == widget.maxRating / value ) {
return 0;
}
return (value % (widget.maxRating/widget.count))/(widget.maxRating/widget.count);
}
return 0;
}
List<Widget> buildRow() {
int full = fullStars();
List<Widget> children = [];
for(int i = 0; i < full; i ++) {
children.add(Image.asset(widget.selectImage,height: widget.size,width: widget.size,));
if(i < widget.count - 1) {
children.add(SizedBox(width: widget.padding,),);
}
}
if(full < widget.count) {
children.add(ClipRect(
clipper: SMClipper(rating: star() * widget.size),
child: Image.asset(widget.selectImage,height: widget.size,width: widget.size),
));
}
return children;
}复制代码
计算满星数量:
每颗星星对应的分数值=最高分/星星数量
当前评分/星星得分四舍五入即为当前满星数量
未满星作物比例:
当前评分系统除以星星分数就是去掉满星后的分数。然后把这个值除以星星分数,就是当前未满星星星的裁剪比例
Crop
因为我们只需要垂直裁剪,所以使用ClipRect就可以满足需求
class SMClipper extends CustomClipper<Rect>{
final double rating;
SMClipper({
this.rating
}): assert(rating != null);
@override
Rect getClip(Size size) {
return Rect.fromLTRB(0.0, 0.0, rating , size.height);
}
@override
bool shouldReclip(SMClipper oldClipper) {
return rating != oldClipper.rating;
}
}复制代码
至此,静态评价显示控制的内容已经写好了。看看效果
动态
现在分数显示出来了,肯定需要一个能得分的人。我们需要在原有的基础上监听控件的触摸和点击事件,并计算出触摸结束的点。 ,获取当前点代表的评估值监听运动
如果你正在监听手势动作,我们可以使用Listener。这里我们需要用到它的两个回调方法onPointerMove和onPointerDown(或者onPointerUp)。第一个用于跟踪滑动,第二个用于跟踪点击
Listener(
child: buildRowRating(),
onPointerDown: (PointerDownEvent event){
double x = event.localPosition.dx;
if (x < 0) x = 0;
pointValue(x);
},
onPointerMove: (PointerMoveEvent event) {
double x = event.localPosition.dx;
if (x < 0) x = 0;
pointValue(x);
},
onPointerUp: (_) {
},
behavior: HitTestBehavior.deferToChild,
)复制代码
根据手势的 x 坐标计算当前手指位置代表的评估值,获得后开始重建。这里的问题是去掉星星之间的间隔以保证准确性
pointValue(double dx) {
if(!widget.selectAble) {
return;
}
if(dx >= widget.size * widget.count + widget.padding * (widget.count - 1)) {
value = widget.maxRating;
}else {
for(double i = 1; i < widget.count + 1;i ++) {
if(dx > widget.size * i + widget.padding *(i -1) && dx < widget.size * i + widget.padding * i) {
value = i * (widget.maxRating/widget.count);
break;
}else if(dx > widget.size * (i -1) + widget.padding*(i -1) && dx < widget.size * i+ widget.padding*i ) {
value = (dx - widget.padding *(i -1))/(widget.size * widget.count ) *widget.maxRating;
break;
}
}
}
setState(() {
widget.onRatingUpdate(value.toStringAsFixed(1));
});
}复制代码
这样可以动态评分的空间就被填满了。使用方法见下文
RatingBar(
value: 9,
size: 30,
padding: 5,
nomalImage: 'img/star_nomal.png',
selectImage: 'img/star.png',
selectAble: true,
onRatingUpdate: (value) {},
maxRating: 10,
count: 6,
)复制代码
各参数说明
- value:当前评级值
- size:星星大小
- padding:星星之间的间隙
- nomalImage:空星星图像
- selectAble :是否可以点击滑动编辑评分值
- onRatingUpdate:点击滑动编辑回调评分值,参数为 String 评分值
- maxRating:最大评分值
- number:星星数
作者:sponmas
链接:https://juejin.im/post/5d6f9349f265da03f66de01b
来源:掘金
如需商业转载请联系作者授权。非商业转载请注明来源。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。