Android自定義控制元件模仿iOS滑塊SwitchButton

王世暉發表於2016-03-15

SwitchButton可以點選的時候切換狀態,類似CheckBox

在拖動的時候,也可以根據拖動的距離判斷是否切換狀態,類似ToggleButton

因此要區別出單擊事件和拖動事件

實現效果如圖所示:


自定義的SwitchButton如下:

public class SwitchButton extends View implements View.OnTouchListener {
    private Bitmap bg_on, bg_off, slipper_btn;
    /**
     * 按下時的x和當前的x
     */
    private float downX, nowX;
    /**
     * 記錄使用者是否在滑動
     */
    private boolean onSlip = false;
    /**
     * 當前的狀態
     */
    private boolean nowStatus = false;
    /**
     * 監聽介面
     */
    private OnChangedListener listener;
    /*
    * 一個滑動的距離臨界值,判斷是滑動還是點選
    * getScaledTouchSlop():
    * Distance in pixels a touch can wander before we think the user is scrolling
    * */
    private  int mTouchSlop=new ViewConfiguration().getScaledTouchSlop();

    public SwitchButton(Context context) {
        super(context);
        init();
    }
    public SwitchButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    public void init(){
        //載入圖片資源
        bg_on = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_on_on);
        bg_off = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_off_off);
        slipper_btn = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_ball_ball);
        setOnTouchListener(this);
    }
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Matrix matrix = new Matrix();
        Paint paint = new Paint();
        float x = 0;
        //根據nowX設定背景,開或者關狀態
        if (nowX < (bg_on.getWidth()/2)){
            canvas.drawBitmap(bg_off, matrix, paint);//畫出關閉時的背景
        }else{
            canvas.drawBitmap(bg_on, matrix, paint);//畫出開啟時的背景
        }
        if (onSlip) {//是否是在滑動狀態,
            if(nowX >= bg_on.getWidth())//是否劃出指定範圍,不能讓滑塊跑到外頭,必須做這個判斷
                x = bg_on.getWidth() - slipper_btn.getWidth()/2;//減去滑塊1/2的長度
            else
                x = nowX - slipper_btn.getWidth()/2;
        }else {
            if(nowStatus){//根據當前的狀態設定滑塊的x值
                x = bg_on.getWidth() - slipper_btn.getWidth();
            }else{
                x = 0;
            }
        }
        //對滑塊滑動進行異常處理,不能讓滑塊出界
        if (x < 0 ){
            x = 0;
        }
        else if(x > bg_on.getWidth() - slipper_btn.getWidth()){
            x = bg_on.getWidth() - slipper_btn.getWidth();
        }
        //畫出滑塊
        canvas.drawBitmap(slipper_btn, x, 0, paint);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:{
                if (event.getX() > bg_off.getWidth() || event.getY() > bg_off.getHeight()){
                    return false;
                }else{
                    onSlip = true;
                    downX = event.getX();
                    nowX = downX;
                }
                break;
            }
            case MotionEvent.ACTION_MOVE:{
                nowX = event.getX();
                break;
            }
            case MotionEvent.ACTION_UP:{
                DebugLog.e("mTouchSlop:"+mTouchSlop);
                onSlip = false;
                nowX = event.getX();
                float float_distance=nowX - downX;
                int int_disatnce=(int)float_distance;
                DebugLog.e("int_disatnce:"+int_disatnce);
                /*滑動距離太短,認定是點選事件*/
                if(Math.abs(int_disatnce)<mTouchSlop){
                    if(this.isChecked()){
                        this.setChecked(false);
                        nowX = 0;
                    }else{
                        this.setChecked(true);
                        nowX = bg_on.getWidth() - slipper_btn.getWidth();
                    }
                }else{
                    /*滑動距離足夠,認為是滑動事件*/
                    if(event.getX() >= (bg_on.getWidth()/2)){
                        nowStatus = true;
                        nowX = bg_on.getWidth() - slipper_btn.getWidth();
                    }else{
                        nowStatus = false;
                        nowX = 0;
                    }
                }
                if(listener != null){
                    listener.OnChanged(SwitchButton.this, nowStatus);
                }
                break;
            }
        }
        //重新整理介面
        invalidate();
        return true;
    }
    /**
     * 為WiperSwitch設定一個監聽,供外部呼叫的方法
     * @param listener
     */
    public void setOnChangedListener(OnChangedListener listener){
        this.listener = listener;
    }
    /**
     * 設定滑動開關的初始狀態,供外部呼叫
     * @param checked
     */
    public void setChecked(boolean checked){
        if(checked){
            nowX = bg_off.getWidth();
        }else{
            nowX = 0;
        }
        nowStatus = checked;
    }
    public boolean isChecked() {
        return nowStatus;
    }
    /**
     * 回撥介面
     *
     */
    public interface OnChangedListener {
        public void OnChanged(SwitchButton wiperSwitch, boolean checkState);
    }
}

佈局檔案中使用:

<com.uestcneon.chuji.changjianglife.share.SwitchButton
    android:id="@+id/user_privacy_state"
    android:layout_width="wrap_content"
    android:layout_height="20dp"
    android:layout_marginLeft="30dp" />

控制元件用到的3個資源圖片:


相關文章