自定義View仿iOS的UiSwitch控件

自定義View仿iOS的UiSwitch控件

本文原創(chuàng)原在,轉(zhuǎn)載請(qǐng)注明出處碰声。歡迎關(guān)注我的 簡(jiǎn)書(shū)播赁。
安利一波我寫(xiě)的開(kāi)發(fā)框架:MyScFrame喜歡的話就給個(gè)Star

前言:

Android的Switch控件相信大家都用過(guò)瞧柔,其實(shí)我覺(jué)得效果還好薪夕,不過(guò)公司要求UI上的統(tǒng)一桃纯,所以讓我仿iOS效果,我就納悶了酷誓,為什么一直要仿iOS,就不能iOS仿Android么态坦?牢騷發(fā)完了盐数,可以開(kāi)工了。

附上效果圖

效果如圖伞梯,看起來(lái)還行吧

思路

繪制控件

整個(gè)控件在繪制的時(shí)候分2個(gè)部分:

  1. 底板娘扩,也就是那個(gè)橢圓形類(lèi)似跑道的部分。
  2. 按鈕壮锻。

這部分沒(méi)什么需要特別說(shuō)明的琐旁,如果有一點(diǎn)自定義View基礎(chǔ)的朋友應(yīng)該都能輕松搞定。

動(dòng)效處理

動(dòng)效我這里用的也是最基礎(chǔ)的平移動(dòng)效猜绣,同時(shí)加上底板顏色漸變效果

/**
     * 關(guān)閉開(kāi)關(guān)
     */
    public void toggleOn() {
        //手柄槽顏色漸變和手柄滑動(dòng)通過(guò)屬性動(dòng)畫(huà)來(lái)實(shí)現(xiàn)
        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "spotStartX", 0, mOffSpotX);
        animator.setDuration(300);
        animator.start();
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                calculateColor(fraction, mOffSlotColor, mOpenSlotColor);
                invalidate();
            }
        });
    }

    /**
     * 打開(kāi)開(kāi)關(guān)
     */
    public void toggleOff() {
        //手柄槽顏色漸變和手柄滑動(dòng)通過(guò)屬性動(dòng)畫(huà)來(lái)實(shí)現(xiàn)
        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "spotStartX", mOffSpotX, 0);
        animator.setDuration(300);
        animator.start();
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                calculateColor(fraction, mOpenSlotColor, mOffSlotColor);
                invalidate();
            }
        });
    }

/**
     * 計(jì)算切換時(shí)的手柄槽的顏色
     *
     * @param fraction   動(dòng)畫(huà)播放進(jìn)度
     * @param startColor 起始顏色
     * @param endColor   終止顏色
     */
    public void calculateColor(float fraction, int startColor, int endColor) {
        final int fb = Color.blue(startColor);
        final int fr = Color.red(startColor);
        final int fg = Color.green(startColor);

        final int tb = Color.blue(endColor);
        final int tr = Color.red(endColor);
        final int tg = Color.green(endColor);

        //RGB三通道線性漸變
        int sr = (int) (fr + fraction * (tr - fr));
        int sg = (int) (fg + fraction * (tg - fg));
        int sb = (int) (fb + fraction * (tb - fb));
        //范圍限定
        sb = clamp(sb, 0, 255);
        sr = clamp(sr, 0, 255);
        sg = clamp(sg, 0, 255);

        mSlotColor = Color.rgb(sr, sg, sb);
    }

Touch事件與Onclick事件

這里我是自己做了一個(gè)手勢(shì)工具類(lèi)灰殴,專(zhuān)門(mén)處理Touch中的手勢(shì),沒(méi)任何難度掰邢,只是懶得每個(gè)自定義控件里面都寫(xiě)一套重復(fù)的代碼牺陶,所以就做了個(gè)工具類(lèi),方便今后的開(kāi)發(fā)
這里也分享給大家(不要吐槽我的類(lèi)名辣之,我是百度翻譯的)

/**
 * Created by caihan on 2017/2/10.
 * 手勢(shì)判斷工具類(lèi),
 */
public class GestureUtils {
    private static final String TAG = "GestureUtils";

    private float startX = 0f;
    private float endX = 0f;
    private float startY = 0f;
    private float endY = 0f;
    private float xDistance = 0f;
    private float yDistance = 0f;

    public enum Gesture {
        PullUp, PullDown, PullLeft, PullRight
    }

