自定义View实现SwichButton效果

这里写图片描述


老习惯先看效果,这种效果其实用自定义的CheckBox也是很好实现的。但是这样实现可以用手指滑动实现和手指点击实现。

需要掌握的技术

  1. 自定义属性
  2. 触摸事件的处理
  3. 回调的使用

实现步骤

  1. 自定义属性
    怎么自定义属性我就不详细介绍了网上一大堆,这里需要现在资源文件夹下的values创建attrs.xml文件,主要定义了默认是否是选中和滑动的图片是什么。
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SwitchView">
        <attr name="check" format="boolean" />
        <attr name="beforeBg" format="reference"/>
    </declare-styleable>
</resources>
  1. 编写SwitchView控件继承View

    2.1 获取自定义属性

 public SwitchView(Context context) {
        this(context, null);
    }

    public SwitchView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        isCheck = attrs.getAttributeBooleanValue(R.styleable.SwitchView_check, false);
        slideButton = attrs.getAttributeResourceValue(R.styleable.SwitchView_beforeBg, R.mipmap.slide_button);
        initView();
    }

2.2 将资源文件转换为Bitmap(按钮底部的背景通过Canvas在上面画图)

  //将资源图片转换为Bitmap
        bitmapSlide = BitmapFactory.decodeResource(getResources(), slideButton);
        bgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_background);

2.3 计算滑块离左右两边的距离

  //计算离左边的最大距离
        MAX_LEFT = bgBitmap.getWidth() - bitmapSlide.getWidth();
        if (isCheck) {
            mSlideLeft = MAX_LEFT;
        } else {
            mSlideLeft = 0;
        }

2.4 将背景图和滑块通过画布画出来

   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(bgBitmap, 0, 0, null);  //绘制底部背景
        canvas.drawBitmap(bitmapSlide, mSlideLeft, 0, null);  //绘制滑块
    }

2.5 监听触摸事件
这里需要做的事是根据滑动的x轴的坐标来改变滑块的位置,但是需要注意的是当滑动的x轴的坐标小于0或者滑动的坐标已经大于最大能一定的值就是MAX_LEFT需要自己设置滑块的位置为0或者MAX_LEFT。这里主要监听的是MotionEvent.ACTION_MOVE方法

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //手指按下时获取x的起点坐标
                startX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                //移动时获取手指移动x的最后坐标
                endX = event.getX();
                float dx = endX - startX;
                moveX = moveX + (int) Math.abs(dx);
                mSlideLeft = mSlideLeft + (int) dx;
                //防止超过边界
                if (mSlideLeft < 0) {
                    mSlideLeft = 0;
                } else if (mSlideLeft > MAX_LEFT) {
                    mSlideLeft = MAX_LEFT;
                }
                invalidate();
                startX = endX;
                break;
            case MotionEvent.ACTION_UP:
                Log.e("CXX", "moveX" + moveX);
                if (moveX > 5) {
                    isClick = false;
                } else {
                    isClick = true;
                }
                moveX = 0;
                if (!isClick) {
                    if (mSlideLeft < MAX_LEFT / 2) {
                        mSlideLeft = 0;
                        isCheck = false;
                    } else {
                        mSlideLeft = MAX_LEFT;
                        isCheck = true;
                    }
                    if (onCheckListener != null) {
                        onCheckListener.onCheck(SwitchView.this, isCheck);
                    }
                }

                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

代码解释下isClick的意识这个是和下面即将写的onClick事件关联在一起的因为我们的代码是可以滑动和点击共同存在的所以需要做个判断。如果不做判断会出现这么一个情况,就是我将滑块从左边滑动到右边不松手在滑动到左边按照道理是应该停留在左边的但是因为写了onClick事件所以还会调onClick中的事件故在这里加了个判断。
onClick中代码如下:

@Override
    public void onClick(View v) {
        if (!isClick) {
            return;
        }
        Log.e("CXX", "click" + isCheck);
        if (isCheck) {
            mSlideLeft = 0;
            isCheck = false;
        } else {
            mSlideLeft = MAX_LEFT;
            isCheck = true;
        }
        Log.e("CXX", "onCheck" + onCheckListener);
        //选中的回调
        if (onCheckListener != null) {
            onCheckListener.onCheck(SwitchView.this, isCheck);
        }
        invalidate();
    }

