Android自定义控件之实现滑动选择开关

前言:今天我们仿照着Google给我们提供的Switch控件来进行一次模仿,自己动手打造一个可以换滑动图片以及背景的图片。

-----------------分割线---------------

先看一下google提供的Switc控件:


其实用法很简单就当普通的控件使用即可!

    <Switch
        android:id="@+id/switch1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Switch" />

-----------------分割线---------------

先来看下我们自定义的效果:


-----------------分割线---------------

其实自定义控件的套路都差不多,都需要onMeasure -> onLayout -> onDraw,然后在里面写一个接口回调,把状态传递给监听者。如何配置自定义属性请看我的另外一篇博客《Android自定义组合控件之实现CheckBox变化》在这里重复的代码就不啰嗦了,后面给出完整代码的下载链接!

注意点:

1.我们可以依据背景图片来确定控件大小。

2.绘制滑块的时候一定要判断滑块滑动的范围。

3.在onTouch里面调用invalidate()可引起onDraw重绘,页面会更新。

4.在onTouch里面返回true,表示消费了用户的点击事件,可收到其他的事件。

其他的可以想看代码里面的注解。

-----------------分割线---------------

代码撸去:

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

/**
 * 自定义滑动选择开关
 *
 */
public class SlideSwitchView extends View {

    private Bitmap switchBackgroupBitmap; // 背景图片
    private Bitmap slideButtonBitmap; // 滑块图片
    private Paint paint; // 画笔
    private boolean mSwitchState = false; // 开关状态, 默认false
    private float currentX;

    /**
     * 用于代码创建控件
     *
     * @param context
     */
    public SlideSwitchView(Context context) {
        super(context);
        init();
    }

    /**
     * 用于在xml里使用, 可指定自定义属性
     *
     * @param context
     * @param attrs
     */
    public SlideSwitchView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        // 获取配置的自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlideSwitchView);
//        int switchBackgroundResource = ta.getResourceId(R.styleable.SlideSwitchView_switch_background, R.drawable.switch_background);//可以设置默认图片
        int switchBackgroundResource = ta.getResourceId(R.styleable.SlideSwitchView_switch_background, -1);
        int slideButtonResource = ta.getResourceId(R.styleable.SlideSwitchView_slide_button, -1);
        mSwitchState = ta.getBoolean(R.styleable.SlideSwitchView_switch_state, false);
        setSwitchBackgroundResource(switchBackgroundResource);
        setSlideButtonResource(slideButtonResource);
    }

    /**
     * 用于在xml里使用, 可指定自定义属性, 如果指定了样式, 则走此构造函数
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public SlideSwitchView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        paint = new Paint();
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 依据背景图片来确定控件大小
        setMeasuredDimension(switchBackgroupBitmap.getWidth(), switchBackgroupBitmap.getHeight());
    }

    // Canvas 画布, 画板. 在上边绘制的内容都会显示到界面上.
    @Override
    protected void onDraw(Canvas canvas) {
        // 1. 绘制背景
        canvas.drawBitmap(switchBackgroupBitmap, 0, 0, paint);
        // 2. 绘制滑块
        if (isTouchMode) {
            // 根据当前用户触摸到的位置画滑块
            // 让滑块向左移动自身一半大小的位置
            float newLeft = currentX - slideButtonBitmap.getWidth() / 2.0f;
            int maxLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth();

            // 限定滑块范围
            if (newLeft < 0) {
                newLeft = 0; // 左边范围
            } else if (newLeft > maxLeft) {
                newLeft = maxLeft; // 右边范围
            }
            canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
        } else {
            // 根据开关状态boolean, 直接设置图片位置
            if (mSwitchState) {// 开
                int newLeft = switchBackgroupBitmap.getWidth() - slideButtonBitmap.getWidth();
                canvas.drawBitmap(slideButtonBitmap, newLeft, 0, paint);
            } else {// 关
                canvas.drawBitmap(slideButtonBitmap, 0, 0, paint);
            }
        }

    }

    boolean isTouchMode = false;
    private OnSwitchStateUpdateListener onSwitchStateUpdateListener;

    // 重写触摸事件, 响应用户的触摸.
    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isTouchMode = true;
                currentX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                currentX = event.getX();
                break;
            case MotionEvent.ACTION_UP:
                isTouchMode = false;
                currentX = event.getX();

                float center = switchBackgroupBitmap.getWidth() / 2.0f;

                // 根据当前按下的位置, 和控件中心的位置进行比较.
                boolean state = currentX > center;

                // 如果开关状态变化了, 通知界面. 里边开关状态更新了.
                if (state != mSwitchState && onSwitchStateUpdateListener != null) {
                    // 把最新的boolean, 状态传出去了
                    onSwitchStateUpdateListener.onStateUpdate(state);
                }

                mSwitchState = state;
                break;
        }

        // 重绘界面
        invalidate(); // 会引发onDraw()被调用, 里边的变量会重新生效.界面会更新

        return true; // 消费了用户的触摸事件, 才可以收到其他的事件.
    }

    /**
     * 设置背景图
     *
     * @param switchBackground
     */
    public void setSwitchBackgroundResource(int switchBackground) {
        if (switchBackground > 0) {
            switchBackgroupBitmap = BitmapFactory.decodeResource(getResources(), switchBackground);
        } else {
            Log.e("---SlideSwitchView--->", "没有设置开关背景图,应设置app:switch_background=\"@drawable/xxx\"");
        }
    }

    /**
     * 设置滑块图片资源
     *
     * @param slideButton
     */
    public void setSlideButtonResource(int slideButton) {
        if (slideButton > 0) {
            slideButtonBitmap = BitmapFactory.decodeResource(getResources(), slideButton);
        } else {
            Log.e("---SlideSwitchView--->", "没有设置滑动按钮图,应设置app:slide_button=\"@drawable/xxx\"");
        }
    }

    /**
     * 设置开关状态
     */
    public void setSwitchState(boolean mSwitchState) {
        this.mSwitchState = mSwitchState;
    }

    public interface OnSwitchStateUpdateListener {
        // 状态回调, 把当前状态传出去
        void onStateUpdate(boolean state);
    }

    public void setOnSwitchStateUpdateListener(OnSwitchStateUpdateListener onSwitchStateUpdateListener) {
        this.onSwitchStateUpdateListener = onSwitchStateUpdateListener;
    }

}
-----------------完整代码下载以及图片资源---------------

下载:Android自定义控件之实现滑动选择开关代码

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等待着冬天的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值