Android-带有切换动画的CheckBox

来源:互联网 发布:没有美工怎么做游戏 编辑:程序博客网 时间:2024/05/16 08:31

转自:https://github.com/andyxialm/SmoothCheckBox

这里写图片描述

前言

切换带动画的效果确实不错,可能以后项目会用得到,所以转发收藏。

1,自定义 SmoothCheckBox 。

public class SmoothCheckBox extends View implements Checkable {    private static final String KEY_INSTANCE_STATE = "InstanceState";    private static final int COLOR_TICK      = Color.WHITE;    private static final int COLOR_UNCHECKED = Color.WHITE;    private static final int COLOR_CHECKED   = Color.parseColor("#FB4846");    private static final int COLOR_FLOOR_UNCHECKED = Color.parseColor("#DFDFDF");    private static final int DEF_ANIM_DURATION = 300;    private Paint mPaint, mTickPaint, mFloorPaint;    private Point[] mTickPoints;    private Point mCenterPoint;    private Path mTickPath;    private float mLeftLineDistance, mRightLineDistance, mDrewDistance;    private float mScaleVal = 1.0f, mFloorScale = 1.0f;    private int mWidth, mAnimDuration, mStrokeWidth;    private int mCheckedColor, mUnCheckedColor, mFloorColor, mFloorUnCheckedColor;    private boolean mChecked;    private boolean mTickDrawing;    private OnCheckedChangeListener mListener;    public SmoothCheckBox(Context context) {        this(context, null);    }    public SmoothCheckBox(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public SmoothCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init(attrs);    }    @TargetApi(Build.VERSION_CODES.LOLLIPOP)    public SmoothCheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init(attrs);    }    private void init(AttributeSet attrs) {        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.SmoothCheckBox);        int tickColor = ta.getColor(R.styleable.SmoothCheckBox_color_tick, COLOR_TICK);        mAnimDuration = ta.getInt(R.styleable.SmoothCheckBox_duration, DEF_ANIM_DURATION);        mFloorColor = ta.getColor(R.styleable.SmoothCheckBox_color_unchecked_stroke, COLOR_FLOOR_UNCHECKED);        mCheckedColor = ta.getColor(R.styleable.SmoothCheckBox_color_checked, COLOR_CHECKED);        mUnCheckedColor = ta.getColor(R.styleable.SmoothCheckBox_color_unchecked, COLOR_UNCHECKED);        mStrokeWidth = ta.getDimensionPixelSize(R.styleable.SmoothCheckBox_stroke_width,                CompatUtils.dp2px(getContext(), 0));        ta.recycle();        mFloorUnCheckedColor = mFloorColor;        mTickPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mTickPaint.setStyle(Paint.Style.STROKE);        mTickPaint.setStrokeCap(Paint.Cap.ROUND);        mTickPaint.setColor(tickColor);        mFloorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mFloorPaint.setStyle(Paint.Style.FILL);        mFloorPaint.setColor(mFloorColor);        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mPaint.setStyle(Paint.Style.FILL);        mPaint.setColor(mCheckedColor);        mTickPath = new Path();        mCenterPoint = new Point();        mTickPoints = new Point[3];        mTickPoints[0] = new Point();        mTickPoints[1] = new Point();        mTickPoints[2] = new Point();        setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                toggle();                mTickDrawing = false;                mDrewDistance = 0;                if (isChecked()) {                    startCheckedAnimation();                } else {                    startUnCheckedAnimation();                }            }        });    }    @Override    protected Parcelable onSaveInstanceState() {        Bundle bundle = new Bundle();        bundle.putParcelable(KEY_INSTANCE_STATE, super.onSaveInstanceState());        bundle.putBoolean(KEY_INSTANCE_STATE, isChecked());        return bundle;    }    @Override    protected void onRestoreInstanceState(Parcelable state) {        if (state instanceof Bundle) {            Bundle bundle = (Bundle) state;            boolean isChecked = bundle.getBoolean(KEY_INSTANCE_STATE);            setChecked(isChecked);            super.onRestoreInstanceState(bundle.getParcelable(KEY_INSTANCE_STATE));            return;        }        super.onRestoreInstanceState(state);    }    @Override    public boolean isChecked() {        return mChecked;    }    @Override    public void toggle() {        this.setChecked(!isChecked());    }    @Override    public void setChecked(boolean checked) {        mChecked = checked;        reset();        invalidate();        if (mListener != null) {            mListener.onCheckedChanged(SmoothCheckBox.this, mChecked);        }    }    /**     * <p>checked with animation</p>     * @param checked checked     * @param animate change with animation     */    public void setChecked(boolean checked, boolean animate) {        if (animate) {            mTickDrawing = false;            mChecked = checked;            mDrewDistance = 0f;            if (checked) {                startCheckedAnimation();//选中之后的动画            } else {                startUnCheckedAnimation();//取消选中的动画            }            if (mListener != null) {                mListener.onCheckedChanged(SmoothCheckBox.this, mChecked);            }        } else {            this.setChecked(checked);        }    }    private void reset() {        mTickDrawing = true;        mFloorScale = 1.0f;        mScaleVal = isChecked() ? 0f : 1.0f;        mFloorColor = isChecked() ? mCheckedColor : mFloorUnCheckedColor;        mDrewDistance = isChecked() ? (mLeftLineDistance + mRightLineDistance) : 0;    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {        mWidth = getMeasuredWidth();        mStrokeWidth = (mStrokeWidth == 0 ? getMeasuredWidth() / 10 : mStrokeWidth);        mStrokeWidth = mStrokeWidth > getMeasuredWidth() / 5 ? getMeasuredWidth() / 5 : mStrokeWidth;        mStrokeWidth = (mStrokeWidth < 3) ? 3 : mStrokeWidth;        mCenterPoint.x = mWidth / 2;        mCenterPoint.y = getMeasuredHeight() / 2;        mTickPoints[0].x = Math.round((float) getMeasuredWidth() / 30 * 7);        mTickPoints[0].y = Math.round((float) getMeasuredHeight() / 30 * 14);        mTickPoints[1].x = Math.round((float) getMeasuredWidth() / 30 * 13);        mTickPoints[1].y = Math.round((float) getMeasuredHeight() / 30 * 20);        mTickPoints[2].x = Math.round((float) getMeasuredWidth() / 30 * 22);        mTickPoints[2].y = Math.round((float) getMeasuredHeight() / 30 * 10);        mLeftLineDistance = (float) Math.sqrt(Math.pow(mTickPoints[1].x - mTickPoints[0].x, 2) +                Math.pow(mTickPoints[1].y - mTickPoints[0].y, 2));        mRightLineDistance = (float) Math.sqrt(Math.pow(mTickPoints[2].x - mTickPoints[1].x, 2) +                Math.pow(mTickPoints[2].y - mTickPoints[1].y, 2));        mTickPaint.setStrokeWidth(mStrokeWidth);    }    @Override    protected void onDraw(Canvas canvas) {        drawBorder(canvas);        drawCenter(canvas);        drawTick(canvas);    }    private void drawCenter(Canvas canvas) {        mPaint.setColor(mUnCheckedColor);        float radius = (mCenterPoint.x - mStrokeWidth) * mScaleVal;        canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, radius, mPaint);    }    private void drawBorder(Canvas canvas) {        mFloorPaint.setColor(mFloorColor);        int radius = mCenterPoint.x;        canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, radius * mFloorScale, mFloorPaint);    }    private void drawTick(Canvas canvas) {        if (mTickDrawing && isChecked()) {            drawTickPath(canvas);        }    }    private void drawTickPath(Canvas canvas) {        mTickPath.reset();        // draw left of the tick        if (mDrewDistance < mLeftLineDistance) {            float step = (mWidth / 20.0f) < 3 ? 3 : (mWidth / 20.0f);            mDrewDistance += step;            float stopX = mTickPoints[0].x + (mTickPoints[1].x - mTickPoints[0].x) * mDrewDistance / mLeftLineDistance;            float stopY = mTickPoints[0].y + (mTickPoints[1].y - mTickPoints[0].y) * mDrewDistance / mLeftLineDistance;            mTickPath.moveTo(mTickPoints[0].x, mTickPoints[0].y);            mTickPath.lineTo(stopX, stopY);            canvas.drawPath(mTickPath, mTickPaint);            if (mDrewDistance > mLeftLineDistance) {                mDrewDistance = mLeftLineDistance;            }        } else {            mTickPath.moveTo(mTickPoints[0].x, mTickPoints[0].y);            mTickPath.lineTo(mTickPoints[1].x, mTickPoints[1].y);            canvas.drawPath(mTickPath, mTickPaint);            // draw right of the tick            if (mDrewDistance < mLeftLineDistance + mRightLineDistance) {                float stopX = mTickPoints[1].x + (mTickPoints[2].x - mTickPoints[1].x) * (mDrewDistance - mLeftLineDistance) / mRightLineDistance;                float stopY = mTickPoints[1].y - (mTickPoints[1].y - mTickPoints[2].y) * (mDrewDistance - mLeftLineDistance) / mRightLineDistance;                mTickPath.reset();                mTickPath.moveTo(mTickPoints[1].x, mTickPoints[1].y);                mTickPath.lineTo(stopX, stopY);                canvas.drawPath(mTickPath, mTickPaint);                float step = (mWidth / 20) < 3 ? 3 : (mWidth / 20);                mDrewDistance += step;            } else {                mTickPath.reset();                mTickPath.moveTo(mTickPoints[1].x, mTickPoints[1].y);                mTickPath.lineTo(mTickPoints[2].x, mTickPoints[2].y);                canvas.drawPath(mTickPath, mTickPaint);            }        }        // invalidate        if (mDrewDistance < mLeftLineDistance + mRightLineDistance) {            postDelayed(new Runnable() {                @Override                public void run() {                    postInvalidate();                }            }, 10);        }    }    private void startCheckedAnimation() {        ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0f);        animator.setDuration(mAnimDuration / 3 * 2);        animator.setInterpolator(new LinearInterpolator());        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                mScaleVal = (float) animation.getAnimatedValue();                mFloorColor = getGradientColor(mUnCheckedColor, mCheckedColor, 1 - mScaleVal);                postInvalidate();            }        });        animator.start();        ValueAnimator floorAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f);        floorAnimator.setDuration(mAnimDuration);        floorAnimator.setInterpolator(new LinearInterpolator());        floorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                mFloorScale = (float) animation.getAnimatedValue();                postInvalidate();            }        });        floorAnimator.start();        drawTickDelayed();    }    private void startUnCheckedAnimation() {        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1.0f);        animator.setDuration(mAnimDuration);        animator.setInterpolator(new LinearInterpolator());        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                mScaleVal = (float) animation.getAnimatedValue();                mFloorColor = getGradientColor(mCheckedColor, COLOR_FLOOR_UNCHECKED, mScaleVal);                postInvalidate();            }        });        animator.start();        ValueAnimator floorAnimator = ValueAnimator.ofFloat(1.0f, 0.8f, 1.0f);        floorAnimator.setDuration(mAnimDuration);        floorAnimator.setInterpolator(new LinearInterpolator());        floorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                mFloorScale = (float) animation.getAnimatedValue();                postInvalidate();            }        });        floorAnimator.start();    }    private void drawTickDelayed() {        postDelayed(new Runnable() {            @Override            public void run() {                mTickDrawing = true;                postInvalidate();            }        }, mAnimDuration);    }    private static int getGradientColor(int startColor, int endColor, float percent) {        int sr = (startColor & 0xff0000) >> 0x10;        int sg = (startColor & 0xff00) >> 0x8;        int sb = (startColor & 0xff);        int er = (endColor & 0xff0000) >> 0x10;        int eg = (endColor & 0xff00) >> 0x8;        int eb = (endColor & 0xff);        int cr = (int) (sr * (1 - percent) + er * percent);        int cg = (int) (sg * (1 - percent) + eg * percent);        int cb = (int) (sb * (1 - percent) + eb * percent);        return Color.argb(0xff, cr, cg, cb);    }    public void setOnCheckedChangeListener(OnCheckedChangeListener l) {        this.mListener = l;    }    public interface OnCheckedChangeListener {        void onCheckedChanged(SmoothCheckBox checkBox, boolean isChecked);    }}

2,在布局文件中引用这个定义的CheckBox控件。

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:gravity="center"    android:orientation="vertical"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="cn.refactor.smoothcheckbox.MainActivity">    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginBottom="10dp"        android:layout_weight="1"        android:gravity="center"        android:orientation="horizontal">        <cn.refactor.library.SmoothCheckBox            android:id="@+id/mcb"            android:layout_width="40dp"            android:layout_height="40dp"            android:layout_margin="5dp"            app:color_checked="@color/colorAccent" />        <cn.refactor.library.SmoothCheckBox            android:layout_width="40dp"            android:layout_height="40dp"            android:layout_margin="5dp"            app:color_checked="#447eeb" />        <cn.refactor.library.SmoothCheckBox            android:layout_width="40dp"            android:layout_height="40dp"            android:layout_margin="5dp"            app:color_checked="#149A45" />        <cn.refactor.library.SmoothCheckBox            android:layout_width="40dp"            android:layout_height="40dp"            android:layout_margin="5dp" />    </LinearLayout></LinearLayout>

这里写图片描述

3,使用

 setChecked(boolean checked);                   // 默认不带动画,若需要动画 调用重载方法 setChecked(boolean checked, boolean animate);  // 参数: animate 是否显示动画
 protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_sample);        final SmoothCheckBox mcb = (SmoothCheckBox) findViewById(R.id.mcb);        mcb.setOnCheckedChangeListener(new SmoothCheckBox.OnCheckedChangeListener() {            @Override            public void onCheckedChanged(SmoothCheckBox checkBox, boolean isChecked) {                Log.d("MellowCheckBox", String.valueOf(isChecked));            }        });    }    
1 0
原创粉丝点击