    public GestureUtils() {

    }

    /**
     * 當(dāng)event.getAction() == MotionEvent.ACTION_DOWN 的時(shí)候調(diào)用
     * 設(shè)置初始X,Y坐標(biāo)
     *
     * @param event
     */
    public void actionDown(MotionEvent event) {
        xDistance = yDistance = 0f;
        setStartX(event);
        setStartY(event);
    }

    /**
     * 當(dāng)event.getAction() == MotionEvent.ACTION_MOVE 的時(shí)候調(diào)用
     * 設(shè)置移動(dòng)的X,Y坐標(biāo)
     *
     * @param event
     */
    public void actionMove(MotionEvent event) {
        setEndX(event);
        setEndY(event);
    }

    /**
     * 當(dāng)event.getAction() == MotionEvent.ACTION_UP 的時(shí)候調(diào)用
     * 設(shè)置截止的X,Y坐標(biāo)
     *
     * @param event
     */
    public void actionUp(MotionEvent event) {
        setEndX(event);
        setEndY(event);
    }

    /**
     * 手勢(shì)判斷接口
     *
     * @param gesture
     * @return
     */
    public boolean getGesture(Gesture gesture) {
        switch (gesture) {
            case PullUp:
                return isRealPullUp();
            case PullDown:
                return isRealPullDown();
            case PullLeft:
                return isRealPullLeft();
            case PullRight:
                return isRealPullRight();
            default:
                LogUtils.e(TAG, "getGesture error");
                return false;
        }
    }

    /**
     * 獲取Touch點(diǎn)相對(duì)于屏幕原點(diǎn)的X坐標(biāo)
     *
     * @param event
     * @return
     */
    private float gestureRawX(MotionEvent event) {
        return event.getRawX();
    }

    /**
     * 獲取Touch點(diǎn)相對(duì)于屏幕原點(diǎn)的Y坐標(biāo)
     *
     * @param event
     * @return
     */
    private float gestureRawY(MotionEvent event) {
        return event.getRawY();
    }

    /**
     * 獲取X軸偏移量,取絕對(duì)值
     *
     * @param startX
     * @param endX
     * @return
     */
    private float gestureDistanceX(float startX, float endX) {
        setxDistance(Math.abs(endX - startX));
        return xDistance;
    }

    /**
     * 獲取Y軸偏移量,取絕對(duì)值
     *
     * @param startY
     * @param endY
     * @return
     */
    private float gestureDistanceY(float startY, float endY) {
        setyDistance(Math.abs(endY - startY));
        return yDistance;
    }

    /**
     * endY坐標(biāo)比startY小,相減負(fù)數(shù)表示手勢(shì)上滑
     *
     * @param startY
     * @param endY
     * @return
     */
    private boolean isPullUp(float startY, float endY) {
        return (endY - startY) < 0;
    }

    /**
     * endY坐標(biāo)比startY大,相減正數(shù)表示手勢(shì)下滑
     *
     * @param startY
     * @param endY
     * @return
     */
    private boolean isPullDown(float startY, float endY) {
        return (endY - startY) > 0;
    }

    /**
     * endX坐標(biāo)比startX大,相減正數(shù)表示手勢(shì)右滑
     *
     * @param startX
     * @param endX
     * @return
     */
    private boolean isPullRight(float startX, float endX) {
        return (endX - startX) > 0;
    }

    /**
     * endX坐標(biāo)比startX小,相減負(fù)數(shù)表示手勢(shì)左滑
     *
     * @param startX
     * @param endX
     * @return
     */
    private boolean isPullLeft(float startX, float endX) {
        return (endX - startX) < 0;
    }

    /**
     * 判斷用戶真實(shí)操作是否是上滑
     *
     * @return
     */
    private boolean isRealPullUp() {
        if (gestureDistanceX(startX, endX) < gestureDistanceY(startY, endY)) {
            //Y軸偏移量大于X軸,表示用戶真實(shí)目的是上下滑動(dòng)
            return isPullUp(startY, endY);
        }
        return false;
    }

    /**
     * 判斷用戶真實(shí)操作是否是下滑
     *
     * @return
     */
    private boolean isRealPullDown() {
        if (gestureDistanceX(startX, endX) < gestureDistanceY(startY, endY)) {
            //Y軸偏移量大于X軸,表示用戶真實(shí)目的是上下滑動(dòng)
            return isPullDown(startY, endY);
        }
        return false;
    }

