自定義左右按鈕選擇控制元件
阿新 • • 發佈:2019-02-03
public class UISwitchButton extends CheckBox { private Paint mPaint; private RectF mSaveLayerRectF; private float mFirstDownY; private float mFirstDownX; private int mClickTimeout; private int mTouchSlop; private final int MAX_ALPHA = 255; private int mAlpha = MAX_ALPHA; private boolean mChecked = false; private boolean mBroadcasting;// 標示是否正在執行監聽事件�? private boolean mTurningOn;// 標示位置是否達到�?啟狀�? private PerformClick mPerformClick; private OnCheckedChangeListener mOnCheckedChangeListener; private OnCheckedChangeListener mOnCheckedChangeWidgetListener; private boolean mAnimating;// 標示是否繼續執行移動動畫 private final float VELOCITY = 350;// 定義按鈕動畫移動的最大長�? private float mVelocity;// 按鈕動畫移動的最大畫素長�? private float mAnimationPosition;// 按鈕動畫移動的當前位�? private float mAnimatedVelocity;// 按鈕動畫移動的實際位�?(+mVelocity/-mVelocity) private Bitmap bmBgGreen;// 綠色背景 private Bitmap bmBgWhite;// 白色背景 private Bitmap bmBtnNormal;// 未按下時按鈕 private Bitmap bmBtnPressed;// 按下時按�? private Bitmap bmCurBtnPic;// 當前顯示的按鈕圖�? private Bitmap bmCurBgPic;// 當前背景圖片 private float bgWidth;// 背景寬度 private float bgHeight;// 背景寬度 private float btnWidth;// 按鈕寬度 private float offBtnPos;// 按鈕關閉時位�? private float onBtnPos;// 按鈕�?啟時位置 private float curBtnPos;// 按鈕當前位置 private float startBtnPos;// �?始按鈕位�? private final int COMMON_WIDTH_IN_PIXEL = 90;// 預設寬度 private final int COMMON_HEIGHT_IN_PIXEL = 50;// 預設高度 public UISwitchButton(Context context, AttributeSet attrs) { this(context, attrs, android.R.attr.checkboxStyle); } public UISwitchButton(Context context) { this(context, null); } public UISwitchButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs); } private void init(Context context, AttributeSet attrs) { mPaint = new Paint(); mPaint.setColor(Color.WHITE); Resources resources = context.getResources(); // get attrConfiguration TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SwitchButton); int width = (int) array.getDimensionPixelSize( R.styleable.SwitchButton_bmWidth, 0); int height = (int) array.getDimensionPixelSize( R.styleable.SwitchButton_bmHeight, 0); array.recycle(); // size width or height if (width <= 0 || height <= 0) { width = COMMON_WIDTH_IN_PIXEL; height = COMMON_HEIGHT_IN_PIXEL; } else { float scale = (float) COMMON_WIDTH_IN_PIXEL / COMMON_HEIGHT_IN_PIXEL; if ((float) width / height > scale) { width = (int) (height * scale); } else if ((float) width / height < scale) { height = (int) (width / scale); } } // get viewConfiguration mClickTimeout = ViewConfiguration.getPressedStateDuration() + ViewConfiguration.getTapTimeout(); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); // get Bitmap bmBgGreen = BitmapFactory.decodeResource(resources, R.drawable.switch_btn_bg_green); bmBgWhite = BitmapFactory.decodeResource(resources, R.drawable.switch_btn_bg_white); bmBtnNormal = BitmapFactory.decodeResource(resources, R.drawable.switch_btn_normal); bmBtnPressed = BitmapFactory.decodeResource(resources, R.drawable.switch_btn_pressed); // size Bitmap bmBgGreen = Bitmap.createScaledBitmap(bmBgGreen, width, height, true); bmBgWhite = Bitmap.createScaledBitmap(bmBgWhite, width, height, true); bmBtnNormal = Bitmap.createScaledBitmap(bmBtnNormal, height, height, true); bmBtnPressed = Bitmap.createScaledBitmap(bmBtnPressed, height, height, true); bmCurBtnPic = bmBtnNormal;// 初始按鈕圖片 bmCurBgPic = mChecked ? bmBgGreen : bmBgWhite;// 初始背景圖片 bgWidth = bmBgGreen.getWidth();// 背景寬度 bgHeight = bmBgGreen.getHeight();// 背景高度 btnWidth = bmBtnNormal.getWidth();// 按鈕寬度 offBtnPos = 0;// 關閉時在�?左邊 onBtnPos = bgWidth - btnWidth;// �?始時在右�? curBtnPos = mChecked ? onBtnPos : offBtnPos;// 按鈕當前為初始位�? // get density float density = resources.getDisplayMetrics().density; mVelocity = (int) (VELOCITY * density + 0.5f);// 動畫距離 mSaveLayerRectF = new RectF(0, 0, bgWidth, bgHeight); } @Override public void setEnabled(boolean enabled) { mAlpha = enabled ? MAX_ALPHA : MAX_ALPHA / 3; super.setEnabled(enabled); } public boolean isChecked() { return mChecked; } public void toggle() { setChecked(!mChecked); } private void setCheckedDelayed(final boolean checked) { postDelayed(new Runnable() { @Override public void run() { setChecked(checked); } }, 10); } public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; // 初始化按鈕位�? curBtnPos = checked ? onBtnPos : offBtnPos; // 改變背景圖片 bmCurBgPic = checked ? bmBgGreen : bmBgWhite; invalidate(); if (mBroadcasting) { // NO-OP return; } // 正在執行監聽事件 mBroadcasting = true; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(UISwitchButton.this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged( UISwitchButton.this, mChecked); } // 監聽事件結束 mBroadcasting = false; } } public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { mOnCheckedChangeListener = listener; } void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) { mOnCheckedChangeWidgetListener = listener; } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); float x = event.getX(); float y = event.getY(); float deltaX = Math.abs(x - mFirstDownX); float deltaY = Math.abs(y - mFirstDownY); switch (action) { case MotionEvent.ACTION_DOWN: ViewParent mParent = getParent(); if (mParent != null) { // 通知父控制元件不要攔截本view的觸控事�? mParent.requestDisallowInterceptTouchEvent(true); } mFirstDownX = x; mFirstDownY = y; bmCurBtnPic = bmBtnPressed; startBtnPos = mChecked ? onBtnPos : offBtnPos; break; case MotionEvent.ACTION_MOVE: float time = event.getEventTime() - event.getDownTime(); curBtnPos = startBtnPos + event.getX() - mFirstDownX; if (curBtnPos >= onBtnPos) { curBtnPos = onBtnPos; } if (curBtnPos <= offBtnPos) { curBtnPos = offBtnPos; } mTurningOn = curBtnPos > bgWidth / 2 - btnWidth / 2; break; case MotionEvent.ACTION_UP: bmCurBtnPic = bmBtnNormal; time = event.getEventTime() - event.getDownTime(); if (deltaY < mTouchSlop && deltaX < mTouchSlop && time < mClickTimeout) { if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } } else { startAnimation(mTurningOn); } break; } invalidate(); return isEnabled(); } private class PerformClick implements Runnable { public void run() { performClick(); } } @Override public boolean performClick() { startAnimation(!mChecked); return true; } @Override protected void onDraw(Canvas canvas) { canvas.saveLayerAlpha(mSaveLayerRectF, mAlpha, 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(bmCurBgPic, 0, 0, mPaint); // 繪製按鈕 canvas.drawBitmap(bmCurBtnPic, curBtnPos, 0, mPaint); canvas.restore(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension((int) bgWidth, (int) bgHeight); } private void startAnimation(boolean turnOn) { mAnimating = true; mAnimatedVelocity = turnOn ? mVelocity : -mVelocity; mAnimationPosition = curBtnPos; new SwitchAnimation().run(); } private void stopAnimation() { mAnimating = false; } private final class SwitchAnimation implements Runnable { @Override public void run() { if (!mAnimating) { return; } doAnimation(); requestAnimationFrame(this); } } private void doAnimation() { mAnimationPosition += mAnimatedVelocity * ANIMATION_FRAME_DURATION / 1000; if (mAnimationPosition <= offBtnPos) { stopAnimation(); mAnimationPosition = offBtnPos; setCheckedDelayed(false); } else if (mAnimationPosition >= onBtnPos) { stopAnimation(); mAnimationPosition = onBtnPos; setCheckedDelayed(true); } curBtnPos = mAnimationPosition; invalidate(); } private static final int MSG_ANIMATE = 1000; public static final int ANIMATION_FRAME_DURATION = 1000 / 60; public void requestAnimationFrame(Runnable runnable) { Message message = new Message(); message.what = MSG_ANIMATE; message.obj = runnable; mHandler.sendMessageDelayed(message, ANIMATION_FRAME_DURATION); } private Handler mHandler = new Handler() { public void handleMessage(Message m) { switch (m.what) { case MSG_ANIMATE: if (m.obj != null) { ((Runnable) m.obj).run(); } break; } } }; }