先看下实现效果
看效果图分析
1 个是进度的背景,背景上面左右两个圆角按钮,下面是一个text显示进度用的
2 整体的高度用QQ截图简单的量下尺寸,毕竟显示到屏幕上的也都是像素,算完后,可根据2倍图,3倍图来具体的做下适配,这都是细节的处理了,测量后,从按钮的顶部到text文字的底部大概50px,按钮的高度为20px,宽度为14px,显示进度的bg是一个高度为12px的矩形区域,好了,变量基本测量完毕,下面可以写代码了
3 这里利用安卓坐标系的一个小技巧,按钮要显示在bg的中点上,如果rect区域的高度为坐标系的起始位置,则按钮头部会被隐藏掉,这也是可以理解的,所以,我门在绘制背景的时候将他的top顶点改为距离坐标系top10个像素,则按钮要以bg的垂直中点来绘制,则 bg的rectF为
bgRectF = new RectF(0, mTop, viewWidth, bgHeight + mTop);
因为bgHeight 的高度 = bottom - top 同理,左边的RectF为
RectF leftThumbF = new RectF(mLeft, mTop + bgHeight / 2 - thumbHeight / 2, thumbWidth + mLeft, mTop + bgHeight / 2 + thumbHeight / 2);
加上mTop相当于坐标系向下平移了mTop距离,因此算出左边按钮的top = mTop + bgHeight / 2 - thumbHeight / 2 ,高度不能变 则 bottom 如上所算,这样按钮就能够在bg的垂直中点上来绘制,同理,则右边按钮的rectF为
RectF rightThumbF = new RectF(thumbRightLeft, mTop + bgHeight / 2 - thumbHeight / 2, thumbWidth + thumbRightLeft, mTop + bgHeight / 2 + thumbHeight / 2);
上面的mLeft是左边按钮距离x轴的偏移量,thumbRight 为右边距离X轴的偏移量,要保证这个按钮在移动过程中的宽度不变,所以增加此变量
4.定义ThumbType 用来标示移动的是哪个按钮
private static class ThumbType { private static final int THUMB_LEFT = 0; private static final int THUMB_RIGHT = 1; }5.根据手指移动距离来判断用户正在移动的是哪个按钮,这个方法很重要,如果左边按钮减去当前的moveX 大于 右边按钮减去当前的moveX,说明用户的手指在右边按钮左右,则Type = Thumb.Right,反之,则是左边的按钮,ok,方法如下,咱们取绝对值
private int judgeIsLeftOrRightThumb(int x) { return Math.abs(mLeft - x) - Math.abs(thumbRightLeft - x) > 0 ? ThumbType.THUMB_RIGHT : ThumbType.THUMB_LEFT; }6 分析左右按钮的移动范围处理临界点,左右按钮互换的情况,这点处理好后,基本上可以实现这个功能了,首先,左边的按钮我现在的处理是,他右边的边界不能超越这个右边的按钮,也就是mLeft 要 小于等于 thumbRightLeft 即 mLeft <= thumbRightLeft,左边的容易理解点,当mLeft 小于等于 按钮宽度的一半时,说明已经在坐标系的最左边了,则让mLeft = 0,这样按钮就紧靠着最左边了;右边的按钮是这样的,用户拖动到与最左边的按钮重合时,还想往最左边拖动,这个时机,我用了点小技巧,此时让thumbType == ThumbType.Left,让左边的Thumb动起来,这样就不会出错了,右边按钮的右边临界点为 getWidth - thumbWidth (不让他为thumbWidth / 2 是因为rightThumbRight中的计算);两个按钮中间的进度条,我是又绘制了一层,计算也很简单 thumbRightLeft - mLeft;底部的文字居中相信大家都会算了,因为right - left都是相对距离,注意要加上mLeft才能真正居中
textLeft = ((thumbRightLeft - mLeft) - textLength) / 2 + mLeft;7上面基本分析完毕,要得到最后的mLeft,right的值,这个年龄我们平分100份来算,则每个刻度
mGraduation = viewWidth / range;
使用一个方法算出这个mLeft,Right对应的值,方法如下
private int[] getmGraduation() { int[] graduations = new int[2]; int leftRange = mLeft / mGraduation; int rightRange = thumbRightLeft / mGraduation; if (leftRange <= 0) { leftRange = 0; } if (rightRange >= 100) { rightRange = 100; } graduations[0] = leftRange; graduations[1] = rightRange; Log.e(getClass().getSimpleName(), graduations[0] + "----" + graduations[1]); return看下输出的log如下
该自定义View基本就是这样了,大家最好练起来,主类就一个,其实不想分享代码,大家自己写最好!
主类代码如下
package com.example.mrboudar.playboy.widgets;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* Created by MrBoudar on 16/9/18.
*/
public class SingleSeekBar extends View {
private int viewWidth;
private int viewHeight = 50;
private int bgHeight = 12;
private int thumbHeight = 20;
private int thumbWidth = 14;
private static final int mTop = 10;
private RectF bgRectF;
private int mLeft;
private int thumbRightLeft;
private int textLeft;
private int downX;
private int mType = ThumbType.THUMB_LEFT;
private int range = 100;
//刻度
private int mGraduation;
public SingleSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SingleSeekBar(Context context) {
this(context, null);
}
public SingleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int measureHeight;
if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
measureHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, viewHeight, getContext().getResources().getDisplayMetrics());
heightMeasureSpec = MeasureSpec.makeMeasureSpec(measureHeight, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.viewWidth = w;
this.viewHeight = h;
mLeft = mTop;
thumbRightLeft = 4 * mLeft;
mGraduation = viewWidth / range;
bgRectF = new RectF(0, mTop, viewWidth, bgHeight + mTop);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawRoundBg(canvas);
drawLeftThumb(canvas);
drawRightThumb(canvas);
drawProgressBg(canvas);
drawText(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
//最好判定下版本,低版本无此API
int masked = event.getActionMasked();
switch (action & masked) {
case MotionEvent.ACTION_DOWN:
downX = (int) event.getRawX();
mType = judgeIsLeftOrRightThumb(downX);
break;
case MotionEvent.ACTION_MOVE:
actionOperations((int) event.getRawX());
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
actionOperations((int) event.getRawX());
break;
}
return true;
}
private void drawRoundBg(Canvas canvas) {
Paint paint = initPaint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.LTGRAY);
canvas.drawRect(bgRectF, paint);
}
private void drawLeftThumb(Canvas canvas) {
Paint paint = initPaint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
RectF leftThumbF = new RectF(mLeft, mTop + bgHeight / 2 - thumbHeight / 2, thumbWidth + mLeft, mTop + bgHeight / 2 + thumbHeight / 2);
canvas.drawRoundRect(leftThumbF, 5, 5, paint);
}
private void drawRightThumb(Canvas canvas) {
Paint paint = initPaint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
RectF rightThumbF = new RectF(thumbRightLeft, mTop + bgHeight / 2 - thumbHeight / 2, thumbWidth + thumbRightLeft, mTop + bgHeight / 2 + thumbHeight / 2);
canvas.drawRoundRect(rightThumbF, 5, 5, paint);
}
private void drawProgressBg(Canvas canvas) {
Paint paint = initPaint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLUE);
RectF progressF = new RectF(mLeft + thumbWidth, mTop, thumbRightLeft, bgHeight + mTop);
canvas.drawRect(progressF, paint);
}
private void drawText(Canvas canvas) {
String text = getmGraduation()[0] + "--" + getmGraduation()[1];
Paint paint = initPaint();
paint.setColor(Color.WHITE);
paint.setTextSize(20.F);
int textLength = (int) paint.measureText(text);
textLeft = ((thumbRightLeft - mLeft) - textLength) / 2 + mLeft;
if (textLeft >= getWidth() - textLength) {
textLeft = getWidth() - textLength;
} else if (textLeft <= 0) {
textLeft = 0;
}
canvas.drawText(text, textLeft, 5 * mTop, paint);
}
private Paint initPaint() {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.RED);
paint.setStrokeWidth(1.F);
paint.setShader(null);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
return paint;
}
private int judgeIsLeftOrRightThumb(int x) {
return Math.abs(mLeft - x) - Math.abs(thumbRightLeft - x) > 0 ? ThumbType.THUMB_RIGHT : ThumbType.THUMB_LEFT;
}
private void actionOperations(int acionX) {
if (mType == ThumbType.THUMB_LEFT) {
if (acionX <= thumbWidth / 2) {
mLeft = 0;
} else if (acionX >= thumbRightLeft - thumbWidth / 2) {
mLeft = thumbRightLeft - thumbWidth / 2;
} else {
mLeft = acionX;
}
} else if (mType == ThumbType.THUMB_RIGHT) {
if (acionX <= thumbWidth / 2 + thumbWidth) {
thumbRightLeft = 0;
} else if (acionX >= getWidth() - thumbWidth) {
thumbRightLeft = getWidth() - thumbWidth;
} else {
if(thumbRightLeft <= mLeft){
mType = ThumbType.THUMB_LEFT;
return;
}
thumbRightLeft = acionX;
}
}
invalidateView();
}
private void invalidateView() {
if (Looper.myLooper() == Looper.getMainLooper()) {
invalidate();
} else {
postInvalidate();
}
}
private void startAnimation(final int x1, final int x2) {
//android 3.0后才有属性动画ObjectAnimation,3.0以下版本使用Nineoldandroid.jar使用属性动画
ValueAnimator valueAnimator = ObjectAnimator.ofFloat(x1, x2);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setDuration(200);
valueAnimator.setRepeatCount(0);
valueAnimator.setStartDelay(200);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
Float atFloat = (Float) valueAnimator.getAnimatedValue();
if (x2 >= x1) {
if (mType == ThumbType.THUMB_LEFT) {
mLeft = (int) (mLeft * atFloat);
} else if (mType == ThumbType.THUMB_RIGHT) {
thumbRightLeft = (int) (thumbRightLeft * atFloat);
}
} else {
if (mType == ThumbType.THUMB_LEFT) {
mLeft = (int) (mLeft * (1 - atFloat));
} else if (mType == ThumbType.THUMB_RIGHT) {
thumbRightLeft = (int) (thumbRightLeft * (1 - atFloat));
}
}
invalidateView();
}
});
valueAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
valueAnimator.start();
}
private int[] getmGraduation() {
int[] graduations = new int[2];
int leftRange = mLeft / mGraduation;
int rightRange = thumbRightLeft / mGraduation;
if (leftRange <= 0) {
leftRange = 0;
}
if (rightRange >= 100) {
rightRange = 100;
}
graduations[0] = leftRange;
graduations[1] = rightRange;
Log.e(getClass().getSimpleName(), graduations[0] + "----" + graduations[1]);
return graduations;
}
private static class ThumbType {
private static final int THUMB_LEFT = 0;
private static final int THUMB_RIGHT = 1;
}
}
ok,每天努力一点点!