    /**
     * 判斷用戶真實(shí)操作是否是左滑
     *
     * @return
     */
    private boolean isRealPullLeft() {
        if (gestureDistanceX(startX, endX) > gestureDistanceY(startY, endY)) {
            //Y軸偏移量大于X軸,表示用戶真實(shí)目的是上下滑動(dòng)
            return isPullLeft(startX, endX);
        }
        return false;
    }

    /**
     * 判斷用戶真實(shí)操作是否是左滑
     *
     * @return
     */
    private boolean isRealPullRight() {
        if (gestureDistanceX(startX, endX) > gestureDistanceY(startY, endY)) {
            //Y軸偏移量大于X軸,表示用戶真實(shí)目的是上下滑動(dòng)
            return isPullRight(startX, endX);
        }
        return false;
    }


    private GestureUtils setStartX(MotionEvent event) {
        this.startX = gestureRawX(event);
        return this;
    }

    private GestureUtils setEndX(MotionEvent event) {
        this.endX = gestureRawX(event);
        return this;
    }

    private GestureUtils setStartY(MotionEvent event) {
        this.startY = gestureRawY(event);
        return this;
    }

    private GestureUtils setEndY(MotionEvent event) {
        this.endY = gestureRawY(event);
        return this;
    }

    private GestureUtils setxDistance(float xDistance) {
        this.xDistance = xDistance;
        return this;
    }

    private GestureUtils setyDistance(float yDistance) {
        this.yDistance = yDistance;
        return this;
    }

    public float getStartX() {
        return startX;
    }

    public float getEndX() {
        return endX;
    }

    public float getStartY() {
        return startY;
    }

    public float getEndY() {
        return endY;
    }

    public float getxDistance() {
        return xDistance;
    }

    public float getyDistance() {
        return yDistance;
    }
}

