自定义SwitchButton控件

本文介绍了一种自定义SwitchButton的实现方法,该组件具备动画滑动切换效果及手动拖动滑块的功能。文章详细解释了如何通过触摸事件实现滑动动画,并提供了关键代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       自己之前一直很懒写博客,今天想想还是写些什么吧。于是写了个自定义的switchButton,实现了点击一下具有动画滑动的切换效果,并且可以手动拖动滑块靠近两端后自己吸附到打开或者关闭位子。这个demo只是实现了功能,还有些细节需要考虑的,请大家轻点拍砖,给我留些继续写下去的动力。

效果图:

private void init(Context context,AttributeSet attrs){
        if(attrs != null){
            TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.SwitchButton);
            onColor = ta.getColor(R.styleable.SwitchButton_onColor,DEF_ONCOLOR);
            offColor = ta.getColor(R.styleable.SwitchButton_offColor,DEF_OFFCOLOR);
            thumbColor = ta.getColor(R.styleable.SwitchButton_thumbColor,DEF_THUMBCOLOR);
            String temOnText = ta.getString(R.styleable.SwitchButton_onText);
            onText = temOnText == null ? ON_TEXT:temOnText;
            temOnText = ta.getString(R.styleable.SwitchButton_offText);
            offText = temOnText == null ? OFF_TEXT:temOnText;
            radio = ta.getDimensionPixelSize(R.styleable.SwitchButton_radio,DEF_RADIO);
            if(radio > DEF_RADIO){
                radio = DEF_RADIO;
            }
            ta.recycle();
        }

        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
        thumb_x = getPaddingLeft();
        thumb_y = getPaddingTop();

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(5);

        mTextPaint = new Paint();
        mTextPaint.setTextSize(textSize);
        mTextPaint.setColor(Color.BLACK);
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        textHOffset = - (fm.ascent + fm.descent)/2;
    }

这个init函数在构造函数里被调用,获取自定义参数开/关的时的颜色,开/关所对应文字。thumb_x为thumb开始绘画的横坐标。
textHOffset偏移量让文字正好写在空间中间。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wMode = MeasureSpec.getMode(widthMeasureSpec);
        int hMode = MeasureSpec.getMode(heightMeasureSpec);
        int wSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSize = MeasureSpec.getSize(heightMeasureSpec);

        if(wMode == MeasureSpec.AT_MOST || wSize < MIN_WIDTH){
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(MIN_WIDTH,MeasureSpec.EXACTLY);
        }
        if(hMode == MeasureSpec.AT_MOST || hSize < MIN_HEIGHT){
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MIN_HEIGHT,MeasureSpec.EXACTLY);
        }

        setMeasuredDimension(widthMeasureSpec,heightMeasureSpec);
    }

在测量时主要是要考虑wrap_content,给了一个自己设的最小宽度,长度。

