自定义开关

参考文档: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);
    }
}
相关文章
相关标签/搜索