package me.xiaopan.android.examples.widget;
import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.Scroller;
/**
* Created by xiaopan on 2014/3/27 0027.
*/
public abstract class BaseSlidingToggleButton extends View implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener {
private static final int DURATION = 300;
private static final int MIN_ROLLING_DISTANCE = 30;//滚动最小生效距离
private GestureDetector gestureDetector;//手势识别器
private Scroller scroller;//滚动器
private Bitmap stateNormalBitmap;//正常状态时的状态图片
private Bitmap stateDisableBitmap;//禁用状态时的状态图片
private Bitmap stateMaskBitmap;//状态遮罩图片
private Bitmap frameBitmap;//框架图片
private Bitmap sliderNormalBitmap;//正常状态时的滑块图片
private Bitmap sliderPressedBitmap;//按下状态时的滑块图片
private Bitmap sliderDisableBitmap;//禁用状态时的滑块图片
private Bitmap sliderMaskBitmap;//滑块遮罩图片
private Paint paint;//颜料
private PorterDuffXfermode porterDuffXfermode;//遮罩类型
private boolean checked;//状态,true:开启;false:关闭
private int currentLeft;//当前状态图以及滑块图的X坐标
private int checkedLeft;//当状态为开启时状态图以及滑块图的X坐标
private int uncheckedLeft;//当状态为关闭时状态图以及滑块图的X坐标
private int scrollDistanceCount;//滚动距离计数器
private boolean needHandle;//当在一组时件中发生了滚动操作时,在弹起或者取消的时候就需要根据滚动的距离来切换状态或者回滚
private boolean down;//是否按下,用来在弹起的时候,恢复状态图以及滑块的状态
private boolean enabled;//是否可用,表示当前视图的激活状态
private OnCheckedChanageListener onCheckedChanageListener;//状态改变监听器
private boolean pendingSetState;//在调用setState()来设置初始状态的时候,如果onLeft字段还没有初始化(在Activity的onCreate()中调用此setState的时候就会出现这种情况),那么就将此字段标记为true,等到在onDraw()方法中初始化onLeft字段时,会检查此字段,如果为true就会再次调用setState()设置初始状态
private boolean pendingChecked;//记录默认状态值
public BaseSlidingToggleButton(Context context) {
super(context);
init();
}
public BaseSlidingToggleButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private final void init(){
// gestureDetector = new GestureDetector(getContext(), this);
// gestureDetector.setOnDoubleTapListener(this);
//
// stateNormalBitmap = onGetStateNormalBitmap();
// if(stateNormalBitmap == null){
// throw new RuntimeException("onGetStateNormalBitmap() The return value cannot be null");
// }
//
// stateDisableBitmap = onGetStateDisableBitmap();
// if(stateDisableBitmap == null){
// stateDisableBitmap = stateNormalBitmap;
// }
//
// stateMaskBitmap = onGetStateMaskBitmap();
// if(stateMaskBitmap == null){
// throw new RuntimeException("onGetStateMasklBitmap() The return value cannot be null");
// }
//
// frameBitmap = onGetFrameBitmap();
// if(frameBitmap == null){
// throw new RuntimeException("onGetFrameBitmap() The return value cannot be null");
// }
//
// sliderNormalBitmap = onGetSliderNormalBitmap();
// if(sliderNormalBitmap == null){
// throw new RuntimeException("onGetSliderNormalBitmap() The return value cannot be null");
// }
//
// sliderPressedBitmap = onGetSliderPressedBitmap();
// if(sliderPressedBitmap == null){
// sliderPressedBitmap = sliderNormalBitmap;
// }
//
//
// sliderDisableBitmap = onGetSliderDisableBitmap();
// if(sliderDisableBitmap == null){
// sliderDisableBitmap = sliderNormalBitmap;
// }
//
// sliderMaskBitmap = onGetSliderMaskBitmap();
// if(sliderMaskBitmap == null){
// throw new RuntimeException("onGetSliderMaskBitmap() The return value cannot be null");
// }
//
// paint = new Paint();
// paint.setFilterBitmap(false);
// porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
// scroller = new Scroller(getContext(), new AccelerateDecelerateInterpolator());
// enabled = isEnabled();
setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("触摸");
// if(!enabled){
// return false;
// }
// //先经过手势识别器的处理
// gestureDetector.onTouchEvent(event);
//
// //如果当前事件是弹起或者取消
// // if(event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP){
// if(event.getAction() == MotionEvent.ACTION_UP){
// //如果之前发生了按下事件,那么此时一定要恢复显示的滑块图片为正常状态时的图片
// if(down){
// down = false;
// invalidate();
// }
//
// //如果本次事件中发生了滑动,那么此时需要判断是否需要切换状态还是需要回滚到原来的位置
// if(needHandle){
// //如果本次滚动的距离超过的最小生效距离,就切换状态,否则就回滚
// if(Math.abs(scrollDistanceCount) >= MIN_ROLLING_DISTANCE){
// setChecked(scrollDistanceCount > 0, currentLeft, DURATION, false, false);
// }else{
// setChecked(isChecked(), currentLeft, DURATION, false, true);
// }
// needHandle = false;
// }
// }
// invalidate();
return true;
}
});
}
// @Override
// protected void onDraw(Canvas canvas) {
// //初始化状态为开启时状态图以及滑块图的X坐标
// if(checkedLeft == 0){
// checkedLeft = -1 * (stateNormalBitmap.getWidth() - frameBitmap.getWidth());//选中时的X坐标就是状态层的宽度减去框架层的宽度的负值
// //如果有需要设置的状态
// if(pendingSetState){
// pendingSetState = false;
// setDefaultChecked(pendingChecked);
// }
// }
//
// //创建一个新的全透明图层,大小同当前视图的大小一样,这一步绝对不可缺少,要不然最周绘制出来的图片背景会是黑色的
// 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);
//
// //绘制状态层
// canvas.drawBitmap(enabled?stateNormalBitmap:stateDisableBitmap, currentLeft, 0, paint);
// paint.setXfermode(porterDuffXfermode);
// canvas.drawBitmap(stateMaskBitmap, 0, 0, paint);//使用遮罩模式只显示状态层中和状态遮罩重合的部分
// paint.setXfermode(null);//因为是共用一个Paint,所以要立马清除掉遮罩效果
//
// //绘制框架层
// canvas.drawBitmap(frameBitmap, 0, 0, paint);
//
// //绘制滑块层
// if(enabled){
// canvas.drawBitmap(down?sliderPressedBitmap:sliderNormalBitmap, currentLeft, 0, paint);
// }else{
// canvas.drawBitmap(sliderDisableBitmap, currentLeft, 0, paint);
// }
// paint.setXfermode(porterDuffXfermode);
// canvas.drawBitmap(sliderMaskBitmap, 0, 0, paint);//使用遮罩模式只显示滑块层中和滑块遮罩重合的部分
// paint.setXfermode(null);//因为是共用一个Paint,所以要立马清除掉遮罩效果
//
// //合并图层
// canvas.restore();
//
// super.onDraw(canvas);
// }
// @Override
// protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// //计算宽度
// int realWidthSize = 0;
// int widthMode = MeasureSpec.getMode(widthMeasureSpec);//解析宽度参考类型
// int widthSize = MeasureSpec.getSize(widthMeasureSpec);//解析宽度尺寸
// switch (widthMode) {
// case MeasureSpec.AT_MOST://如果widthSize是当前视图可使用的最大宽度
// realWidthSize = frameBitmap.getWidth();
// break;
// case MeasureSpec.EXACTLY://如果widthSize是当前视图可使用的绝对宽度
// realWidthSize = widthSize;
// break;
// case MeasureSpec.UNSPECIFIED://如果widthSize对当前视图宽度的计算没有任何参考意义
// realWidthSize = frameBitmap.getWidth();
// break;
// }
//
// //计算高度
// int realHeightSize = 0;
// int heightMode = MeasureSpec.getMode(heightMeasureSpec);//解析参考类型
// int heightSize = MeasureSpec.getSize(heightMeasureSpec);//解析高度尺寸
// switch (heightMode) {
// case MeasureSpec.AT_MOST://如果heightSize是当前视图可使用的最大高度
// realHeightSize = frameBitmap.getHeight();
// break;
// case MeasureSpec.EXACTLY://如果heightSize是当前视图可使用的绝对高度
// realHeightSize = heightSize;
// break;
// case MeasureSpec.UNSPECIFIED://如果heightSize对当前视图高度的计算没有任何参考意义
// realHeightSize = frameBitmap.getHeight();
// break;
// }
//
// setMeasuredDimension(realWidthSize, realHeightSize);
// }
@Override
public void computeScroll() {
// //如果正处于滚动中那么就更改X坐标并刷新
// if(scroller.computeScrollOffset()){
// currentLeft = scroller.getCurrX();
// invalidate();
// }
}
// @Override
// public boolean onTouchEvent(MotionEvent event) {
// System.out.println("触摸");
// if(enabled){
// //先经过手势识别器的处理
// gestureDetector.onTouchEvent(event);
//
// //如果当前事件是弹起或者取消
//// if(event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP){
// if(event.getAction() == MotionEvent.ACTION_UP){
// //如果之前发生了按下事件,那么此时一定要恢复显示的滑块图片为正常状态时的图片
// if(down){
// down = false;
// invalidate();
// }
//
// //如果本次事件中发生了滑动,那么此时需要判断是否需要切换状态还是需要回滚到原来的位置
// if(needHandle){
// //如果本次滚动的距离超过的最小生效距离,就切换状态,否则就回滚
// if(Math.abs(scrollDistanceCount) >= MIN_ROLLING_DISTANCE){
// setChecked(scrollDistanceCount > 0, currentLeft, DURATION, false, false);
// }else{
// setChecked(isChecked(), currentLeft, DURATION, false, true);
// }
// needHandle = false;
// }
// }
// }
// return true;
// }
@Override
public boolean onDown(MotionEvent e) {
// scrollDistanceCount = 0;
// needHandle = false;
//
// //切换滑块图片的状态
// down = true;
// invalidate();
return true;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// toggle();
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// needHandle = true;//标记在弹起或取消的时候需要处理
// scrollDistanceCount += distanceX;//记录本次总的滑动的距离
// currentLeft -= distanceX;//计算接下来状态层以及滑块曾的X坐标
// //防止滑动的过程中超过范围
// if(currentLeft >= uncheckedLeft){
// currentLeft = uncheckedLeft;
// }else if(currentLeft <= checkedLeft){
// currentLeft = checkedLeft;
// }
// invalidate();
return true;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// needHandle = false;//标记在弹起或取消时不再处理
// setChecked(e2.getX() < e1.getX(), currentLeft, DURATION, false, false);//根据前后两次X坐标的大小,判断接下来谁要切换为开启状态还是关闭状态
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
return true;
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
this.enabled = enabled;
}
public boolean isChecked() {
return checked;
}
/**
* 滚动
* @param startX 开始X坐标
* @param endX 结束Y坐标
* @param duration 持续时间
*/
private void scroll(int startX, int endX, int duration){
// //当开始位置和结束位置一样时不处理
// if(startX != endX){
// scroller.startScroll(startX, 0, endX - startX, 0, duration);
// invalidate();
// }
}
/**
* 设置状态
* @param isChecked 开启还是关闭
* @param startX 开始滚动的位置
* @param duration 持续时间
* @param isDefaultValue 是否是默认值
* @param isRollback 是否是回滚的
*/
private void setChecked(boolean isChecked, int startX, int duration, boolean isDefaultValue, boolean isRollback){
// if(isRollback || this.checked != isChecked){
// this.checked = isChecked;
// //如果是要开启
// if(isChecked()){
// scroll(startX, checkedLeft, isDefaultValue?0:duration);
// }else{
// scroll(startX, uncheckedLeft, isDefaultValue?0:duration);
// }
// if(!isRollback){
// //调用选中状态改变回调
// if(onCheckedChanageListener != null && !isDefaultValue){
// onCheckedChanageListener.onCheckedChanage(this, isChecked());
// }
// }
// }
}
/**
* 设置状态
* @param isChecked 开启还是关闭
* @param duration 持续时间
*/
public void setChecked(boolean isChecked, int duration){
// //如果尚未完成初始化工作,就先延迟,等待初始化完毕之后再处理
// if(checkedLeft == 0){
// pendingSetState = true;
// pendingChecked = isChecked;
// }else{
// setChecked(isChecked, isChecked?uncheckedLeft:checkedLeft, duration, false, false);
// }
}
/**
* 设置状态
* @param isChecked 开启还是关闭
*/
public void setChecked(boolean isChecked){
// setChecked(isChecked, DURATION);
}
/**
* 设置默认状态,设置默认状态时不会有动画效果,并且不会触发状态改变监听器,适合用于Adapter中
* @param isChecked 开启还是关闭
*/
public void setDefaultChecked(boolean isChecked){
// //如果尚未完成初始化工作,就先延迟,等待初始化完毕之后再处理
// if(checkedLeft == 0){
// pendingSetState = true;
// pendingChecked = isChecked;
// }else{
// setChecked(isChecked, isChecked?uncheckedLeft:checkedLeft, 0, true, false);
// }
}
/**
* 切换状态
* @param duration 持续时间
*/
public void toggle(int duration){
setChecked(!isChecked(), duration);
}
/**
* 切换状态
*/
public void toggle(){
setChecked(!isChecked());
}
public OnCheckedChanageListener getOnCheckedChanageListener() {
return onCheckedChanageListener;
}
/**
* 设置选中状态改变监听器
* @param onCheckedChanageListener 选中状态改变监听器
*/
public void setOnCheckedChanageListener(OnCheckedChanageListener onCheckedChanageListener) {
this.onCheckedChanageListener = onCheckedChanageListener;
}
/**
* 选中状态改变监听器
*/
public interface OnCheckedChanageListener{
/**
* 当选中状态发生改变
* @param slidingToggleButton
* @param isChecked 是否选中
*/
public void onCheckedChanage(BaseSlidingToggleButton slidingToggleButton, boolean isChecked);
}
/**
* 获取正常状态时的状态图片
* @return
*/
public abstract Bitmap onGetStateNormalBitmap();
/**
* 获取禁用状态时的状态图片
* @return
*/
public abstract Bitmap onGetStateDisableBitmap();
/**
* 获取状态遮罩图片
* @return
*/
public abstract Bitmap onGetStateMaskBitmap();
/**
* 获取框架图片
* @return
*/
public abstract Bitmap onGetFrameBitmap();
/**
* 获取正常状态时的滑块图片
* @return
*/
public abstract Bitmap onGetSliderNormalBitmap();
/**
* 获取按下状态时的滑块图片
* @return
*/
public abstract Bitmap onGetSliderPressedBitmap();
/**
* 获取禁用状态时的滑块图片
* @return
*/
public abstract Bitmap onGetSliderDisableBitmap();
/**
* 获取滑块遮罩图片
* @return
*/
public abstract Bitmap onGetSliderMaskBitmap();
}