样式是:
需求概述
我们需要一个类似原生RatingBar的这样一个评分View,但是RatingBar不够灵活,我们需要有不同颜色,不同间距,而且可调的一个评分Bar。
分析
我们主要是定义ratingbar,不和文字一起定义。
首先是:单个星星的高和宽,然后是需要知道每个星星之间的间距,还有就是那几颗星星是active那几颗是disactivie的。同时,我们需要计算在这个View中的什么位置开始画,最后就是需要指定我们的星星的resource了。
实现
属性定义
需要定义的属性有:
<declare-styleable name="CustomRatingStyle">
<!--宽,高,间距,激活的数量,没有激活的数量,激活的icon,没有激活的icon-->
<!--属性分别是:单个的宽,高,之间的距离,激活的数量,总数量,激活的drawable,没有激活的drawable-->
<attr name="custom_rate_width" format="dimension"/>
<attr name="custom_rate_height" format="dimension"/>
<attr name="custom_rate_padding" format="dimension"/>
<attr name="custom_rate_active_size" format="integer"/>
<attr name="custom_rate_size" format="integer"/>
<attr name="custom_rate_active_drawable" format="reference"/>
<attr name="custom_rate_disactive_drawable" format="reference"/>
</declare-styleable>
代码实现:
我们继承自View就好,不要ViewGroup。然后获取xml文件中的自定义属性值,获取属性值完成,同时给定默认值。同时,我们需要计算一个步长,作用是知道我们画下一个星星开始的坐标,他的计算是星星的width+星星之间的padding。同时,还有一点是可能active和disactivie的drawable大小不同,所以我们需要按照其中一个区计算步长,同时缩放到等同大小。比如我们按照active的drawable大小为标准,当disactive的drawable大小不同的时候,就对他进行比例缩放,直到相同。代码如下:
代码实现
//单个高
private float singleWidth;
//单个宽
private float singleHeight;
//间距
private int padding;
//总数量
private int size = 5;
//默认激活数量
private int activeSize = 3;
//激活的bitmap
private Bitmap activeBitmap;
//没有激活的bitmap
private Bitmap disactiveBitmap;
//画笔
private Paint mPaint;
//步长
private int stepSize;
//开始画的x坐标
private int drawStartX;
//开始画的y坐标
private int drawStartY;
初始化代码:
int activeId = 0;
int disactiveId = 0;
if (attrs != null) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomRatingStyle);
singleWidth = array.getDimensionPixelOffset(R.styleable.CustomRatingStyle_custom_rate_width, 0);
singleHeight = array.getDimensionPixelOffset(R.styleable.CustomRatingStyle_custom_rate_height, 0);
activeId = array.getResourceId(R.styleable.CustomRatingStyle_custom_rate_active_drawable, R.drawable.custom_rateingbar_active);
disactiveId = array.getResourceId(R.styleable.CustomRatingStyle_custom_rate_disactive_drawable, R.drawable.custom_rateingbar_disactive);
size = array.getInteger(R.styleable.CustomRatingStyle_custom_rate_size, 5);
activeSize = array.getInteger(R.styleable.CustomRatingStyle_custom_rate_active_size, 3);
padding = array.getDimensionPixelOffset(R.styleable.CustomRatingStyle_custom_rate_padding, 10);
array.recycle();
}
activeBitmap = BitmapFactory.decodeResource(getResources(), activeId);
disactiveBitmap = BitmapFactory.decodeResource(getResources(), disactiveId);
stepSize = padding + activeBitmap.getWidth();
mPaint = new Paint();
if (singleHeight <= 0) {
singleHeight = activeBitmap.getHeight();
}
if (singleWidth <= 0) {
singleWidth = activeBitmap.getWidth();
}
//大小不同的话,对Bitmap进行压缩,
if (activeBitmap.getWidth() != disactiveBitmap.getWidth() || activeBitmap.getHeight() != disactiveBitmap.getHeight()) {
//把dis压缩或者是放大的active的大小
disactiveBitmap = Bitmap.createScaledBitmap(disactiveBitmap, activeBitmap.getWidth(), activeBitmap.getHeight(), false);
}
逻辑实现
测量步长计算:我们是把五角星放在这个View的center位置,然后他所占的长度是5width+4padding。
onMeasure 方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//计算宽
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
width = 5 * stepSize;
}
//计算高
if (heightMode == MeasureSpec.EXACTLY) {
height = heightSize;
} else {
height = (int) singleHeight;
}
//总共应该的宽
int drawWidth = (int) (size * singleWidth + (size <= 1 ? 0 : (size - 1) * padding));
//开始的y位置
int drawHeight = (int) ((height - singleHeight) / 2);
drawStartX = (width - drawWidth) / 2;
drawStartY = drawHeight > 0 ? drawHeight : 0;
setMeasuredDimension(width, height);
}
onDraw方法:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//开始画active
for (int i = 0; i < activeSize; i++) {
canvas.drawBitmap(activeBitmap, drawStartX + i * stepSize, drawStartY, mPaint);
}
//开始画disactive
for (int i = activeSize; i < size; i++) {
canvas.drawBitmap(disactiveBitmap, drawStartX + i * stepSize, drawStartY, mPaint);
}
}
然后,我们在对外提供一个设置activie的方法:
/**
* 设置激活的数量
*
* @param activeSize
*/
public void setActiveSize(int activeSize) {
this.activeSize = activeSize;
if (CommentUtil.isUIThread()) {
invalidate();
} else {
postInvalidate();
}
}
这样子就完成了星星评分的UI了。
结果截图: