自定义可拖拽评分进度条控件
来源:互联网 发布:网络词表妹是什么梗 编辑:程序博客网 时间:2024/06/05 11:16
愉快的周末,在家撸代码如何?刚刚看完《战狼2》,心潮澎湃啊,China No.1 ! 23333333 李时珍的皮,言归正传。
最近项目中UI设计了这样一种控件,可以拖拽的评分进度条,这让我一次没有写过自定义View的人如何是好啊?不过经过我一天的查阅资料,终于实现了,先看一下效果吧:
在来看下我们实现之后动态的效果图:
好了,效果我们都看见了,下面直接进入正题,自定义View。
自定义view,首先我们先分析一下,这个控件需要哪些属性;①进度条的背景色②三种进度条变化的颜色③滑块的drawable④进度条的高度⑤进度条之间留白间隙的宽度⑥滑块的宽度和高度,我们先列出这么些,如果有需要到时候我们再添加。
需要的属性我们列好后,在values目录下新建attrs资源文件夹,新增以下属性:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="DragProgressBar"> <!--进度条的默认背景色--> <attr name="default_background" format="color"/> <!--进度颜色1--> <attr name="progress_color1" format="color"/> <!--进度颜色2--> <attr name="progress_color2" format="color"/> <!--进度颜色3--> <attr name="progress_color3" format="color"/> <!--滑块的drawable--> <attr name="seek_btn" format="reference"/> <!--滑块的宽度--> <attr name="seek_btn_width" format="dimension"/> <!--滑块的高度--> <attr name="seek_btn_height" format="dimension"/> <!--进度条之间的留白部分--> <attr name="progress_padding" format="dimension"/> <!--进度条的高度--> <attr name="progress_height" format="dimension"/> <!--是否可以拖拽--> <attr name="can_drag" format="boolean"/> </declare-styleable></resources>这样我们的自定义属性定义好之后,新建一个DragProgressBar继承自View,然后重写构造方法,这个代码就不罗列了,大家应该都知道。接下来就应该获取我们自定义的属性。
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.DragProgressBar); this.mDefaultBgColor = mTypedArray.getColor(R.styleable.DragProgressBar_default_background, getResources().getColor(R.color.gray_link_bg)); this.mProgressGradle1Color = mTypedArray.getColor(R.styleable.DragProgressBar_progress_color1, getResources().getColor(R.color.red_progress)); this.mProgressGradle2Color = mTypedArray.getColor(R.styleable.DragProgressBar_progress_color2, getResources().getColor(R.color.yellow_progress)); this.mProgressGradle3Color = mTypedArray.getColor(R.styleable.DragProgressBar_progress_color3, getResources().getColor(R.color.green_progress)); this.mSeekBtnDrawable = mTypedArray.getDrawable(R.styleable.DragProgressBar_seek_btn); this.mSeekBtnWidth = mTypedArray.getDimension(R.styleable.DragProgressBar_seek_btn_width, 56); this.mSeekBtnHeight = mTypedArray.getDimension(R.styleable.DragProgressBar_seek_btn_height, 56); this.mProgressPadding = mTypedArray.getDimension(R.styleable.DragProgressBar_progress_padding, 2); this.mProgressHeight = mTypedArray.getDimension(R.styleable.DragProgressBar_progress_height, 12); this.mCanEdit = mTypedArray.getBoolean(R.styleable.DragProgressBar_can_drag, false); mTypedArray.recycle();最后一行recycle()方法不要忘记加哦!现在我们的自定义属性的值都获取到了之后,我们需要定义一些变量,方便下面的计算。
private int centerTop;//进度条的上边界 private int centerBottom;//进度条的下边界 private float roundLeft = 0;//根据left 来更新现在滑块的位置 private float mStartX; private float mLastX; private float mDistX; private onProgressChangeListener mOnProgressChangeListener;//进度改变的回调接口 private Bitmap seekbar;//滑块bitmap private int mDefaultBgColor;//背景色 private int mProgressGradle1Color;//进度1的颜色 private int mProgressGradle2Color;//进度2的颜色 private int mProgressGradle3Color;//进度3的颜色 private Drawable mSeekBtnDrawable;//滑块的drawable private float mSeekBtnWidth;//滑块宽度 private float mHalfSeekBtnWidth;//滑块一半宽度 private float mSeekBtnHeight;//滑块高度 private float mHalfSeekBtnHeight;//滑块一半高度 private float mProgressPadding;//进度之间的padding(留白部分) private float mProgressHeight;//进度条的高度 private float mHalfProgressHeight;//进度条的高度 private float mKeduWidth;//每个刻度的宽度 private boolean mCanEdit;//默认可拖动可编辑
mHalfSeekBtnWidth = mSeekBtnWidth / 2; mHalfSeekBtnHeight = mSeekBtnHeight / 2; mHalfProgressHeight = mProgressHeight / 2; mRadiusPaint = new Paint(); mRadiusPaint.setAntiAlias(true); mRadiusPaint.setColor(mDefaultBgColor); centerTop = (int) ((mSeekBtnHeight - mProgressHeight) / 2); centerBottom = (int) (centerTop + mProgressHeight); this.seekbar = drawableToBitmap(mSeekBtnDrawable);这里有一个drawableToBitmap方法,是用来把drawable资源转成bitmap用的:
private Bitmap drawableToBitmap(Drawable drawable) { if (drawable == null) return null; Bitmap bitmap = Bitmap.createBitmap((int) mSeekBtnWidth, (int) mSeekBtnHeight, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, (int) mSeekBtnWidth, (int) mSeekBtnHeight); drawable.draw(canvas); return bitmap; }
在我们绘制之前,有必要先理一下思路,对于这个view来说,我们首先要做的肯定是绘制进度条,这个进度条左右都有一个半圆,中间还有留白的padding部分,进度条的刻度应该怎么画呢?
仔细分析一下,除了左右两边的那个刻度,中间的其实都是一个矩形而已,至于中间的留白,drawRect方法会传矩形的左右两条边的值,在每个矩形之间空出padding就可以;那中间的分析好之后,左右两边的呢?其实左右两边都是一个半圆而已,直径就是进度条的高度,我们画一个圆,然后裁剪一下,得到半圆就ok啦!
简单写下代码:
//绘制背景 for (int i = 0; i < 10; i++) { mRadiusPaint.setColor(mDefaultBgColor); if (i == 0) { //最左边是有圆角的 而圆角起始x点应该是seekBtn宽度的一半 RectF leftOval = new RectF(mHalfSeekBtnWidth, centerTop, mHalfSeekBtnWidth + mProgressHeight, centerBottom); //绘制扇形 取90°~270° canvas.drawArc(leftOval, 90, 180, true, mRadiusPaint); //绘制除圆弧之外 剩余正常进度条部分 canvas.drawRect(mHalfSeekBtnWidth + mHalfProgressHeight, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth, centerBottom, mRadiusPaint); } else if (i == 9) { canvas.drawRect(mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth - mHalfProgressHeight, centerBottom, mRadiusPaint); //移动画布 RectF rightOval = new RectF(mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth - mHalfProgressHeight - mHalfProgressHeight, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth - mHalfProgressHeight - mHalfProgressHeight + mProgressHeight, centerBottom); canvas.drawArc(rightOval, 90, -180, true, mRadiusPaint); //绘制完第10个进度条 移动画布 } else { canvas.drawRect(mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth, centerBottom, mRadiusPaint); } }
这样以后,我们就绘制好了进度条的背景色,看下效果。
OK 背景绘制好了,就考虑进度的问题了,以及滑块的位置问题。
同样,我们先分析一下进度应该怎么显示,既然是可拖拽的,那么肯定和事件处理相关,我们需要计算当前点击的位置,其实这里只用到了x轴的位置,当我们点击进度条
的时候,在ACTION_DOWM里会获取到点击的x位置,然后根据x位置计算出当前的进度,根据进度绘制不同颜色的进度。
至于滑块的滑动问题,我们需要定义一个字段roundLeft,就是滑块左边界的x点起始位置,根据这个值,不停更新滑块位置。
switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { Log.d("hyq", "ACTION_DOWN x:" + event.getX()); mStartX = mLastX = event.getX(); setSelectByLeft(mStartX); break; } case MotionEvent.ACTION_MOVE: { mLastX = event.getX(); mDistX = mLastX - mStartX; Log.d("hyq", "ACTION_MOVE left:" + roundLeft + " distx:" + mDistX + " maxwidth:" + getMeasuredWidth() + " x:" + mLastX); if (Math.abs(mDistX) > 5) { //保证不出界 roundLeft += mDistX; if (roundLeft <= 0) { roundLeft = 0; } if (roundLeft > getMeasuredWidth() - mSeekBtnWidth) { roundLeft = getMeasuredWidth() - mSeekBtnWidth; } float center = roundLeft + mHalfSeekBtnWidth; mStartX = event.getX(); setMoveLeft(center); } break; } case MotionEvent.ACTION_UP: { //根据抬起时候 圆心的位置 最接近哪个刻度 就给哪个值 float center = roundLeft + mHalfSeekBtnWidth; setSelectByLeft(center); break; } }
这里有两个方法,分别是setMoveLeft,setSelectByLeft方法,是根据当前滑块中心点的位置,计算出当前的progress,而且需要处理滑动超过每个进度的一半的时候,手指
抬起后滑块自动选择最相近的进度。
private void setMoveLeft(float center) { if (center < mHalfSeekBtnWidth + mKeduWidth / 2) { select = 0; } else if (center > getMeasuredWidth() - mHalfSeekBtnWidth - mKeduWidth / 2) { select = 10; } else { select = (int) (center / (mKeduWidth + 2)); } Log.d("hyq", "center:" + center + " select:" + select); invalidate(); if (mOnProgressChangeListener != null) { if (select != lastSelect) { mOnProgressChangeListener.onProgressChange(select); } } lastSelect = select; }
/** * 根据 点击 或 抬起时候的 中心点 定位到当前的选中progress * * @param center */ private void setSelectByLeft(float center) { if (center < mHalfSeekBtnWidth + mKeduWidth / 2) { select = 0; } else if (center > getMeasuredWidth() - mHalfSeekBtnWidth - mKeduWidth / 2) { select = 10; } else { select = (int) (center / (mKeduWidth + 2)); } Log.d("hyq", "center:" + center + " select:" + select); getRoundLeft(); invalidate(); if (mOnProgressChangeListener != null) { if (select != lastSelect) { mOnProgressChangeListener.onProgressChange(select); } } lastSelect = select; }
现在应该很简单了吧!想要的位置的值啥的全都有啦。只需要复制绘制背景的代码,然后把循环的条件改成当前的刻度,不就可以绘制出当前进度了吗?那进度的颜色呢?
这还不简单吗...根据进度设置画笔的颜色呀!
if (select < 5) { mRadiusPaint.setColor(mProgressGradle1Color); } else if (select < 7) { mRadiusPaint.setColor(mProgressGradle2Color); } else { mRadiusPaint.setColor(mProgressGradle3Color); }现在颜色啥的也设置好了,直接绘制:
for (int i = 0; i < select; i++) { if (i == 0) { RectF leftOval = new RectF(mHalfSeekBtnWidth, centerTop, mHalfSeekBtnWidth + mProgressHeight, centerBottom); canvas.drawArc(leftOval, 90, 180, true, mRadiusPaint); canvas.drawRect(mHalfSeekBtnWidth + mHalfProgressHeight, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth, centerBottom, mRadiusPaint); } else if (i == 9) { canvas.drawRect(mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth - mHalfProgressHeight, centerBottom, mRadiusPaint); RectF rightOval = new RectF(mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth - mHalfProgressHeight - mHalfProgressHeight, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth - mHalfProgressHeight - mHalfProgressHeight + mProgressHeight, centerBottom); canvas.drawArc(rightOval, 90, -180, true, mRadiusPaint); } else { canvas.drawRect(mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i, centerTop, mHalfSeekBtnWidth + (mKeduWidth + mProgressPadding) * i + mKeduWidth, centerBottom, mRadiusPaint); } }绘制好了之后,现在就根据roundLeft的值绘制当前的滑块的位置,这里因为是bitmap,需要先将canvs画布移动到roundLeft的位置,所以绘制的时候需要这么写:
canvas.translate(roundLeft, 0); canvas.drawRect(0, 0, mSeekBtnWidth, mSeekBtnHeight, mSeekBarPaint);
我们再看一下现在的效果
我们只需要在activity中实现我们自定义view里面定义好的进度回调接口,就能实时的获取当前的进度,到此就大功告成啦!
好啦,第一次写blog,还不是很熟练这个编辑工具。在今后的日子里,还希望能和大家一起讨论新知识,新技术。
ps:周末愉快!
源码点此下载
- 自定义可拖拽评分进度条控件
- 自定义进度条和RatingBar评分控件
- 3-6进度条,评分控件
- C#自定义评分控件
- 自定义RatingBar 评分控件
- 自定义评分控件
- android自定义等级评分圆形进度条
- Android 自定义RatingBar评分控件
- Android自定义RatingBar(评分控件)
- android星星评分自定义控件
- Android 自定义 RatingBar (评分控件)
- Android自定义小星星评分控件
- Android 自定义星级评分控件
- Android自定义星星评分控件
- Kotlin-->自定义评分控件RatingBar
- Android自定义RatingBar,评分控件
- android 自定义星级评分控件
- ASP.NET评分自定义控件 星级评分控件
- 设计模式——抽象工厂模式
- GTK程序设计
- (十四)Android的消息机制Handler
- 调研tcp定时器
- CopyOnWriteArrayList学习笔记
- 自定义可拖拽评分进度条控件
- AJAX基础
- Eclipse使用问题—svn Could not create the view
- 汇编语言(1)基本概念
- Java Web常用的几个开发方案
- 1100. Mars Numbers (20)<map>
- 层次分析法(matlab实现)
- 虚函数的实调用与虚调用
- solr的简单介绍