package me.xiaopan.android.examples.widget;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.animation.*;
import android.widget.CompoundButton;
import android.widget.Scroller;
import me.xiaopan.android.examples.R;
/**
* Created by xiaopan on 2014/3/27 0027.
*/
public class SwitchButton extends CompoundButton {
private Bitmap frameBitmap;
private Bitmap sliderMaskBitmap;//滑块遮罩图片
private Bitmap statusMaskBitmap;//状态遮罩图片
private Drawable statusDrawable;
private Drawable sliderDrawable;
private Paint paint;
private PorterDuffXfermode porterDuffXfermode;//遮罩类型
private float touchX;
private int slideX;
private int minSlideX;
private int maxSlideX;
private int moveDistance;
private int minChancgeDistance = 30;
private boolean isMoved;
private SwitchScroller switchScroller;
public SwitchButton(Context context) {
super(context);
init();
}
public SwitchButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SwitchButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init(){
paint = new Paint();
porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
switchScroller = new SwitchScroller(getContext(), new AccelerateDecelerateInterpolator());
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//计算宽度
int measureWidth;
switch (MeasureSpec.getMode(widthMeasureSpec)) {
case MeasureSpec.AT_MOST://如果widthSize是当前视图可使用的最大宽度
measureWidth = frameBitmap != null?frameBitmap.getWidth():0;
break;
case MeasureSpec.EXACTLY://如果widthSize是当前视图可使用的绝对宽度
measureWidth = MeasureSpec.getSize(widthMeasureSpec);
break;
case MeasureSpec.UNSPECIFIED://如果widthSize对当前视图宽度的计算没有任何参考意义
measureWidth = frameBitmap != null?frameBitmap.getWidth():0;
break;
default:
measureWidth = frameBitmap != null?frameBitmap.getWidth():0;
break;
}
//计算高度
int measureHeight;
switch (MeasureSpec.getMode(heightMeasureSpec)) {
case MeasureSpec.AT_MOST://如果heightSize是当前视图可使用的最大宽度
measureHeight = frameBitmap != null?frameBitmap.getHeight():0;
break;
case MeasureSpec.EXACTLY://如果heightSize是当前视图可使用的绝对宽度
measureHeight = MeasureSpec.getSize(heightMeasureSpec);
break;
case MeasureSpec.UNSPECIFIED://如果heightSize对当前视图宽度的计算没有任何参考意义
measureHeight = frameBitmap != null?frameBitmap.getHeight():0;
break;
default:
measureHeight = frameBitmap != null?frameBitmap.getHeight():0;
break;
}
setMeasuredDimension(measureWidth, measureHeight);
}
@Override
protected void onDraw(Canvas canvas) {
//保存并创建一个新的透明层,如果不这样做的话,画出来的背景会是黑的
canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
//绘制状态层
if(statusDrawable != null && statusMaskBitmap != null){
canvas.save();
canvas.translate(slideX, 0);
statusDrawable.draw(canvas);
canvas.restore();
paint.setXfermode(porterDuffXfermode);
canvas.drawBitmap(statusMaskBitmap, 0, 0, paint);
paint.setXfermode(null);
}
//绘制框架层
if(frameBitmap != null){
canvas.drawBitmap(frameBitmap, 0, 0, paint);
}
//绘制滑块层
if(sliderDrawable != null && sliderMaskBitmap != null){
canvas.save();
canvas.translate(slideX, 0);
sliderDrawable.draw(canvas);
canvas.restore();
paint.setXfermode(porterDuffXfermode);
canvas.drawBitmap(sliderMaskBitmap, 0, 0, paint);
paint.setXfermode(null);
}
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch(event.getAction()){
case MotionEvent.ACTION_DOWN :
isMoved = false;
moveDistance = 0;
touchX = event.getX();
setPressed(true);
break;
case MotionEvent.ACTION_MOVE :
isMoved = true;
float newTouchX = event.getX();
int currentDistance = (int) (newTouchX - touchX);
moveDistance += currentDistance;
setSlideX(slideX += currentDistance);
touchX = newTouchX;
invalidate();
break;
case MotionEvent.ACTION_UP :
setPressed(false);
if(Math.abs(moveDistance) > 10){//当滑动距离大于10才会被认为这是一次有效的滑动操作
if(Math.abs(moveDistance) > minChancgeDistance){//如果滑动距离大于等于最小切换距离就切换状态
setChecked(!isChecked());
}else{
switchScroller.scroll(isChecked());//本次滑动无效,回滚
post(switchScroller);
}
}else{
setChecked(!isChecked()); //单击切换状态
}
break;
case MotionEvent.ACTION_CANCEL :
switchScroller.scroll(isChecked());
post(switchScroller);
break;
case MotionEvent.ACTION_OUTSIDE :
switchScroller.scroll(isChecked());
post(switchScroller);
break;
}
return true;
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if(statusDrawable != null){
statusDrawable.setState(getDrawableState());
}
if(sliderDrawable != null){
sliderDrawable.setState(getDrawableState());
}
invalidate();
}
@Override
public void setChecked(boolean checked) {
super.setChecked(checked);
if(switchScroller != null){
switchScroller.scroll(checked);
post(switchScroller);
}
}
public void setDrawables(Bitmap frameBitmap, Drawable statusDrawable, Bitmap statusMaskBitmap, Drawable sliderDrawable, Bitmap sliderMaskBitmap){
if(frameBitmap == null || statusDrawable == null || statusMaskBitmap == null || sliderDrawable == null || sliderMaskBitmap == null){
throw new IllegalArgumentException("ALL NOT NULL");
}
this.frameBitmap = frameBitmap;
this.statusDrawable = statusDrawable;
this.statusMaskBitmap = statusMaskBitmap;
this.sliderDrawable = sliderDrawable;
this.sliderMaskBitmap = sliderMaskBitmap;
this.statusDrawable.setBounds(0, 0, this.statusDrawable.getIntrinsicWidth(), this.statusDrawable.getIntrinsicHeight());
this.statusDrawable.setCallback(this);
this.sliderDrawable.setBounds(0, 0, this.sliderDrawable.getIntrinsicWidth(), this.sliderDrawable.getIntrinsicHeight());
this.sliderDrawable.setCallback(this);
this.minSlideX = (-1 * (statusDrawable.getIntrinsicWidth() - frameBitmap.getWidth()));
requestLayout();
}
public void setDrawableResIds(int frameBitmapResId, int statusDrawableResId, int statusMaskBitmapResId, int sliderDrawableResId, int sliderMaskBitmapResId){
setDrawables(
BitmapFactory.decodeResource(getResources(), R.drawable.button_sliding_frame),
getResources().getDrawable(R.drawable.selector_switch_status),
BitmapFactory.decodeResource(getResources(), R.drawable.button_sliding_state_mask),
getResources().getDrawable(R.drawable.selector_switch_slider),
BitmapFactory.decodeResource(getResources(), R.drawable.button_sliding_slider_mask)
);
}
private void setSlideX(int slideX) {
this.slideX = slideX;
if(this.slideX < minSlideX){
this.slideX = minSlideX;
}
if(this.slideX > maxSlideX){
this.slideX = maxSlideX;
}
}
private class SwitchScroller implements Runnable{
private Scroller scroller;
public SwitchScroller(Context context, android.view.animation.Interpolator interpolator) {
this.scroller = new Scroller(context, interpolator);
}
public void scroll(boolean checked){
scroller.startScroll(slideX, 0, (checked?minSlideX:maxSlideX) - slideX, 0, 300);
}
@Override
public void run() {
if(scroller.computeScrollOffset()){
setSlideX(scroller.getCurrX());
invalidate();
post(this);
}
}
}
}