public boolean onTouchEvent(MotionEvent event) {
        float currentTouchX = event.getX();


        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                if(!isTouchInThumb(event)){
                    return false;
                }
                if(lastTouchX == -1){
                    lastTouchX = currentTouchX;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if(Math.abs(lastTouchX - currentTouchX) > touchSlop){
                    thumb_x += currentTouchX - lastTouchX;
                    lastTouchX = currentTouchX;
                    if(thumb_x > getWidth()-thumb_width - getPaddingRight()){
                        thumb_x = getWidth()-thumb_width;
                    }else if(thumb_x < getPaddingLeft()){
                        thumb_x = getPaddingLeft();
                    }
                    thumb_state = STATE_DRAGGING;
                    postInvalidate();
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                lastTouchX = -1;
                if(thumb_state == STATE_DRAGGING){
                    if(thumb_x -getPaddingLeft() + thumb_width/2 < tap_thresoul*(getWidth()-getPaddingLeft() - getPaddingRight())){
                        thumb_state = STATE_TO_OFF;
                    }else{
                        thumb_state = STATE_TO_ON;
                    }
                }else if(thumb_state == STATE_ON){
                    thumb_state = STATE_TO_OFF;
                }else if(thumb_state == STATE_OFF){
                    thumb_state = STATE_TO_ON;
                }
}


重写事件函数,在down事件中判断点击点是否在滑块上,如果不是就返回false不对后续事件进行处理。在MOVE事件中不断对滑块的绘画起点进行更新,同时还进行边界条件的判断。
在up事件中,分两种情况。一种是拖动过,根据靠近哪端设置对应状态。另一种是点击一下滑块,会切换为对立的状态。

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();

        canvas.save();
        canvas.translate(0,height/2);

        mPaint.setStyle(Paint.Style.FILL);//关的部分
        mPaint.setColor(offColor);
        bgRect.set(paddingLeft+ thumb_size_ex,-(height/2 - thumb_size_ex),width-paddingRight-thumb_size_ex,height/2 - thumb_size_ex);
        canvas.drawRoundRect(bgRect,radio,radio,mPaint);

        mPaint.setColor(onColor);       //开的部分
        bgRect.set(paddingLeft + thumb_size_ex,-(height/2 - thumb_size_ex),thumb_x+thumb_width/2,height/2 - thumb_size_ex);
        canvas.drawRoundRect(bgRect,radio,radio,mPaint);

        mTextPaint.setColor(Color.BLACK);
        mTextPaint.setTextAlign(Paint.Align.RIGHT);         //关的字
        Paint.FontMetrics fm = mTextPaint.getFontMetrics();
        canvas.drawText(offText,width-paddingRight-thumb_size_ex-radio,textHOffset,mTextPaint);

        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTextAlign(Paint.Align.LEFT);          //开的字
        canvas.drawText(onText,paddingLeft + radio,textHOffset,mTextPaint);

        if(thumb_state == STATE_TO_OFF){
            thumb_x -= MOVE_STEP;
            if(thumb_x < getPaddingLeft() ){
                thumb_x = getPaddingLeft();
                thumb_state = STATE_OFF;
                changeOnOff(thumb_state);
            }
            postInvalidate();
        }else if(thumb_state == STATE_TO_ON){
            thumb_x += MOVE_STEP;
            if(thumb_x > getWidth() - getPaddingRight() - thumb_width){
                thumb_x = getWidth() - getPaddingRight() - thumb_width;
                thumb_state = STATE_ON;
                changeOnOff(thumb_state);
            }
            postInvalidate();
        }


        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(thumbColor);            //thumb
        bgRect.set(thumb_x,-height/2,thumb_x+thumb_width,height/2);
        canvas.drawRoundRect(bgRect,10,10,mPaint);
        mPaint.setColor((thumb_state == STATE_ON || thumb_state == STATE_TO_ON) ? onColor:offColor);
        canvas.drawLine(thumb_x + thumb_width/2 - 10 ,-(height/2 - thumb_size_ex),thumb_x + thumb_width/2 - 10,height/2 + thumb_size_ex,mPaint);
        canvas.drawLine(thumb_x + thumb_width/2  ,-(height/2 - thumb_size_ex),thumb_x + thumb_width/2,height/2 + thumb_size_ex,mPaint);
        canvas.drawLine(thumb_x + thumb_width/2 + 10 ,-(height/2 - thumb_size_ex),thumb_x + thumb_width/2 + 10,height/2 + thumb_size_ex,mPaint);
        canvas.restore();


    }


                postInvalidate();
                break;
            default:
                break;
        }

        return true;
    }

这里的实现方式是先画关闭的图形,再在上层画出打开的图形,再写字,最后是滑块。注意左右滑动动画就是通过里面
if(thumb_state == STATE_TO_OFF) 和 else if(thumb_state == STATE_TO_ON) 来判断,然后每次增加或减少MOVE_STEP给thumb_x,并在最后通过
postInvalidate()来请求绘制。如果已经滑动到位就不再调用postInvalidate()。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值