MaterialCheckBox

演示效果:

源码

原理分析图:

圆形背景;

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="200" android:exitFadeDuration="200">

    <item android:state_pressed="false">
        <shape android:shape="oval">
            <solid android:color="@android:color/transparent" />
        </shape>
    </item>
    <item android:state_checked="true">
        <shape android:shape="oval">
            <solid android:color="#881f38fd" />
        </shape>
    </item>
    <item android:state_focused="true">
        <shape android:shape="oval">
            <solid android:color="#881f38fd" />
        </shape>
    </item>
    <item android:state_pressed="true">
        <shape android:shape="oval">
            <solid android:color="#881f38fd" />
        </shape>
    </item>
</selector>

自定义View源码:

package com.nsb.app.ui.view;

import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;

/** * Created by Hanks on 2015/6/30. */
public class MaterialCheckBox extends View {

    private Paint paintBlue;
    private Paint paintWithe;

    private int borderColor = Color.GRAY;     //边框颜色
    private int backgroundColor = Color.BLUE; //填充颜色
    private int doneShapeColor = Color.WHITE; //对号颜色

    private int baseWidth;                    //checkbox 边框宽度
    private int borderWidth;
    private int width, height;                //控件宽高

    private float[] points = new float[8];    //对号的4个点的坐标

    private int DURATION = 200;               //动画时长
    private boolean checked;                  //选择状态
    private float correctProgress;            //划对号的进度

    private boolean drawRecting;
    private boolean isAnim;
    private OnCheckedChangeListener listener;

    private float padding;                    //内切圆的边据边框的距离


    public MaterialCheckBox(Context context) {
        this(context, null);
    }

    public MaterialCheckBox(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MaterialCheckBox(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public MaterialCheckBox(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    /** * init * * @param context */
    private void init(Context context) {

        borderWidth = baseWidth = dp2px(2);

        paintBlue = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintBlue.setColor(borderColor);
        paintBlue.setStrokeWidth(borderWidth);

        paintWithe = new Paint(Paint.ANTI_ALIAS_FLAG);
        paintWithe.setColor(doneShapeColor);
        paintWithe.setStrokeWidth(dp2px(2));

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                setChecked(!isChecked());
            }
        });
        drawRecting = true;
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        height = width = Math.max(w, h);

        points[0] = 101 / 378f * width;
        points[1] = 0.5f * width;

        points[2] = 163 / 378f * width;
        points[3] = 251 / 378f * width;

        points[4] = 149 / 378f * width;
        points[5] = 250 / 378f * width;

        points[6] = 278 / 378f * width;
        points[7] = 122 / 378f * width;

        padding = 57 / 378f * width;

    }

    /** * draw checkbox * * @param canvas */
    @Override
    protected void onDraw(Canvas canvas) {
        RectF rect = new RectF(padding, padding, width - padding, height - padding);
        canvas.drawRoundRect(rect, baseWidth, baseWidth, paintBlue);
        if (drawRecting) {
            canvas.drawRect(padding + borderWidth, padding + borderWidth, width - padding - borderWidth, height - padding - borderWidth, paintWithe);
        } else {
            //画对号
            if (correctProgress > 0) {
                if (correctProgress < 1 / 3f) {
                    float x = points[0] + (points[2] - points[0]) * correctProgress;
                    float y = points[1] + (points[3] - points[1]) * correctProgress;
                    canvas.drawLine(points[0], points[1], x, y, paintWithe);
                } else {
                    float x = points[4] + (points[6] - points[4]) * correctProgress;
                    float y = points[5] + (points[7] - points[5]) * correctProgress;
                    canvas.drawLine(points[0], points[1], points[2], points[3], paintWithe);
                    canvas.drawLine(points[4], points[5], x, y, paintWithe);
                }
            }
        }
    }


    public void setBackgroundColor(int backgroundColor) {
        this.backgroundColor = backgroundColor;
    }

    public void setDoneShapeColor(int doneShapeColor) {
        this.doneShapeColor = doneShapeColor;
        paintWithe.setColor(doneShapeColor);
    }

    public void setBorderColor(int borderColor) {
        this.borderColor = borderColor;
    }

    public void setBorderWidth(int baseWidth) {
        this.baseWidth = baseWidth;
    }

    public boolean isChecked() {
        return checked;
    }

    public void setChecked(boolean checked) {
        this.checked = checked;
        if (checked) {
            showRect();
        } else {
            hideCorrect();
        }
    }

    private void hideRect() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        drawRecting = true;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float p = (float) animation.getAnimatedValue();
                float c = 1f - p;
                borderWidth = (int) (baseWidth + c * (width - baseWidth));
                paintBlue.setColor(evaluate(c, borderColor, backgroundColor));
                invalidate();
                if (p >= 1) {
                    isAnim = false;
                    if (listener != null) {
                        checked = false;
                        listener.onCheckedChanged(MaterialCheckBox.this, checked);
                    }
                }
            }
        });
        va.start();
    }

    private void showRect() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        drawRecting = true;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float p = (float) animation.getAnimatedValue();
                borderWidth = (int) (10 + p * (width - 10));
                paintBlue.setColor(evaluate(p, borderColor, backgroundColor));
                invalidate();
                if (p >= 1) {
                    isAnim = false;
                    drawRecting = false;
                    showCorrect();
                }
            }
        });
        va.start();
    }

    private void showCorrect() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        correctProgress = 0;
        drawRecting = false;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                correctProgress = (float) animation.getAnimatedValue();
                invalidate();
                if (correctProgress >= 1) {
                    isAnim = false;
                    if (listener != null) {
                        checked = true;
                        listener.onCheckedChanged(MaterialCheckBox.this, checked);
                    }
                }
            }
        });
        va.start();
    }

    private void hideCorrect() {
        if (isAnim) {
            return;
        }
        isAnim = true;
        correctProgress = 1;
        drawRecting = false;
        ValueAnimator va = ValueAnimator.ofFloat(0, 1).setDuration(DURATION);
        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float p = (float) animation.getAnimatedValue();
                correctProgress = 1f - p;
                invalidate();
                if (p >= 1) {
                    isAnim = false;
                    hideRect();
                }
            }
        });
        va.start();
    }

    public void setOnCheckedChangedListener(OnCheckedChangeListener listener) {
        this.listener = listener;
    }

    interface OnCheckedChangeListener {
        void onCheckedChanged(View view, boolean isChecked);
    }

    private int evaluate(float fraction, int startValue, int endValue) {
        int startInt = startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;
        return ((startA + (int) (fraction * (endA - startA))) << 24)
                | ((startR + (int) (fraction * (endR - startR))) << 16)
                | ((startG + (int) (fraction * (endG - startG))) << 8)
                | ((startB + (int) (fraction * (endB - startB))));
    }

    public int dp2px(float value) {
        final float scale = getContext().getResources().getDisplayMetrics().densityDpi;
        return (int) (value * (scale / 160) + 0.5f);
    }
}

你可能感兴趣的:(android,checkbox,view,自定义,material)