大家都知道掰伸,Onclick事件是要在Touch的MotionEvent.ACTION_UP事件之后才觸發(fā)的,也就是說(shuō)怀估,如果我們dispatchTouchEvent分發(fā)Touch事件的時(shí)候狮鸭,當(dāng)event.getAction() = MotionEvent.ACTION_UP時(shí)合搅,返回true歧蕉,那么Onclick就不會(huì)觸發(fā)惯退,這樣的話催跪,我們就能針對(duì)不同的事件做不同的處理懊蒸,我這邊就是這樣設(shè)計(jì)的

    private boolean mIsToggleOn = false;//當(dāng)前開(kāi)關(guān)標(biāo)記
    private boolean isTouchEvent = false;//是否由滑動(dòng)事件消費(fèi)掉
    private boolean isMoveing = false;//是否還在Touch相應(yīng)中

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mGestureUtils.actionDown(event);
                isTouchEvent = false;
                isMoveing = false;
                break;
            case MotionEvent.ACTION_MOVE:
                mGestureUtils.actionMove(event);
                if (mGestureUtils.getGesture(GestureUtils.Gesture.PullLeft)) {
                    //左滑,關(guān)閉
                    isTouchEvent = true;
                    touchToggle(false);
                    return true;
                } else if (mGestureUtils.getGesture(GestureUtils.Gesture.PullRight)) {
                    //右滑,開(kāi)啟
                    isTouchEvent = true;
                    touchToggle(true);
                    return true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            case MotionEvent.ACTION_UP:
                isMoveing = false;
                if (isTouchEvent) {
                    //不會(huì)觸發(fā)Onclick事件了
                    return true;
                }
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * Touch事件觸發(fā)
     * mIsToggleOn是當(dāng)前狀態(tài),當(dāng)mIsToggleOn != open時(shí)做出相應(yīng)
     *
     * @param open 是否打開(kāi)
     */
    private void touchToggle(boolean open) {
        if (!isMoveing) {
            isMoveing = true;
            if (mIsToggleOn != open) {
                if (mIsToggleOn) {
                    toggleOff();
                } else {
                    toggleOn();
                }
                mIsToggleOn = !mIsToggleOn;
                if (mOnToggleListener != null) {
                    mOnToggleListener.onSwitchChangeListener(mIsToggleOn);
                }
            }
        }
    }

    /**
     * Onclick事件觸發(fā)
     */
    private void onClickToggle() {
        if (mIsToggleOn) {
            toggleOff();
        } else {
            toggleOn();
        }
        mIsToggleOn = !mIsToggleOn;
        if (mOnToggleListener != null) {
            mOnToggleListener.onSwitchChangeListener(mIsToggleOn);
        }
    }

然后監(jiān)聽(tīng)按鈕狀態(tài)

    public interface OnToggleListener {
        void onSwitchChangeListener(boolean switchState);
    }

    public void setOnToggleListener(OnToggleListener listener) {
        mOnToggleListener = listener;
    }

完了完了,就這么簡(jiǎn)單...什么?要完整代碼?好吧

/**
 * Created by caihan on 2017/2/10.
 * 仿iOS的UiSwitch控件
 */
public class IosSwitch extends View implements View.OnClickListener {

    private static final String TAG = "IosSwitch";

    private final int BORDER_WIDTH = 2;//邊框?qū)挾?
    private int mBasePlaneColor = Color.parseColor("#4ebb7f");//底盤(pán)顏色,布局描邊顏色
    private int mOpenSlotColor = Color.parseColor("#4ebb7f");//開(kāi)啟時(shí)手柄滑動(dòng)槽的顏色
    private int mOffSlotColor = Color.parseColor("#EEEEEE");//關(guān)閉時(shí)手柄滑動(dòng)槽的顏色

    private int mSlotColor;

    private RectF mRect = new RectF();

    //繪制參數(shù)
    private float mBackPlaneRadius;//底板的圓形半徑
    private float mSpotRadius;//手柄半徑

    private float spotStartX;//手柄的起始X位置,切換時(shí)平移改變它
    private float mSpotY;//手柄的起始X位置抡笼,不變
    private float mOffSpotX;//關(guān)閉時(shí),手柄的水平位置

    private Paint mPaint;//畫(huà)筆

    private boolean mIsToggleOn = false;//當(dāng)前開(kāi)關(guān)標(biāo)記
    private boolean isTouchEvent = false;//是否由滑動(dòng)事件消費(fèi)掉
    private boolean isMoveing = false;//是否還在Touch相應(yīng)中

    private OnToggleListener mOnToggleListener;//toggle事件監(jiān)聽(tīng)

    private GestureUtils mGestureUtils;//手勢(shì)工具類(lèi)

    public interface OnToggleListener {
        void onSwitchChangeListener(boolean switchState);
    }

    public IosSwitch(Context context) {
        super(context);
        init(context);
    }

    public IosSwitch(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        setOnClickListener(this);
        setEnabled(true);
        mGestureUtils = new GestureUtils();
    }

    @Override
    public void onClick(View v) {
        onClickToggle();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mGestureUtils.actionDown(event);
                isTouchEvent = false;
                isMoveing = false;
                break;
            case MotionEvent.ACTION_MOVE:
                mGestureUtils.actionMove(event);
                if (mGestureUtils.getGesture(GestureUtils.Gesture.PullLeft)) {
                    //左滑,關(guān)閉
                    isTouchEvent = true;
                    touchToggle(false);
                    return true;
                } else if (mGestureUtils.getGesture(GestureUtils.Gesture.PullRight)) {
                    //右滑,開(kāi)啟
                    isTouchEvent = true;
                    touchToggle(true);
                    return true;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            case MotionEvent.ACTION_UP:
                isMoveing = false;
                if (isTouchEvent) {
                    //不會(huì)觸發(fā)Onclick事件了
                    return true;
                }
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }


    @Override
    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);
        int resultWidth = wSize;
        int resultHeight = hSize;
        Resources r = Resources.getSystem();
        //lp = wrapcontent時(shí) 指定默認(rèn)值
        if (wMode == MeasureSpec.AT_MOST) {
            resultWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, r.getDisplayMetrics());
        }
        if (hMode == MeasureSpec.AT_MOST) {
            resultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics());
        }
        setMeasuredDimension(resultWidth, resultHeight);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        mBackPlaneRadius = Math.min(getWidth(), getHeight()) * 0.5f;
        mSpotRadius = mBackPlaneRadius - BORDER_WIDTH;
        spotStartX = 0;
        mSpotY = 0;
        mOffSpotX = getMeasuredWidth() - mBackPlaneRadius * 2;
        mSlotColor = mOffSlotColor;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //畫(huà)底板
        mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
        mPaint.setColor(mBasePlaneColor);
        canvas.drawRoundRect(mRect, mBackPlaneRadius, mBackPlaneRadius, mPaint);

        //畫(huà)手柄的槽
        mRect.set(BORDER_WIDTH,
                BORDER_WIDTH,
                getMeasuredWidth() - BORDER_WIDTH,
                getMeasuredHeight() - BORDER_WIDTH);

        mPaint.setColor(mSlotColor);
        canvas.drawRoundRect(mRect, mSpotRadius, mSpotRadius, mPaint);

        //手柄包括包括兩部分,深色底板和白板,這樣做的目的是使圓盤(pán)具有邊框
        //手柄的底盤(pán)
        mRect.set(spotStartX,
                mSpotY,
                spotStartX + mBackPlaneRadius * 2,
                mSpotY + mBackPlaneRadius * 2);

        mPaint.setColor(mBasePlaneColor);
        canvas.drawRoundRect(mRect, mBackPlaneRadius, mBackPlaneRadius, mPaint);

        //手柄的圓板
        mRect.set(spotStartX + BORDER_WIDTH,
                mSpotY + BORDER_WIDTH,
                mSpotRadius * 2 + spotStartX + BORDER_WIDTH,
                mSpotRadius * 2 + mSpotY + BORDER_WIDTH);

        mPaint.setColor(Color.WHITE);
        canvas.drawRoundRect(mRect, mSpotRadius, mSpotRadius, mPaint);
    }

    public float getSpotStartX() {
        return spotStartX;
    }

    public void setSpotStartX(float spotStartX) {
        this.spotStartX = spotStartX;
    }

    /**
     * 計(jì)算切換時(shí)的手柄槽的顏色
     *
     * @param fraction   動(dòng)畫(huà)播放進(jìn)度
     * @param startColor 起始顏色
     * @param endColor   終止顏色
     */
    public void calculateColor(float fraction, int startColor, int endColor) {
        final int fb = Color.blue(startColor);
        final int fr = Color.red(startColor);
        final int fg = Color.green(startColor);

        final int tb = Color.blue(endColor);
        final int tr = Color.red(endColor);
        final int tg = Color.green(endColor);

        //RGB三通道線性漸變
        int sr = (int) (fr + fraction * (tr - fr));
        int sg = (int) (fg + fraction * (tg - fg));
        int sb = (int) (fb + fraction * (tb - fb));
        //范圍限定
        sb = clamp(sb, 0, 255);
        sr = clamp(sr, 0, 255);
        sg = clamp(sg, 0, 255);

        mSlotColor = Color.rgb(sr, sg, sb);
    }

    private int clamp(int value, int low, int high) {
        return Math.min(Math.max(value, low), high);
    }

    /**
     * 關(guān)閉開(kāi)關(guān)
     */
    public void toggleOn() {
        //手柄槽顏色漸變和手柄滑動(dòng)通過(guò)屬性動(dòng)畫(huà)來(lái)實(shí)現(xiàn)
        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "spotStartX", 0, mOffSpotX);
        animator.setDuration(300);
        animator.start();
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                calculateColor(fraction, mOffSlotColor, mOpenSlotColor);
                invalidate();
            }
        });
    }

    /**
     * 打開(kāi)開(kāi)關(guān)
     */
    public void toggleOff() {
        //手柄槽顏色漸變和手柄滑動(dòng)通過(guò)屬性動(dòng)畫(huà)來(lái)實(shí)現(xiàn)
        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "spotStartX", mOffSpotX, 0);
        animator.setDuration(300);
        animator.start();
        animator.setInterpolator(new DecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                calculateColor(fraction, mOpenSlotColor, mOffSlotColor);
                invalidate();
            }
        });
    }

    public boolean getSwitchState() {
        return mIsToggleOn;
    }

    /**
     * Touch事件觸發(fā)
     * mIsToggleOn是當(dāng)前狀態(tài),當(dāng)mIsToggleOn != open時(shí)做出相應(yīng)
     *
     * @param open 是否打開(kāi)
     */
    private void touchToggle(boolean open) {
        if (!isMoveing) {
            isMoveing = true;
            if (mIsToggleOn != open) {
                if (mIsToggleOn) {
                    toggleOff();
                } else {
                    toggleOn();
                }
                mIsToggleOn = !mIsToggleOn;
                if (mOnToggleListener != null) {
                    mOnToggleListener.onSwitchChangeListener(mIsToggleOn);
                }
            }
        }
    }

    /**
     * Onclick事件觸發(fā)
     */
    private void onClickToggle() {
        if (mIsToggleOn) {
            toggleOff();
        } else {
            toggleOn();
        }
        mIsToggleOn = !mIsToggleOn;
        if (mOnToggleListener != null) {
            mOnToggleListener.onSwitchChangeListener(mIsToggleOn);
        }
    }

    public void setOnToggleListener(OnToggleListener listener) {
        mOnToggleListener = listener;
    }

    /**
     * 界面上設(shè)置開(kāi)關(guān)初始狀態(tài)
     * @param open
     */
    public void setChecked(final boolean open) {
        this.postDelayed(new Runnable() {
            @Override
            public void run() {
                touchToggle(open);
            }
        }, 300);
    }
}

