自定义Android 星级评分控件,先上一下效果图
关于控件的制作步骤:
1.首先要确定控件所需要用到的属性,那位作为一个星级评分的控件,能想到的就是大小,形状,数量,评分设置标准,是否允许点击操作,监听等内容,所以我定义了以下属性
public class Star extends View {
//星星评分
private float starMark = 0.0F;
//星星个数
private int starNum = 5;
//星星高度
private int starHeight;
//星星宽度
private int starWidth;
//星星间距
private int starDistance;
//星星背景
private Drawable starBackgroundBitmap;
//动态星星
private Bitmap starDrawDrawable;
//星星变化监听
private OnStarChangeListener changeListener;
//是否可以点击
private boolean isClick = true;
//画笔
private Paint mPaint;
}
然后为这个类添加两个构造方法
public Star(Context mContext, AttributeSet attrs) {
super(mContext, attrs);
}
public Star(Context mContext, AttributeSet attrs, int defStyleAttr) {
super(mContext, attrs, defStyleAttr);
}
可能有些人不理解attrs这个参数的作用,在我们使用过的很多自定义控件的构造方法里都会涉及到,他的作用就是来传递自定义的控件在xml里布局设置的对应的属性。
2.经过1步骤之后,可以说自定义类的框架已经出来了,接着了解下自定义布局的核心的内容。我找了一张绘制view的流程图
这个图的意思就是绘制view的主要三个步骤,measure测量,layout布局,draw绘制,测量控件所占用的宽高,决定把他放在那块布局上,然后画出来我们要的样式,不一定每个步骤都需要自己来完成,自己做自定义的话只需要完成draw这一步就可以了,像图中所说的方法measure();layout();draw();是不能被直接重写的,但是给我们提供了可以重写的方法,onMeasure();onLayout();onDraw();我重写了onMeasure();onDraw();两个方法,道理是如果像自己的控件支持设置宽高度warp_content,那么必须重写下这个方法,ondraw();是真正这次作自定义需要的内容,关于自定义view的原理,我这边找到了一篇很好文章,分享给大家https://gold.xitu.io/entry/580467dc8ac24700580a4b1a
下面是我重写的ondraw()的内容
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (null == starDrawDrawable || null == starBackgroundBitmap) {
return;
}
for (int i = 0; i < starNum; i++) {
starBackgroundBitmap.setBounds(starDistance * i + starWidth * i, 0, starWidth * (i + 1) + starDistance * i, starHeight);
starBackgroundBitmap.draw(canvas);
}
if (starMark > 1) {//是否只有一颗星
//如果超过一颗星,先画一颗星,在判断是否剩下的是整颗的某几颗星,如果是整颗,直接剩下几颗画几颗,如果不是,画完整颗的在画剩余的
canvas.drawRect(0, 0, starWidth, starHeight, mPaint);
if (starMark - (int) (starMark) == 0) {
for (int i = 1; i < starMark; i++) {
canvas.translate(starDistance + starWidth, 0);
canvas.drawRect(0, 0, starWidth, starHeight, mPaint);
}
} else {
for (int i = 1; i < starMark - 1; i++) {
canvas.translate(starDistance + starWidth, 0);
canvas.drawRect(0, 0, starWidth, starHeight, mPaint);
}
canvas.translate(starDistance + starWidth, 0);
canvas.drawRect(0, 0, starWidth * (Math.round((starMark - (int) (starMark)) * 10) * 1.0f / 10), starHeight, mPaint);
}
} else {
//如果不到一颗星,直接mark多少就画多少
canvas.drawRect(0, 0, starWidth * starMark, starHeight, mPaint);
}
}
关于canvas方法,就不解释了,网上内容很多,到此,核心的东西就没有了。
接着展示下初始化控件的方法,
private void init(Context mContext, AttributeSet attrs) {
//初始化控件属性
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.star);
starNum = typedArray.getInteger(R.styleable.star_starsNum, 5);
starHeight = (int) typedArray.getDimension(R.styleable.star_starHeight, 0);
starWidth = (int) typedArray.getDimension(R.styleable.star_starWidth, 0);
starDistance = (int) typedArray.getDimension(R.styleable.star_starDistance, 0);
isClick = typedArray.getBoolean(R.styleable.star_starClickable, true);
starBackgroundBitmap = typedArray.getDrawable(R.styleable.star_starBackground);
starDrawDrawable = drawableToBitmap(typedArray.getDrawable(R.styleable.star_starDrawBackground));
typedArray.recycle();
setClickable(isClick);
//初始化画笔
mPaint = new Paint();
//设置抗锯齿
mPaint.setAntiAlias(true);
mPaint.setShader(new BitmapShader(starDrawDrawable, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
}
利用typedArray 获取xml设置的控件属性,xml设置如下
<ratingstar.yueleng.com.freedomstar_master.Star
android:id="@+id/star"
Star:starsNum ="5"
Star:starWidth ="18dp"
Star:starHeight ="18dp"
Star:starDistance ="3dp"
Star:starBackground ="@drawable/s2"
Star:starDrawBackground ="@drawable/s1"
Star:starClickable ="false"
android:layout_marginLeft="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
attrs.xml内容是这样的
<declare-styleable name="star">
//星星数量
<attr format="integer" name="starsNum"></attr>
//星星宽度
<attr format="dimension" name="starWidth"></attr>
//星星高度
<attr format="dimension" name="starHeight"></attr>
//星星间距
<attr format="dimension" name="starDistance"></attr>
//星星背景
<attr format="reference" name="starBackground"></attr>
//星星变化背景
<attr format="reference" name="starDrawBackground"></attr>
//控件是否可以点击
<attr format="boolean" name="starClickable"></attr>
</declare-styleable>
一个转化bitmap的方法
/**
* drawable转bitmap
*/
public Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) return null;
Bitmap bitmap = Bitmap.createBitmap(starWidth, starHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, starWidth, starHeight);
drawable.draw(canvas);
return bitmap;
}
添加按钮滑动点击控制
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
if (x < 0)
x = 0;
if (x > getMeasuredWidth())
x = getMeasuredWidth();
if (isClick) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum));
break;
case MotionEvent.ACTION_MOVE:
setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum));
break;
case MotionEvent.ACTION_UP:
setMark(x * 1.0f / (getMeasuredWidth() * 1.0f / starNum));
break;
}
}
return true;
}
csdn:http://download.youkuaiyun.com/detail/z690798364/9735752
github:https://github.com/songfuli/freedomstar-ratingbar