效果预览
代码实现
public class ToggleButton extends View {
private int mLineWidth = 5;
private int mTextSize = 18;
private TextPaint mTextPaint = null;
private int mTouchSlop = 0;
private boolean isTouchMove = false;
private int offsetX = 0;
private int contentWidth = 0;
private int contentHeight = 0;
private int slideBarRadius;
float startX = 0f;
float startY = 0f;
private int STATUS_LEFT = 0;
private int STATUS_RIGHT = 1;
private int mState = STATUS_LEFT;
private ValueAnimator mSlideAnimator = null;
public ToggleButton(Context context) {
this(context, null);
}
public ToggleButton(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ToggleButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//设置此项true,否则无法滑动
mTextPaint = initPaint();
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop() / 2;
setClickable(true);
setFocusable(true);
setFocusableInTouchMode(true);
setBackgroundColor(0xffffffff);
}
private TextPaint initPaint() {
// 实例化画笔并打开抗锯齿
TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
paint.setStrokeWidth(dpTopx(mLineWidth));
paint.setTextSize(dpTopx(mTextSize));
return paint;
}
private float dpTopx(int dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = getPaddingTop() + getPaddingBottom() + 100;
} else if (heightMode == MeasureSpec.AT_MOST) {
int disire = getPaddingTop() + getPaddingBottom() + 100;
heightSize = Math.min(disire, heightSize);
}
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
if (widthMode == MeasureSpec.UNSPECIFIED) {
widthSize = getPaddingTop() + getPaddingBottom() + 280;
} else if (widthMode == MeasureSpec.AT_MOST) {
int disire = getPaddingTop() + getPaddingBottom() + 280;
widthSize = Math.min(disire, widthSize);
}
setMeasuredDimension(widthSize, heightSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int w = getWidth();
int h = getHeight();
int lineWidthPixel = (int) mTextPaint.getStrokeWidth();
contentWidth = w - 2 * lineWidthPixel;
contentHeight = h - 2 * lineWidthPixel;
int radius = Math.min(w, h) / 2 - lineWidthPixel / 2;
if (radius < 0) return;
mTextPaint.setColor(Color.DKGRAY);
mTextPaint.setStyle(Paint.Style.STROKE);
RectF outlineRectF = new RectF(
lineWidthPixel / 2,
lineWidthPixel / 2,
lineWidthPixel + contentWidth + lineWidthPixel / 2,
lineWidthPixel + contentHeight + lineWidthPixel / 2
);
canvas.drawRoundRect(outlineRectF, radius, radius, mTextPaint);
RectF innerRectF = new RectF(
lineWidthPixel,
lineWidthPixel,
lineWidthPixel + contentWidth,
lineWidthPixel + contentHeight
);
mTextPaint.setColor(0xffffffff);
mTextPaint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(innerRectF, radius, radius, mTextPaint);
mTextPaint.setColor(Color.DKGRAY);
RectF middleRect = new RectF(
w / 2 - lineWidthPixel / 2,
lineWidthPixel * 3,
w / 2 + lineWidthPixel / 2,
h - lineWidthPixel * 3
);
mTextPaint.setStyle(Paint.Style.FILL);
canvas.drawRoundRect(middleRect, radius, radius, mTextPaint);
slideBarRadius = contentHeight / 2;
RectF circleRectF = new RectF(
lineWidthPixel + lineWidthPixel + offsetX,
0,
slideBarRadius * 2 + offsetX,
h
);
mTextPaint.setColor(Color.GRAY);
canvas.drawCircle(circleRectF.centerX(), circleRectF.centerY(), slideBarRadius, mTextPaint);
if(mState==STATUS_LEFT) {
mTextPaint.setColor(Color.CYAN);
}else{
mTextPaint.setColor(Color.RED);
}
canvas.drawCircle(circleRectF.centerX(), circleRectF.centerY(), slideBarRadius / 4, mTextPaint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int lineWidthPixies = (int) dpTopx(mLineWidth);
switch (action) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
float currentX = event.getX();
float currentY = event.getY();
float dy = Math.abs(currentY - startY);
float dx = Math.abs(currentX - startX);
if (dy <= dx && dx >= mTouchSlop) {
isTouchMove = true;
}
if (isTouchMove) {
offsetX = (int) (currentX - slideBarRadius);
startX = currentX;
startY = currentY;
if (offsetX < (lineWidthPixies)) {
offsetX = 0; //最左边
} else if (offsetX > contentWidth - slideBarRadius * 2) { //最右边
offsetX = contentWidth - slideBarRadius * 2;
}
postInvalidateOnAnimation();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_OUTSIDE:
getParent().requestDisallowInterceptTouchEvent(false);
startX = event.getX();
if (startX <= getWidth() / 2) {
if(isTouchMove){
offsetX = 0;
mState = STATUS_LEFT;
}else{
startSlideBarAnimation(offsetX,0,STATUS_LEFT);
}
} else {
if(isTouchMove){
mState = STATUS_RIGHT;
offsetX = contentWidth - 2 * slideBarRadius;
}else{
startSlideBarAnimation(offsetX,contentWidth - 2 * slideBarRadius,STATUS_RIGHT);
}
}
isTouchMove = false;
postInvalidateOnAnimation();
break;
}
return super.onTouchEvent(event);
}
public void postInvalidateOnAnimation() {
if (Build.VERSION.SDK_INT >= 16) {
super.postInvalidateOnAnimation();
} else {
postInvalidate();
}
}
public void setState(int state) {
this.mState = state;
if (state == STATUS_LEFT) {
offsetX = 0;
} else {
offsetX = contentWidth - 2 * slideBarRadius;
}
postInvalidate();
}
public void startSlideBarAnimation(int from, int to, final int state) {
if (mSlideAnimator != null) {
mSlideAnimator.cancel();
}
mSlideAnimator = ValueAnimator.ofInt(from, to).setDuration(300);
mSlideAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
mSlideAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
offsetX = (int) animation.getAnimatedValue();
float fraction = animation.getAnimatedFraction();
if(fraction>=0.9){
mState = state;
}
postInvalidate();
}
});
mSlideAnimator.start();
}
}