感謝

感謝kakacxicm提供的繪制思路與動(dòng)效處理
這邊再分享幾個(gè)其他的版本給大家參考:
SwitchButton
自定義控件(三步搞定switch)
Swift-自定義switch控件

搞定,收工

歡迎大家留言指出我的不足厂捞。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末靡馁,一起剝皮案震驚了整個(gè)濱河市臭墨,隨后出現(xiàn)的幾起案子尤误,更是在濱河造成了極大的恐慌,老刑警劉巖掺冠,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件德崭,死亡現(xiàn)場(chǎng)離奇詭異揖盘,居然都是意外死亡憾股,警方通過(guò)查閱死者的電腦和手機(jī)服球,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)颠焦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伐庭,你說(shuō)我怎么就攤上這事』恚” “怎么了霸株?”我有些...
    開(kāi)封第一講書(shū)人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵集乔,是天一觀的道長(zhǎng)淳衙。 經(jīng)常有香客問(wèn)我饺著,道長(zhǎng)箫攀,這世上最難降的妖魔是什么靴跛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任肥印,我火速辦了婚禮绝葡,結(jié)果婚禮上藏畅,老公的妹妹穿的比我還像新娘敷硅。我一直安慰自己,他們只是感情好愉阎,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布绞蹦。 她就那樣靜靜地躺著,像睡著了一般榜旦。 火紅的嫁衣襯著肌膚如雪幽七。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 50,050評(píng)論 1 291
  • 那天溅呢,我揣著相機(jī)與錄音澡屡,去河邊找鬼。 笑死咐旧,一個(gè)胖子當(dāng)著我的面吹牛挪蹭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播休偶,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼梁厉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了踏兜?” 一聲冷哼從身側(cè)響起词顾,我...
    開(kāi)封第一講書(shū)人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碱妆,沒(méi)想到半個(gè)月后肉盹,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疹尾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年上忍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纳本。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡窍蓝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出繁成,到底是詐尸還是另有隱情吓笙,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布巾腕,位于F島的核電站面睛,受9級(jí)特大地震影響絮蒿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叁鉴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一土涝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幌墓,春花似錦但壮、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腔长。三九已至袭祟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間捞附,已是汗流浹背巾乳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸟召,地道東北人胆绊。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像欧募,于是被迫代替她去往敵國(guó)和親压状。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,858評(píng)論 25 707
  • 從現(xiàn)在開(kāi)始,做一個(gè)簡(jiǎn)單的人舔糖, 早起跑步娱两,看朝陽(yáng)漫漫聚集成型, 感受第一縷強(qiáng)光照射身上的融融暖意金吗。 從現(xiàn)在開(kāi)始十兢,做一...
    6班三期12組組長(zhǎng)孫榮亮閱讀 500評(píng)論 6 6
  • 世界上有兩種人,一種是中心人摇庙,一種是邊緣人旱物。中心人的世界我不懂,這個(gè)物質(zhì)社會(huì)他們能混得如魚(yú)得水卫袒。邊緣人的生活...
    康文閱讀 2,023評(píng)論 0 5
  • 我總希望自己能夠把煙戒掉异袄,也因此痛下了很多次決心,可想來(lái)決心這東西的唯一用處是拿到安慰深夜睡不著的自己玛臂,而讓失眠變...
    筆名蘇式閱讀 175評(píng)論 0 0
  • 不知不覺(jué)發(fā)現(xiàn)明天又到了新的一年上班的時(shí)候了烤蜕,感覺(jué)完全沒(méi)有準(zhǔn)備好 開(kāi)始又一年的時(shí)間~暫且寫(xiě)個(gè)目標(biāo)清單這里 1.代碼篇...
    四顧sel閱讀 141評(píng)論 0 0