前言
今天看到了扔物线大神准备了一年开的教学博客,开篇也是从自定义View讲起的,讲的系统体系同时注重基础。适合各个阶段的开发者参考学习。在这里安利一波,同时为扔物线大神的无私奉献点赞啦。
扔物线大神博客地址:HenCoder
微信公众号:HenCoder 微博:扔物线
我们要实现的RatingBar效果如下
当然RatingBar系统已经给我们实现好了,这个只是做练习的。项目中实际使用优先选择系统的,如果有特殊效果可以考虑自定义来实现
效果涉及到的知识点
- Paint和Canvas的使用,使用Canvas绘制Bitmap
- 屏幕触摸事件的监听,处理用户交互和反馈
- 接口回调,View对外提供接口,返回用户选择的评分级别
效果实现分析
- 选中和未选中的星星用两张图片绘制
- onTouchEvent()监听用户触摸并计算触摸位置
- 触摸位置之前的星星绘制选中的,未触摸的绘制默认的,最后调用invalidate()刷新界面
代码实现
-
定义自定义属性
<declare-styleable name="RatingBar"> <!--默认星星图片 --> <attr name="normalStar" format="reference" /> <!--选中星星图片 --> <attr name="selectStar" format="reference" /> <!--星星的总数量 --> <attr name="starNum" format="integer" /> <!--星星的间隔 --> <attr name="starPadding" format="dimension" /> </declare-styleable>
-
onMeasure()中确定View的宽高
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //高度设置为 星星的高度 int height = mNormalStar.getHeight(); //宽度设置为 星星总宽度 + 间隔 int width = mNormalStar.getWidth() * mStarNum + mStarPadding * (mStarNum - 1); setMeasuredDimension(width, height); }
-
根据当前触摸级别绘制星星图片
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (int i = 0; i < mStarNum; i++) { int x = (mNormalStar.getWidth() + mStarPadding) * i; if (mCurrentStarPos > i) { //绘制选中 canvas.drawBitmap(mSelectStar, x, 0, mPaint); } else { //绘制默认 canvas.drawBitmap(mNormalStar, x, 0, mPaint); } } }
-
onTouchEvent()监听用户触摸,计算当前等级,invalidate刷新界面
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_DOWN: //触摸位置 float dx = event.getX(); //计算触摸的级别 int currentStarPos = (int) (dx / (mNormalStar.getWidth() + mStarPadding) + 1); if (currentStarPos > mStarNum) { currentStarPos = mStarNum; } if (currentStarPos <= 0) { mCurrentStarPos = 1; } //如果滑动级别未发生变化,return,不重绘 if (currentStarPos == mCurrentStarPos) { return true; } mCurrentStarPos = currentStarPos; invalidate(); //回调当前选中的级别 onRateSelectedListener.onRateSelect(mCurrentStarPos); break; } return true; }
-
提供设置初始化级别的方法和级别选择回调接口
/** * 初始化级别 * @param rate */ public void setInitRate(int rate) { if (rate > mStarNum) { throw new RuntimeException("初始化的Rate值要小于最大值" + mStarNum); } this.mCurrentStarPos = rate; invalidate(); } public OnRateSelectedListener onRateSelectedListener; public void setonRateSelectedListener(OnRateSelectedListener listener) { this.onRateSelectedListener = listener; } public interface OnRateSelectedListener { void onRateSelect(int rate); }
-
在Activity中测试和使用
RatingBar ratingBar = (RatingBar) findViewById(R.id.ratingBar); ratingBar.setInitRate(3); ratingBar.setonRateSelectedListener(new RatingBar.OnRateSelectedListener() { @Override public void onRateSelect(int rate) { Log.i("m1Ku", "当前选择级别是" + rate +"级"); } });
星星图片资源
项目Github地址:https://github.com/m1Koi/CustomViewPractice
小结
扔物线大神说,自定义View有重要的三点:绘制、布局和触摸反馈。而这个效果练习的就是触摸反馈,可能这是最基础的触摸反馈的练习,onTouch触摸反馈的学习任重而道远啊。