自定义有动画的Switch控件

自定义有动画的Switch控件

效果图

需求分析

我们需要一个类型Android Switch的功能,但是系统带有的有点丑,我们自己自定义一个带有动画的Switch,完全使用自定义View实现,他是可以根据手势进行move,以及过半时候的颜色装换

实现分析

首先,我们需要线条和圆的未打开和已经打开的状态颜色,圆的半径,线条的宽度,已经是否是打开状态这几个属性,定义如下

<declare-styleable name="TransitionSwitchViewStyle">
        <attr name="transitionCircleEnableColor" format="color|reference"/>
        <attr name="transitionCircleDisColor" format="color|reference"/>
        <attr name="transitionLineEnableColor" format="color|reference"/>
        <attr name="transitionLineDisColor" format="color|reference"/>
        <attr name="transitionCircleRadius" format="dimension|reference"/>
        <attr name="transitionLineWidth" format="dimension|reference"/>
        <attr name="transitionIsChecked" format="boolean"/>
    </declare-styleable>
代码实现思路

我们需要重写onTouchEvent,同时需要返回true,然后move和up事件得以响应。
对于线条而言,他的起始点事固定的,宽度自己定义,改变的知识颜色,所以我们的一些东西不要在onMeasure中去初始化,不然重绘的时候会执行多次,影响效率。
对于圆而言,他的y坐标也是固定的 改变的是x位置和颜色,所以,我们需要在touch事件同不断改变x,同时重绘view,需要注意的是,同时我们需要判断当前touch的位置,是否过半了,假如是过半了我们就要改变线条和圆的颜色。

核心代码具体实现,

首先是继承View
初始化方法,获取属性值和初始化画笔

 private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TransitionSwitchViewStyle, defStyleAttr, 0);
        enableCircleColor = array.getColor(R.styleable.TransitionSwitchViewStyle_transitionCircleEnableColor, DEFAULT_ENABLE_COLOR);
        disCircleColor = array.getColor(R.styleable.TransitionSwitchViewStyle_transitionCircleDisColor, DEFAULT_DIS_COLOR);
        enableLineColor = array.getColor(R.styleable.TransitionSwitchViewStyle_transitionLineEnableColor, DEFAULT_ENABLE_COLOR);
        disLineColor = array.getColor(R.styleable.TransitionSwitchViewStyle_transitionLineDisColor, DEFAULT_DIS_COLOR);
        isChecked = array.getBoolean(R.styleable.TransitionSwitchViewStyle_transitionIsChecked, false);
        radius = array.getDimension(R.styleable.TransitionSwitchViewStyle_transitionCircleRadius, 10);
        lineWidth = array.getDimension(R.styleable.TransitionSwitchViewStyle_transitionLineWidth, 10);
        array.recycle();
        mPaint = new Paint();
        setBackgroundColor(Color.TRANSPARENT);
    }

onMeasure方法,因为他在重绘阶段是不会再次调用的,我们需要在里面去初始化一些定了就不会变的东西,而onDraw方法是每次都会被调用,所以不适合。

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
        circleX = isChecked ? (int) (getWidth() - radius) : (int) radius;
        circleY = getHeight() / 2;
        lineLeft = 0;
        lineBottom = lineTop = getMeasuredHeight() / 2;
        lineRight = (int) (getMeasuredWidth() - radius);
        width = getMeasuredWidth();
    }

onDraw方法:

 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawLine(canvas, isChecked);
        drawCircle(canvas, isChecked);
    }

    /**
     *  画线
     * @param canvas
     * @param isChecked
     */
    private void drawLine(Canvas canvas, boolean isChecked) {
        @ColorInt int currentColor = isChecked ? enableLineColor : disLineColor;
        mPaint.setColor(currentColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(lineWidth);
        canvas.drawLine(lineLeft, lineTop, lineRight, lineBottom, mPaint);
    }

    /**
     *  画圆
     * @param canvas
     * @param isChecked
     */
    private void drawCircle(Canvas canvas, boolean isChecked) {
        @ColorInt int currentColor = isChecked ? enableCircleColor : disCircleColor;
        mPaint.setColor(currentColor);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(circleX, circleY, radius, mPaint);
    }

最重要的onTouch方法如下:

 @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                updateCircleX(event.getX(), false);
                break;
            case MotionEvent.ACTION_MOVE:
                updateCircleX(event.getX(), false);
                break;
            case MotionEvent.ACTION_UP:
                // 结束事件
                updateCircleX(event.getX(), true);
                break;
        }
        return true;
    }

    /** 更新x坐标
     * @param x
     * @param isActionUp
     */
    private void updateCircleX(float x, boolean isActionUp) {
        if (isActionUp) {
            isChecked = hasHalfWidth(x);
            circleX = (int) (hasHalfWidth(x)?(width - radius):radius);
            if (changeListener != null) {
                changeListener.onCheckChange(this,isChecked);
            }
        } else if (x <= radius) {    // 保证不会画出边界
            circleX = (int) radius;
            isChecked = false;
        } else if (x >= getMeasuredWidth() - radius) {
            circleX = (int) (getMeasuredWidth() - radius);
            isChecked = true;
        } else {
            // 判断x是否过半
            isChecked = hasHalfWidth(x);
            circleX = (int) x;
        }
        update();
    }

    /**
     * 重绘
     */
    public void update() {
        if (LiWeiJieUtil.isUIThread()) {
            invalidate();
        } else {
            postInvalidate();
        }
    }

    private boolean hasHalfWidth(float x) {
        return x > (width / 2);
    }

这样子我们就完成了自定义Switch空间的完整流程。

测试代码
 <com.liweijie.view.silmpleswitchbtn.TransitionSwitchView
        android:id="@+id/transition"
        android:layout_width="80dp"
        android:layout_height="30dp"
        app:transitionCircleDisColor="#333333"
        app:transitionCircleEnableColor="#ff0000"
        app:transitionLineWidth="5dp"
        app:transitionIsChecked="true"
        app:transitionCircleRadius="10dp"
        app:transitionLineDisColor="#333333"
        app:transitionLineEnableColor="#ff0000" />

源码下载

求星星,求star。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值