篇幅有限,本文只讲解关键关键思路,伸手党和想看详细思路的请移步 传送门点我点我!!,如果喜欢,欢迎 Star 和 Fork !
本控件其实奔着双向滑动的SeekBar实现的,不过兼容了单向滑动(隐藏一个拖动按钮不就是单向的了嘛),所以我以 双向滑动思路为例。
RangeSeekBar主要包括两个类,一个是RangeSeekbar类,主要负责绘制进度条以及处理滑动相关逻辑,计算当前滑动值;另一个是SeekBar类,主要负责绘制拖动按钮相关,如绘制背景以及提示信息等。我们用RangeSeekBar初始化控件的一些属性,并且生成两个SeekBar对象,协调他们之间的关系。
主要包括控件绘制、两个拖动按钮的滑动逻辑及进度的计算。
绘制原理很简单,计算 –> 定位 –> 绘制。讲之前先放一张图,你就能理解Android是如何定位的了。
计算:
lineLeft = 2 * DEFALT_PADDING;
lineRight = View的宽度 - 2 * DEFALT_PADDING;
lineTop = (int)mHintBGHeight+ mThumbSize/2 -mSeekbarHight/2 + DEFALT_PADDING;
lineBottom = lineTop + mSeekbarHight;
lineWidth = lineRight - lineLeft;
lineCorners = (int) ((lineBottom - lineTop) * 0.45f);
定位:
RectF line = new RectF();
line.set(lineLeft, lineTop, lineRight, lineBottom);
绘制:
canvas.drawRoundRect(line, lineCorners, lineCorners, mMainPaint);
这里只讲解使用图片如何绘制,自己填充的和进度条类似。
计算:
left = lineLeft - mThumbSize / 2;
right = lineLeft + mThumbSize / 2;
top = lineBottom - mThumbSize / 2;
bottom = lineBottom + mThumbSize / 2;
Bitmap original = BitmapFactory.decodeResource(context.getResources(), bmpResId);
if (original != null) {
Matrix matrix = new Matrix();
float scaleHeight = mThumbSize * 1.0f / original.getHeight();
float scaleWidth = scaleHeight;
matrix.postScale(scaleWidth, scaleHeight);
bmp = Bitmap.createBitmap(original, 0, 0, original.getWidth(), original.getHeight(), matrix, true);
}
定位与绘制:
canvas.drawBitmap(bmp, left, lineTop - bmp.getHeight() / 2, null);
首先要判断当前手指拖动的是哪个按钮,SeekBar类中这个方法可以判断当前按钮是否被拖动:
/**
* 拖动检测
* @param event
* @return
*/
protected boolean collide(MotionEvent event) {
float x = event.getX();
float y = event.getY();
int offset = (int) (lineWidth * currPercent);
return x > left + offset && x < right + offset && y > top && y < bottom;
}
然后在RangeSeekbar的onTouchEvent中当手指按下时根据按钮的位置和手指的当前坐标即可判断当前按钮
case MotionEvent.ACTION_DOWN:
boolean touchResult = false;
if (rightSB != null && rightSB.currPercent >= 1 && leftSB.collide(event)) {
currTouch = leftSB;
touchResult = true;
} else if (rightSB != null && rightSB.collide(event)) {
currTouch = rightSB;
touchResult = true;
} else if (leftSB.collide(event)) {
currTouch = leftSB;
touchResult = true;
}
当手指移动时,我们根据坐标即可计算出按钮当前位置占整个进度条的比例,从而可以算出两个按钮的值,至于两个按钮相遇时,我们可以根据两个按钮当前的值判断,左边的按钮的值不能大于右边的值,同理,右边按钮的值也不能小于左边的值。
进度提示的背景的绘制和拖动的按钮原理类似,但是有一点不一样的地方就是考虑到他可能会被拉伸,所以我用的是9Path文件,9Path的绘制和普通的bitmap绘制稍有不同,详情请看另一篇文章 9path 绘制。如果你想改变它的背景图片的话,请使用9Path文件!