自定义开关滑动按钮控件

package com.loaderman.switch;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 控件绘制流程:
 * <p>
 * 1. measure方法来确定控件尺寸
 * 2. layout方法确定控件位置
 * 3. draw方法绘制控件内容
 * <p>
 * measure->layout->draw
 * onMeasure->onLayout->onDraw
 * <p>
 * 1280x720  xhdpi
 * 480x800 hdpi
 */
public class MySwitch extends View {

    private Paint paint;
    private Bitmap mBitmapBg;
    private Bitmap mBitmapSlide;
    private int MAX_LEFT;//最大左边距
    private int mSlideLeft;//当前左边距
    private boolean isOpen = false;//当前开关状态
    private static final String NAMESPACE = "http://schemas.android.com/apk/res-auto";
    public MySwitch(Context context) {
        this(context, null);
    }
    public MySwitch(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }
    public MySwitch(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
        //设置开关初始状态
        isOpen = attrs.getAttributeBooleanValue(NAMESPACE, "isOpen", false);
        if(isOpen) {
            mSlideLeft = MAX_LEFT;
        }else {
            mSlideLeft = 0;
        }
        //设置滑块图片
        int slideDrawable = attrs.getAttributeResourceValue(NAMESPACE, "slideDrawable", -1);
        mBitmapSlide = BitmapFactory.decodeResource(getResources(), slideDrawable);

        invalidate();
    }
    private void init() {
        paint = new Paint();
        paint.setColor(Color.RED);
        //初始化背景图片
        mBitmapBg = BitmapFactory.decodeResource(getResources(), R.drawable.switch_background);
        //滑块图片
        mBitmapSlide = BitmapFactory.decodeResource(getResources(), R.drawable.slide_button);
        MAX_LEFT = mBitmapBg.getWidth() - mBitmapSlide.getWidth();
        //设置点击事件
        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isClick) {
                    if (isOpen) {
                        mSlideLeft = 0;
                        isOpen = false;
                    } else {
                        mSlideLeft = MAX_LEFT;
                        isOpen = true;
                    }

                    //刷新当前控件
                    invalidate();//此方法会导致onDraw重新走一遍

                    //调用回调对象的方法, 来回传数据
                    if (listener != null) {
                        listener.onCheckedChanged(isOpen);
                    }
                }
            }
        });
        //this.setOnTouchListener();
    }
    private int startX;
    private int moveX;//记录移动总距离
    private boolean isClick;//标记当前是否是点击事件
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //1.记录起点坐标
                startX = (int) event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                //2. 记录移动后坐标
                int endX = (int) event.getX();
                //3. 计算偏移量
                int dx = endX - startX;
                moveX += Math.abs(dx);
                //4. 根据偏移量更新控件位置
                mSlideLeft += dx;
                //避免越界
                if (mSlideLeft < 0) {
                    mSlideLeft = 0;
                }
                if (mSlideLeft > MAX_LEFT) {
                    mSlideLeft = MAX_LEFT;
                }
                invalidate();
                //5. 重新初始化起点坐标
                startX = endX;
                break;
            case MotionEvent.ACTION_UP:
                //根据移动总距离确定是否是点击事件
                if (moveX > 5) {//允许5个像素的误差
                    //移动
                    isClick = false;
                } else {
                    //点击
                    isClick = true;
                }
                moveX = 0;//移动总距离一定要归0;
                if (!isClick) {
                    //确定最终开关状态
                    if (mSlideLeft < MAX_LEFT / 2) {
                        mSlideLeft = 0;
                        isOpen = false;
                    } else {
                        mSlideLeft = MAX_LEFT;
                        isOpen = true;
                    }

                    invalidate();
                    //调用回调对象的方法, 来回传数据
                    if (listener != null) {
                        listener.onCheckedChanged(isOpen);
                    }
                }
                break;
            default:
                break;
        }
        //return true;//消费了此事件,导致onClick无法响应  ACTION_DOWN + ACTION_MOVE..... + ACTION_UP
        //return false;//不消费事件,只会走ACTION_DOWN, 后续所有事件不都不处理了
        return super.onTouchEvent(event);//由于onClick的回调是在super.onTouchEvent完成的,
        // 所以必须返回父类的onTouchEvent才能保证onClick也能够响应事件
    }
    //重写此方法可以修改控件尺寸
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //setMeasuredDimension(100, 100);
        setMeasuredDimension(mBitmapBg.getWidth(), mBitmapBg.getHeight());
        System.out.println("onMeasure....");
    }
    //设置控件位置
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        System.out.println("onLayout....");
    }
    //绘制控件
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //canvas.drawRect(0, 0, 200, 200, paint);
        System.out.println("onDraw......");
        //在左上角绘制图片
        canvas.drawBitmap(mBitmapBg, 0, 0, null);
        canvas.drawBitmap(mBitmapSlide, mSlideLeft, 0, null);
    }
    private OnCheckedChangeListener listener;
    public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
        this.listener = listener;
    }
    //定义回调接口
    public interface OnCheckedChangeListener {
        void onCheckedChanged(boolean isChecked);
    }
//    public void smsBackup(OnCheckedChangeListener callback) {
//
//        callback.onCheckedChanged(true);
//    }
}

 

package com.loaderman.switch;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
    private MySwitch mSwitch;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSwitch = (MySwitch) findViewById(R.id.my_switch);
        mSwitch.setOnCheckedChangeListener(new MySwitch.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(boolean isChecked) {
                Toast.makeText(MainActivity.this, "开关状态:" + isChecked, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.loaderman.switch.MainActivity">
    <com.loaderman.switch.MySwitch
        android:id="@+id/my_switch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        app:isOpen="true"
        app:slideDrawable="@drawable/slide_button"
        />
</RelativeLayout>

 自定义属性atts_my_switch.xml

<resources>
    <declare-styleable name="MySwitch">
        <!--开关状态-->
        <attr name="isOpen" format="boolean"/>
        <!--滑块图片-->
        <attr name="slideDrawable" format="reference"/>
    </declare-styleable>
</resources>

 效果图:

posted on 2017-03-07 14:11  LoaderMan  阅读(173)  评论(0编辑  收藏  举报

导航