自定義開關

參考文檔:java

    http://blog.csdn.net/singwhatiwanna/article/details/9254309android

    Android開發文檔API的View
canvas

    本文中自定義開關MySwitch是我在閱讀了上述文檔,根據本身的理解寫的,所引用的圖片資源來自http://home.ustc.edu.cn/~voa/res/HelloJni.apkide

    代碼已在Android 2.2的模擬器和Android 4.2.1的手機上測試,都可運行。post

    手機測試效果以下:
測試

    a 默認效果
動畫

    

    b 向右滑,滑過中間位置動效果
this

    

    c 鬆開手指,自動滑到右部效果
spa

    

    d 向左滑,滑過中間位置動效果.net

    

    鬆開手指,又回到a所示狀態。

    MySwitch代碼:

package cn.mswitch;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
/**
 * 自定義開關按鈕類,打開狀態值爲true,關閉爲false
 * @author liuwh
 * @since  2014-05-2
 */
public class MySwitch extends View {
    private static final String TAG = "MySwitch";
    
    /**
     * 關閉狀態
     */
    private static final int STATE_OFF = 0;
    /**
     * 打開狀態
     */
    private static final int STATE_ON = 1;
    /**
     * 正在滑動狀態
     */
    private static final int STATE_SCROLL = 2;
    
    private static Bitmap bitmapOFF;//關閉狀態背景圖片
    private static Bitmap bitmapON;//打開狀態背景圖片
    private static Bitmap bitmapThumb;//圓點圖片(爲正方形)
    private static int bgWidth;//背景寬度
    private static int bgHeigth;//背景高度
    private static int thumbWidth;//圓點寬度
    
    /**
     * MySwitch狀態標誌,值爲true或false,默認爲false,即關閉
     */
    private boolean mState = false;
    
    /**
     * 滑動事件標誌,true爲滑動事件,false則爲點擊事件
     */
    private boolean mHasScroll = false;
    
    /**
     * MySwitch當前狀態標誌,值爲0,1,2,
     * 0對應狀態STATE_OFF
     * 1對應狀態STATE_ON
     * 2對應狀態STATE_SCROLL
     */
    private int currState = 0;
    
    public MySwitch(Context context) {
        super(context);
        //DisplayMetrics dm = new DisplayMetrics();
        //float density = context.getResources().getDisplayMetrics().density;
        //Log.i(TAG, "density= " + density);//屏幕密度
    }
    
    public MySwitch(Context context, AttributeSet ats) {
        super(context, ats);
        init();
    }
    
    public MySwitch(Context context, AttributeSet ats, int dfStyle) {
        super(context, ats, dfStyle);
        init();
    }
    
    /**
     * 初始化圖片對象,寬高數據和當前狀態
     */
    private void init() {
        Resources res = getResources();
        bitmapThumb = BitmapFactory.decodeResource(res, R.drawable.switch_thumb);
        bitmapON = BitmapFactory.decodeResource(res, R.drawable.bg_switch_on);
        bitmapOFF = BitmapFactory.decodeResource(res, R.drawable.bg_switch_off);
        bgWidth = bitmapON.getWidth();
        bgHeigth = bitmapON.getHeight();
        thumbWidth = bitmapThumb.getWidth();
        currState = mState ? STATE_ON : STATE_OFF;
        Log.i(TAG, "bw: " + bgWidth + ", bh: " + bgHeigth + ", tw: " + thumbWidth);
    }
    
    @Override
    public void setLayoutParams(LayoutParams params) {
        //設置MySwitch的View區域寬高
        params.width = bgWidth;
        params.height = bgHeigth;
        super.setLayoutParams(params);
    }
    
    /**
     * 獲取MySwitch對象的狀態
     * @return  boolean值
     */
    public boolean getMState(){
        return this.mState;
    }
    
    /**
     * 設置MySwitch對象的狀態
     * @param isOn boolean值
     */
    public void setMState(boolean mState){
        if(this.mState != mState){
            //更改當前狀態currState的值
            currState = mState ? STATE_ON : STATE_OFF;
            //通知從新畫圖
            MySwitch.this.postInvalidate();
        }
        this.mState = mState;
    }
    
    //狀態改變監聽接口對象
    private OnMySwicthStateChangedListener msl;
    /**
     * 設置狀態改變監聽
     * @param mslOnMySwicthStateChangedListener對象
     */
    public void setOnMySwicthStateChangedListener(OnMySwicthStateChangedListener msl){
        this.msl = msl;
    }
    
    private int startX;
    private int currX = 0;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int pointers = event.getPointerCount();
        //若是不是一個手指,則不作處理
        if(pointers > 1) return true;
        
