效果图:

 

需要两张图片,一张图片为背景,一张图片为滑动的点

布局去指定一个自定义View对象:

view.custom.shangguigucustomview.MyCustomSwitch
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ShangGuiguTestActivity">

    <view.custom.shangguigucustomview.MyCustomSwitch
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

</RelativeLayout>

 

自定义开关代码处理:

public class MyCustomSwitch extends View implements View.OnClickListener {

    private static final String TAG = "MyCustomSwitch";

    /**
     * 定义两张图片
     */
    private Bitmap backroundBitmap;
    private Bitmap slideBitmap;

    /**
     * 定义画笔🖌️
     */
    private Paint paint;

    /**
     * 定义移动到右边到最大值
     */
    private int reghtMax;

    /**
     * 动态变化 距离到左边到值
     */
    private int leftValue;

    /**
     * 在布局中使用本类必须要实现这个构造方法,否则直接奔溃
     * @param context
     * @param attrs
     */
    public MyCustomSwitch(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        // 引用资源的两张图片
        backroundBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_background);
        slideBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.slide_button);

        // 实例化画笔
        paint = new Paint();
        paint.setAntiAlias(true); // 去掉锯齿

        // 移动到右边最大值
        reghtMax = backroundBitmap.getWidth() - slideBitmap.getWidth();

        // 设置点击事件
        setOnClickListener(this);
    }


    /***
     * 一个视图从创建到显示到主要方法
     *
     * View:
     * 1)构造方法,实例化,并创建一个视图
     * 2)测量方法,用来测量显示到(视图大小(宽高)最终在这里确定)
     * 3)绘制方法,绘制控件到显示
     *
     * ViewGroup:
     * 1)构造方法,实例化,并创建一个视图
     * 2)测量方法,用来测量显示到(视图大小最终在这里确定)
     * 3)onLayout方法,用来指定 孩子位置相关
     * 4)
     */

    /**
     * 测量控件的本自定义视图(控件的宽高)
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(backroundBitmap.getWidth(), backroundBitmap.getHeight());
    }

    /**
     * 绘制,把两张图片绘制好显示
     */
    @Override
    protected void onDraw(Canvas canvas) {
        // super.onDraw(canvas);
        // 画背景(开/关) 距离左边0,距离顶部0
        canvas.drawBitmap(backroundBitmap, 0, 0, paint);
        // 画 用来移动的图片
        // canvas.drawBitmap(slideBitmap, 10, 0, paint);
        // canvas.drawBitmap(slideBitmap, backroundBitmap.getWidth() / 2, 0, paint);
        // canvas.drawBitmap(slideBitmap, reghtMax, 0, paint);
        // canvas.drawBitmap(slideBitmap, 0, 0 , paint);
        canvas.drawBitmap(slideBitmap, leftValue, 0, paint);
    }


    private boolean isClick = true;

    @Override
    public void onClick(View view) {

        if (isClick) {

            Log.i(TAG, "点击了》》》》》》》》》》》》》》》》");
            if (leftValue == 0) {
                leftValue = reghtMax;
            } else {
                leftValue = 0;
            }
            // 此方法让绘制方法重新执行
            invalidate();
        }
    }

    // 当前 拖动图片 距离左边到值
    private float startX;

    // 记录初始值
    private float defaultX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                defaultX = startX = event.getX();
                // 只要按下就认为是点击事件,所以设置为true
                isClick = true;
                break;
            case MotionEvent.ACTION_UP:
                if (reghtMax /2 < leftValue){
                    leftValue = reghtMax;
                }  else {
                    leftValue = 0;
                }
                invalidate();
                startX = event.getX();
                break;

            case MotionEvent.ACTION_MOVE:
                float endX = event.getX();
                float result = endX - startX;

                leftValue += result;

                // 判断越界
                if (leftValue < 0) {
                    leftValue = 0;
                } else if (leftValue > reghtMax) {
                    leftValue = reghtMax;
                }

                // 此方法让绘制方法重新执行
                invalidate();

                // 值还原
                startX = event.getX();

                // 计算移动的差值,来决定,是否是移动
                if (Math.abs(endX - defaultX) > 5) {
                    isClick = false;
                }

                break;

            default:
                break;
        }
        return true; // 先自己来消耗
    }
}