<declare-styleable name="MyElongScaleSeekBar">
<attr name="scale_progress_normal_color" format="color" />
<attr name="scale_progress_section_color" format="color" />
<attr name="scale_left_ball_bg_color" format="color" />
<attr name="scale_left_ball_stroke_color" format="color" />
<attr name="scale_left_ball_stroke_with" format="dimension" />
<attr name="scale_right_ball_bg_color" format="color" />
<attr name="scale_right_ball_stroke_color" format="color" />
<attr name="scale_right_ball_stroke_with" format="dimension" />
<attr name="scale_ball_radio" format="dimension" />
<attr name="scale_ball_shadow_radio" format="dimension" />
<attr name="scale_ball_shadow_color" format="color" />
<attr name="scale_seek_height" format="dimension" />
<attr name="scale_left_text_color" format="dimension" />
<attr name="scale_left_text_size" format="dimension" />
<attr name="scale_right_text_color" format="dimension" />
<attr name="scale_right_text_size" format="dimension" />
<attr name="scale_text_margin_ball" format="dimension" />
<attr name="scale_ball_stick_height" format="dimension" />
<attr name="scale_ball_stick_width" format="dimension" />
<attr name="scale_ball_stick_margin" format="dimension" />
<attr name="scale_ball_stick_color" format="color" />
<attr name="scale_progress_unit" format="integer" />
<attr name="scale_symbol_front" format="string" />
declare-styleable>
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyElongScaleSeekBar, 0, R.style.default_scale_seekbar_style);
int indexCount = typedArray.getIndexCount();
for (int i = 0; i < indexCount; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.MyElongScaleSeekBar_scale_progress_normal_color:
scaleProgressNormalColor = typedArray.getColor(attr, Color.BLACK);
break;
....省略.....
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 背景进度条
drawScaleSeekNormal(canvas);
// 当前选中的进度
drawScaleSeekSection(canvas);
// 左边的球
drawLeftBall(canvas);
// 右边的球
drawRightBall(canvas);
// 左边球的文字
drawLeftText(canvas);
// 右边球的文字
drawRightText(canvas);
// 说了这么多,其实就是画圆圈和画方块的小把戏
}
// 画方块
canvas.drawRect(...)
// 画圆圈
canvas.drawCircle(...)
// 本效果就用到了这两个api
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int measureHeight;
if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
//确定view的高度(文字高度+距离+拖拽高度)
measureHeight = Math.max(scaleLeftTextSize, scaleRightTextSize) + 2 + scaleTextMarginBall + scaleBallRadio * 2;
heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
思路很简单,把UI给你的效果图,在一张纸上画一下,
你用铅笔画的过程,就是你转换代码确定位置的过程,
你的纸就是Android的坐标系,画圆圈要确定圆心位置,画方块要确定方块的四个顶点.那么接下来你只要确定了圆心位置,和方块位置定点,那么静态图就出来了.
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//球的半径包含描边
radioWithStroke = scaleBallRadio + scaleLeftBallStrokeWith * 0.5F;
xCoordinateUnit = radioWithStroke * 2;
valueEntities = calculateAllXCoordinate(scaleProgressUnit, w - radioWithStroke * 2, maxValue);
if (valueEntities != null && valueEntities.size() > 0) {
currentLeft = valueEntities.get(0);
currentRight = valueEntities.get(valueEntities.size() - 1);
leftDesc = creatCurrentDataDesc(currentLeft);
rightDesc = creatCurrentDataDesc(currentRight);
if (this.seekBarDragListener != null) {
this.seekBarDragListener.seekMoveValue(currentLeft.value, currentRight.value);
}
}
if (currentLeft != null && currentRight != null) {
// 背景进度条的区域
scaleSeekNormalRectF = new RectF();
scaleSeekNormalRectF.left = 0;
float top = h - (scaleBallRadio + scaleLeftBallStrokeWith * 0.5F) - scaleSeekHeight * 0.5F - scaleBallShadowRadio;
scaleSeekNormalRectF.top = top;
scaleSeekNormalRectF.bottom = top + scaleSeekHeight;
scaleSeekNormalRectF.right = w;
// 左边球的圆心坐标
leftBallPoint = new SeekPoint();
leftBallPoint.x = currentLeft.xCoordinat + radioWithStroke;
leftBallPoint.y = h - radioWithStroke - scaleBallShadowRadio;
// 左边球中间的猴三棍
calculateLeftSticks();
// 右边球的坐标
rightBallPoint = new SeekPoint();
rightBallPoint.x = currentRight.xCoordinat + radioWithStroke;
rightBallPoint.y = h - radioWithStroke - scaleBallShadowRadio;
// 右边球中间的猴三棍
calculateRightSticks();
// 选中背景条的间距部分
scaleSeekSectionRectF = new RectF();
scaleSeekSectionRectF.left = leftBallPoint.x - radioWithStroke;
scaleSeekSectionRectF.right = rightBallPoint.x - radioWithStroke;
scaleSeekSectionRectF.top = scaleSeekNormalRectF.top;
scaleSeekSectionRectF.bottom = scaleSeekNormalRectF.bottom;
// 圆球的路径和区域
scaleLeftBallPath = new Path();
scaleLeftBallPath.addCircle(leftBallPoint.x, leftBallPoint.y, radioWithStroke, Path.Direction.CW);
scaleRightBallPath = new Path();
scaleRightBallPath.addCircle(rightBallPoint.x, rightBallPoint.y, radioWithStroke, Path.Direction.CW);
scaleLeftBallRegion = updateRegionByPath(scaleLeftBallPath);
scaleRightBallRegion = updateRegionByPath(scaleRightBallPath);
}
}
因为你每次的拖拽都执行的move,那么成千上万的move组成的静态图在一zhen一zhen的过的时候,是不是所谓的连续动画,类似于小时候的动画书…
//这里大概都猜到用什么实现的了,没错就是传说中path+Region...直接看代码
// 圆球的路径和区域
scaleLeftBallPath = new Path();
scaleLeftBallPath.addCircle(leftBallPoint.x, leftBallPoint.y, radioWithStroke, Path.Direction.CW);
Region region = new Region();
if (path != null) {
RectF tempRectF = new RectF();
path.computeBounds(tempRectF, true);
region.setPath(path, new Region((int) tempRectF.left, (int) tempRectF.top, (int) tempRectF.right, (int) tempRectF.bottom));
}
return region;
case MotionEvent.ACTION_DOWN:
boolean touchLeftBall = scaleLeftBallRegion.contains((int) event.getX(), (int) event.getY()) && !scaleRightBallRegion.contains((int) event.getX(), (int) event.getY());
boolean touchRightBall = scaleRightBallRegion.contains((int) event.getX(), (int) event.getY()) && !scaleLeftBallRegion.contains((int) event.getX(), (int) event.getY());
这个就是一个经常面试的时候一个小问题,给你一个任意数字,在一个数组里面找到与他最接近的数字并返回.
对应到我们onTouch里面就是,你触摸的任意一点,要从你生成的步长数据集合中找到与之对应的最接近的值即可
public UnitValueEntity binarySearchKey(List data, int targetNum) {
if (data != null && data.size() > 0) {
int left = 0, right = 0;
for (right = data.size() - 1; left != right; ) {
int midIndex = (right + left) / 2;
int mid = (right - left);
int midValue = (int) data.get(midIndex).xCoordinat;
if (targetNum == midValue) {
return data.get(midIndex);
}
if (targetNum > midValue) {
left = midIndex;
} else {
right = midIndex;
}
if (mid <= 1) {
break;
}
}
UnitValueEntity rightnum = data.get(right);
UnitValueEntity leftnum = data.get(left);
return Math.abs((rightnum.xCoordinat - leftnum.xCoordinat) / 2) > Math.abs(rightnum.xCoordinat - targetNum) ? rightnum : leftnum;
}
return null;
}
// 我们这边就用二分查找,来找到这个值,并且返回.