        int action = event.getAction();
        switch (action) {
        case MotionEvent.ACTION_DOWN://手指按下
            startX = (int) event.getX();
            //Log.i(TAG, "start: " + startX);
            break;
            
        case MotionEvent.ACTION_MOVE://手指滑動
            //設置滑動標誌爲true,並將當前狀態置爲滑動
            mHasScroll = true;
            currState = STATE_SCROLL;
            
            currX = (int)event.getX();
            currX = currX>bgWidth ? bgWidth : currX;
            //通知重畫MySwitch的View區域
            this.invalidate();
            //Log.i(TAG, "curr x: " + currX );
            break;
            
        case MotionEvent.ACTION_UP://手指鬆開
            int endX = (int)event.getX();
            //Log.i(TAG, "endX: " + endX);
            int midX = bgWidth/2;
            //Log.i(TAG, "midX= " + midX);
            
            MyAnimationTrans mat = null;
            if(mHasScroll){//滑動事件
                Log.i(TAG, "scroll event");
                //已滑到0或bgWidth位置,判斷是否須要發送狀態改變通知,再也不重畫MySwitch區域圖像
                if(endX >= bgWidth || endX <= 0){
                    isChange = startX>midX ? endX<=0 : endX>=bgWidth;
                    mState = isChange? endX>=bgWidth : mState;
                    postStateChanged();
                    mHasScroll = false;
                    return true;
                }
                //圖片滑向的終點位置
                int toX = endX < midX ? 0 : bgWidth;
                mat = new MyAnimationTrans(endX, toX);
            }
            else{//點擊事件
                Log.i(TAG, "click event");
                if(mState){
                //點擊處在關閉區域,滑向0位置
                    if(endX < midX){
                        mat = new MyAnimationTrans(endX, 0);
                    }
                }else{
                //點擊處在打開區域,滑向bgWidth位置
                    if(endX > midX){
                        mat = new MyAnimationTrans(endX, bgWidth);
                    }
                }
            }
            //處理狀態改變響應事件
            isChange = mState ? endX<midX : endX>midX;
            mState = isChange ? endX > midX : mState;
            postStateChanged();
            
            //將滑動標誌更改成false
            mHasScroll = false;
            
            if(mat != null){
                this.invalidate();
                new Thread(mat).start();
            }
            break;
            
        default:
            break;
        }
        return true;
    }
    
    //狀態是否發生改變,true爲發生改變,false爲無
    private boolean isChange = false;
    /**
     * 響應狀態改變監聽事件
     */
    private void postStateChanged(){
    //通知狀態改變監聽接口
        if(isChange && msl != null){
            msl.onMySwicthChanged(this);
            isChange = false;
            Log.i(TAG, "ACTION_UP,發送狀態改變事件,將isChange置爲false!");
        }
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //關閉狀態
        if(currState == STATE_OFF){
            drawBitmap(canvas, null, null, bitmapOFF);
            drawBitmap(canvas, null, null, bitmapThumb);
        }
        //打開狀態
        else if(currState == STATE_ON){
            drawBitmap(canvas, null, null, bitmapON);
            int count = canvas.getSaveCount();
            canvas.translate(bgWidth - thumbWidth, 0);
            drawBitmap(canvas, null, null, bitmapThumb);
            canvas.restoreToCount(count);
        }
        //滑動狀態
        else{
            int count = canvas.save();
            if(currX > bgWidth/2){
               drawBitmap(canvas, null, null, bitmapON);
               Log.i(TAG, "狀態改變爲打開,mState=" + mState);
            }else{
                drawBitmap(canvas, null, null, bitmapOFF);
                Log.i(TAG, "狀態改變爲關閉,mState=" + mState);
            }
            canvas.restoreToCount(count);
            count = canvas.save();
            int toX = currX - thumbWidth/2;
            toX = toX < 0 ? 0 : toX > (bgWidth - thumbWidth) ? (bgWidth - thumbWidth) : toX;
            //Log.i(TAG, "toX = " + toX);
            canvas.translate( toX, 0);
            drawBitmap(canvas, null, null, bitmapThumb);
            canvas.restoreToCount(count);
        }
    }
    /**
     * 將圖片畫到屏幕中
     * @param canvasCanvas對象
     * @param fromRC初始位置
     * @param destRC終點位置
     * @param bitmap圖片對象
     */
    private void drawBitmap(Canvas canvas, Rect fromRC, Rect destRC, Bitmap bitmap){
        //destRC爲null,則從(0,0)位置開始畫圖
        destRC = destRC==null ? new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()) : destRC;
        Paint paint = new Paint();
        canvas.drawBitmap(bitmap, fromRC, destRC, paint);
    }
    /**
     * 自定義動畫類
     * @author liuwh
     *
     */
    private class MyAnimationTrans implements Runnable{
        private int fromX;
        private int endX;
        private static final int SPEED = 5;//默認移動速度
        
        public MyAnimationTrans(final int fromX, final int endX){
            this.fromX = fromX;
            this.endX = endX;
        }
        
        @Override
        public void run() {
            int speed = endX > fromX ? SPEED : -SPEED;
            currX = endX;
            //是否結束循環標誌,true爲結束
            boolean isEnd = false;
            while(true){
                currX += speed;
                if(currX > bgWidth || currX < 0){
                   currX = speed>0 ? bgWidth : 0;
                   isEnd = true;//結束循環
                }
                MySwitch.this.currState = STATE_SCROLL;
                MySwitch.this.postInvalidate();
                if(isEnd){
                   break;
                }
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 狀態改變監聽
     * @author liuwh
     *
     */
    public interface OnMySwicthStateChangedListener{
        void onMySwicthChanged(MySwitch mySwitch);
    }
}
相關文章
相關標籤/搜索