2.6 回调事件
定义一个接口

 public void setOnCheckListener(OnCheckListener onCheckListener) {
        this.onCheckListener = onCheckListener;
    }

    public interface OnCheckListener {
        public void onCheck(View view, boolean isCheck);
    }

最后是整个SwitchView的源码:

package com.media;

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

/**
 * Created by CXX on 2016/4/4.
 */
public class SwitchView extends View implements View.OnClickListener {

    private boolean isCheck;  //当前滑动开关状态
    private int slideButton;  //滑动图片资源文件
    private Bitmap bitmapSlide; //滑动图片Bitmap
    private Bitmap bgBitmap;  //背景图片资源文件
    private int MAX_LEFT;     //滑动最大左边距
    private int mSlideLeft;    //当前滑块左边距
    private float startX;
    private float endX;
    private int moveX;
    private boolean isClick = false;  //是否可以点击

    public SwitchView(Context context) {
        this(context, null);
    }

    public SwitchView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        isCheck = attrs.getAttributeBooleanValue(R.styleable.SwitchView_check, false);
        slideButton = attrs.getAttributeResourceValue(R.styleable.SwitchView_beforeBg, R.mipmap.slide_button);
        initView();
    }

    private void initView() {
        Log.e("CXX", "isCheck" + isCheck);
        //将资源图片转换为Bitmap
        bitmapSlide = BitmapFactory.decodeResource(getResources(), slideButton);
        bgBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.switch_background);
        //计算离左边的最大距离
        MAX_LEFT = bgBitmap.getWidth() - bitmapSlide.getWidth();
        if (isCheck) {
            mSlideLeft = MAX_LEFT;
        } else {
            mSlideLeft = 0;
        }
        //刷新界面
        invalidate();
        setOnClickListener(this);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //手指按下时获取x的起点坐标
                startX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                //移动时获取手指移动x的最后坐标
                endX = event.getX();
                float dx = endX - startX;
                moveX = moveX + (int) Math.abs(dx);
                mSlideLeft = mSlideLeft + (int) dx;
                //防止超过边界
                if (mSlideLeft < 0) {
                    mSlideLeft = 0;
                } else if (mSlideLeft > MAX_LEFT) {
                    mSlideLeft = MAX_LEFT;
                }
                invalidate();
                startX = endX;
                break;
            case MotionEvent.ACTION_UP:
                Log.e("CXX", "moveX" + moveX);
                if (moveX > 5) {
                    isClick = false;
                } else {
                    isClick = true;
                }
                moveX = 0;
                if (!isClick) {
                    if (mSlideLeft < MAX_LEFT / 2) {
                        mSlideLeft = 0;
                        isCheck = false;
                    } else {
                        mSlideLeft = MAX_LEFT;
                        isCheck = true;
                    }
                    if (onCheckListener != null) {
                        onCheckListener.onCheck(SwitchView.this, isCheck);
                    }
                }

                invalidate();
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(bgBitmap.getWidth(), bgBitmap.getHeight());
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(bgBitmap, 0, 0, null);  //绘制底部背景
        canvas.drawBitmap(bitmapSlide, mSlideLeft, 0, null);  //绘制滑块
    }

    @Override
    public void onClick(View v) {
        if (!isClick) {
            return;
        }
        Log.e("CXX", "click" + isCheck);
        if (isCheck) {
            mSlideLeft = 0;
            isCheck = false;
        } else {
            mSlideLeft = MAX_LEFT;
            isCheck = true;
        }
        Log.e("CXX", "onCheck" + onCheckListener);
        //选中的回调
        if (onCheckListener != null) {
            onCheckListener.onCheck(SwitchView.this, isCheck);
        }
        invalidate();
    }

    private OnCheckListener onCheckListener;

    public void setOnCheckListener(OnCheckListener onCheckListener) {
        this.onCheckListener = onCheckListener;
    }

    public interface OnCheckListener {
        public void onCheck(View view, boolean isCheck);
    }
}

源码地址

源码地址

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值