自定义switchview

来源:互联网 发布:linux是哪个国家的 编辑:程序博客网 时间:2024/06/05 18:07
public class SwitchView extends View {    private final int DEFAULT_COLOR_PRIMARY = 0xFF4BD763;//switch 默认值    private final int DEFAULT_COLOR_PRIMARY_DARK = 0xFF3AC652;    private final float RATIO_ASPECT = 0.68f;    private final float ANIMATION_SPEED = 0.1f; // (0,1]    private static final int STATE_SWITCH_ON = 4; // you change value you die    private static final int STATE_SWITCH_ON2 = 3;    private static final int STATE_SWITCH_OFF2 = 2;    private static final int STATE_SWITCH_OFF = 1;    private final AccelerateInterpolator interpolator = new AccelerateInterpolator(2);    private final Paint paint = new Paint();    private final Path sPath = new Path();    private final Path bPath = new Path();    private final RectF bRectF = new RectF();    private float sAnim, bAnim;    private RadialGradient shadowGradient;    private int state;    private int lastState;    private boolean isCanVisibleDrawing = false;    private OnClickListener mOnClickListener;    private int colorPrimary;    private int colorPrimaryDark;    private boolean hasShadow;    private boolean isOpened;    private int mWidth, mHeight;    private int actuallyDrawingAreaLeft;    private int actuallyDrawingAreaRight;    private int actuallyDrawingAreaTop;    private int actuallyDrawingAreaBottom;    private float sWidth, sHeight;    private float sLeft, sTop, sRight, sBottom;    private float sCenterX, sCenterY;    private float sScale;    private float bOffset;    private float bRadius, bStrokeWidth;    private float bWidth;    private float bLeft, bTop, bRight, bBottom;    private float bOnLeftX, bOn2LeftX, bOff2LeftX, bOffLeftX;    private float shadowReservedHeight;    public SwitchView(Context context) {        this(context, null);    }    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    public SwitchView(Context context, AttributeSet attrs) {        super(context, attrs);        setLayerType(LAYER_TYPE_SOFTWARE, null);        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwitchView);        colorPrimary = a.getColor(R.styleable.SwitchView_primaryColor, DEFAULT_COLOR_PRIMARY);        colorPrimaryDark = a.getColor(R.styleable.SwitchView_primaryColorDark, DEFAULT_COLOR_PRIMARY_DARK);        hasShadow = a.getBoolean(R.styleable.SwitchView_hasShadow, true);        isOpened = a.getBoolean(R.styleable.SwitchView_isOpened, false);        state = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;        lastState = state;        a.recycle();        if (colorPrimary == DEFAULT_COLOR_PRIMARY && colorPrimaryDark == DEFAULT_COLOR_PRIMARY_DARK) {            try {                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {                    TypedValue primaryColorTypedValue = new TypedValue();                    context.getTheme().resolveAttribute(android.R.attr.colorPrimary, primaryColorTypedValue, true);                    if (primaryColorTypedValue.data > 0) colorPrimary = primaryColorTypedValue.data;                    TypedValue primaryColorDarkTypedValue = new TypedValue();                    context.getTheme().resolveAttribute(android.R.attr.colorPrimaryDark, primaryColorDarkTypedValue, true);                    if (primaryColorDarkTypedValue.data > 0)                        colorPrimaryDark = primaryColorDarkTypedValue.data;                }            } catch (Exception e) {                e.printStackTrace();            }        }    }    /**     * TODO 设置新的颜色     * @param newColorPrimary     * @param newColorPrimaryDark     */    public void setColor(int newColorPrimary, int newColorPrimaryDark) {        colorPrimary = newColorPrimary;        colorPrimaryDark = newColorPrimaryDark;        invalidate();    }    /**     * TODO 设置是否有阴影     * @param shadow     */    public void setShadow(boolean shadow) {        hasShadow = shadow;        invalidate();    }    public boolean isOpened() {        return isOpened;    }    /**     * TODO 设置是否打开     * @param isOpened     */    public void setOpened(boolean isOpened) {        int wishState = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;        if (wishState == state) {            return;        }        refreshState(wishState);    }    public void toggleSwitch(boolean isOpened) {        int wishState = isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;        if (wishState == state) {            return;        }        if ((wishState == STATE_SWITCH_ON && (state == STATE_SWITCH_OFF || state == STATE_SWITCH_OFF2))                || (wishState == STATE_SWITCH_OFF && (state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2))) {            sAnim = 1;        }        bAnim = 1;        refreshState(wishState);    }    private void refreshState(int newState) {        if (!isOpened && newState == STATE_SWITCH_ON) {            isOpened = true;        } else if (isOpened && newState == STATE_SWITCH_OFF) {            isOpened = false;        }        lastState = state;        state = newState;        postInvalidate();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int resultWidth;        if (widthMode == MeasureSpec.EXACTLY) {            resultWidth = widthSize;        } else {            resultWidth = (int) (56 * getResources().getDisplayMetrics().density + 0.5f)                    + getPaddingLeft() + getPaddingRight();            if (widthMode == MeasureSpec.AT_MOST) {                resultWidth = Math.min(resultWidth, widthSize);            }        }        int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        int resultHeight;        if (heightMode == MeasureSpec.EXACTLY) {            resultHeight = heightSize;        } else {            int selfExpectedResultHeight = (int) (resultWidth * RATIO_ASPECT) + getPaddingTop() + getPaddingBottom();            resultHeight = selfExpectedResultHeight;            if (heightMode == MeasureSpec.AT_MOST) {                resultHeight = Math.min(resultHeight, heightSize);            }        }        setMeasuredDimension(resultWidth, resultHeight);    }    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        mWidth = w;        mHeight = h;        isCanVisibleDrawing = mWidth > getPaddingLeft() + getPaddingRight() && mHeight > getPaddingTop() + getPaddingBottom();        if (isCanVisibleDrawing) {            int actuallyDrawingAreaWidth = mWidth - getPaddingLeft() - getPaddingRight();            int actuallyDrawingAreaHeight = mHeight - getPaddingTop() - getPaddingBottom();            if (actuallyDrawingAreaWidth * RATIO_ASPECT < actuallyDrawingAreaHeight) {                actuallyDrawingAreaLeft = getPaddingLeft();                actuallyDrawingAreaRight = mWidth - getPaddingRight();                int heightExtraSize = (int) (actuallyDrawingAreaHeight - actuallyDrawingAreaWidth * RATIO_ASPECT);                actuallyDrawingAreaTop = getPaddingTop() + heightExtraSize / 2;                actuallyDrawingAreaBottom = getHeight() - getPaddingBottom() - heightExtraSize / 2;            } else {                int widthExtraSize = (int) (actuallyDrawingAreaWidth - actuallyDrawingAreaHeight / RATIO_ASPECT);                actuallyDrawingAreaLeft = getPaddingLeft() + widthExtraSize / 2;                actuallyDrawingAreaRight = getWidth() - getPaddingRight() - widthExtraSize / 2;                actuallyDrawingAreaTop = getPaddingTop();                actuallyDrawingAreaBottom = getHeight() - getPaddingBottom();            }            shadowReservedHeight = (int) ((actuallyDrawingAreaBottom - actuallyDrawingAreaTop) * 0.09f);            sLeft = actuallyDrawingAreaLeft;            sTop = actuallyDrawingAreaTop + shadowReservedHeight;            sRight = actuallyDrawingAreaRight;            sBottom = actuallyDrawingAreaBottom - shadowReservedHeight;            sWidth = sRight - sLeft;            sHeight = sBottom - sTop;            sCenterX = (sRight + sLeft) / 2;            sCenterY = (sBottom + sTop) / 2;            bLeft = sLeft;            bTop = sTop;            bBottom = sBottom;            bWidth = sBottom - sTop;            bRight = sLeft + bWidth;            final float halfHeightOfS = bWidth / 2; // OfB            bRadius = halfHeightOfS * 0.95f;            bOffset = bRadius * 0.2f; // offset of switching            bStrokeWidth = (halfHeightOfS - bRadius) * 2;            bOnLeftX = sRight - bWidth;            bOn2LeftX = bOnLeftX - bOffset;            bOffLeftX = sLeft;            bOff2LeftX = bOffLeftX + bOffset;            sScale = 1 - bStrokeWidth / sHeight;            sPath.reset();            RectF sRectF = new RectF();            sRectF.top = sTop;            sRectF.bottom = sBottom;            sRectF.left = sLeft;            sRectF.right = sLeft + sHeight;            sPath.arcTo(sRectF, 90, 180);            sRectF.left = sRight - sHeight;            sRectF.right = sRight;            sPath.arcTo(sRectF, 270, 180);            sPath.close();            bRectF.left = bLeft;            bRectF.right = bRight;            bRectF.top = bTop + bStrokeWidth / 2;            bRectF.bottom = bBottom - bStrokeWidth / 2;            float bCenterX = (bRight + bLeft) / 2;            float bCenterY = (bBottom + bTop) / 2;            shadowGradient = new RadialGradient(bCenterX, bCenterY, bRadius, 0xff000000, 0x00000000, Shader.TileMode.CLAMP);        }    }    private void calcBPath(float percent) {        bPath.reset();        bRectF.left = bLeft + bStrokeWidth / 2;        bRectF.right = bRight - bStrokeWidth / 2;        bPath.arcTo(bRectF, 90, 180);        bRectF.left = bLeft + percent * bOffset + bStrokeWidth / 2;        bRectF.right = bRight + percent * bOffset - bStrokeWidth / 2;        bPath.arcTo(bRectF, 270, 180);        bPath.close();    }    private float calcBTranslate(float percent) {        float result = 0;        switch (state - lastState) {            case 1:                if (state == STATE_SWITCH_OFF2) {                    result = bOffLeftX; // off -> off2                } else if (state == STATE_SWITCH_ON) {                    result = bOnLeftX - (bOnLeftX - bOn2LeftX) * percent; // on2 -> on                }                break;            case 2:                if (state == STATE_SWITCH_ON) {                    result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent; // off2 -> on                } else if (state == STATE_SWITCH_ON) {                    result = bOn2LeftX - (bOn2LeftX - bOffLeftX) * percent;  // off -> on2                }                break;            case 3:                result = bOnLeftX - (bOnLeftX - bOffLeftX) * percent; // off -> on                break;            case -1:                if (state == STATE_SWITCH_ON2) {                    result = bOn2LeftX + (bOnLeftX - bOn2LeftX) * percent; // on -> on2                } else if (state == STATE_SWITCH_OFF) {                    result = bOffLeftX;  // off2 -> off                }                break;            case -2:                if (state == STATE_SWITCH_OFF) {                    result = bOffLeftX + (bOn2LeftX - bOffLeftX) * percent;  // on2 -> off                } else if (state == STATE_SWITCH_OFF2) {                    result = bOff2LeftX + (bOnLeftX - bOff2LeftX) * percent;  // on -> off2                }                break;            case -3:                result = bOffLeftX + (bOnLeftX - bOffLeftX) * percent;  // on -> off                break;            default: // init            case 0:                if (state == STATE_SWITCH_OFF) {                    result = bOffLeftX; //  off -> off                } else if (state == STATE_SWITCH_ON) {                    result = bOnLeftX; // on -> on                }                break;        }        return result - bOffLeftX;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (!isCanVisibleDrawing) return;        paint.setAntiAlias(true);        final boolean isOn = (state == STATE_SWITCH_ON || state == STATE_SWITCH_ON2);        // Draw background        paint.setStyle(Paint.Style.FILL);        paint.setColor(isOn ? colorPrimary : 0xffE3E3E3);        canvas.drawPath(sPath, paint);        sAnim = sAnim - ANIMATION_SPEED > 0 ? sAnim - ANIMATION_SPEED : 0;        bAnim = bAnim - ANIMATION_SPEED > 0 ? bAnim - ANIMATION_SPEED : 0;        final float dsAnim = interpolator.getInterpolation(sAnim);        final float dbAnim = interpolator.getInterpolation(bAnim);        // Draw background animation        final float scale = sScale * (isOn ? dsAnim : 1 - dsAnim);        final float scaleOffset = (sRight - sCenterX - bRadius) * (isOn ? 1 - dsAnim : dsAnim);        canvas.save();        canvas.scale(scale, scale, sCenterX + scaleOffset, sCenterY);        paint.setColor(0xFFFFFFFF);        canvas.drawPath(sPath, paint);        canvas.restore();        // To prepare center bar path        canvas.save();        canvas.translate(calcBTranslate(dbAnim), shadowReservedHeight);        final boolean isState2 = (state == STATE_SWITCH_ON2 || state == STATE_SWITCH_OFF2);        calcBPath(isState2 ? 1 - dbAnim : dbAnim);        // Use center bar path to draw shadow        if (hasShadow) {            paint.setStyle(Paint.Style.FILL);            paint.setColor(0xFF333333);            paint.setShader(shadowGradient);            canvas.drawPath(bPath, paint);            paint.setShader(null);        }        canvas.translate(0, -shadowReservedHeight);        // draw bar        canvas.scale(0.98f, 0.98f, bWidth / 2, bWidth / 2);        paint.setStyle(Paint.Style.FILL);        paint.setColor(0xffffffff);        canvas.drawPath(bPath, paint);        paint.setStyle(Paint.Style.STROKE);        paint.setStrokeWidth(bStrokeWidth * 0.5f);        paint.setColor(isOn ? colorPrimaryDark : 0xFFBFBFBF);        canvas.drawPath(bPath, paint);        canvas.restore();        paint.reset();        if (sAnim > 0 || bAnim > 0) invalidate();    }    @Override    public boolean onTouchEvent(MotionEvent event) {        if ((state == STATE_SWITCH_ON || state == STATE_SWITCH_OFF) && (sAnim * bAnim == 0)) {            switch (event.getAction()) {                case MotionEvent.ACTION_DOWN:                    return true;                case MotionEvent.ACTION_UP:                    lastState = state;                    bAnim = 1;                    if (state == STATE_SWITCH_OFF) {                        refreshState(STATE_SWITCH_OFF2);                        listener.toggleToOn(this);                    } else if (state == STATE_SWITCH_ON) {                        refreshState(STATE_SWITCH_ON2);                        listener.toggleToOff(this);                    }                    if (mOnClickListener != null) {                        mOnClickListener.onClick(this);                    }                    break;            }        }        return super.onTouchEvent(event);    }    @Override    public void setOnClickListener(OnClickListener l) {        super.setOnClickListener(l);        mOnClickListener = l;    }    public interface OnStateChangedListener {        void toggleToOn(SwitchView view);        void toggleToOff(SwitchView view);    }    private OnStateChangedListener listener = new OnStateChangedListener() {        @Override        public void toggleToOn(SwitchView view) {            toggleSwitch(true);        }        @Override        public void toggleToOff(SwitchView view) {            toggleSwitch(false);        }    };    public void setOnStateChangedListener(OnStateChangedListener listener) {        if (listener == null) throw new IllegalArgumentException("empty listener");        this.listener = listener;    }    @Override    public Parcelable onSaveInstanceState() {        Parcelable superState = super.onSaveInstanceState();        SavedState ss = new SavedState(superState);        ss.isOpened = isOpened;        return ss;    }    @Override    public void onRestoreInstanceState(Parcelable state) {        SavedState ss = (SavedState) state;        super.onRestoreInstanceState(ss.getSuperState());        this.isOpened = ss.isOpened;        this.state = this.isOpened ? STATE_SWITCH_ON : STATE_SWITCH_OFF;        invalidate();    }    @SuppressLint("ParcelCreator")    static final class SavedState extends BaseSavedState {        private boolean isOpened;        SavedState(Parcelable superState) {            super(superState);        }        private SavedState(Parcel in) {            super(in);            isOpened = 1 == in.readInt();        }        @Override        public void writeToParcel(Parcel out, int flags) {            super.writeToParcel(out, flags);            out.writeInt(isOpened ? 1 : 0);        }    }}
在资源Res:values文件夹中简历arrts资源文件:
<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="SwitchView">        <attr name="hasShadow" format="boolean"/>        <attr name="primaryColor" format="color"/>        <attr name="primaryColorDark" format="color"/>        <attr name="isOpened" format="boolean"/>    </declare-styleable></resources>



 
原创粉丝点击