签到view

效果:

打卡view

code:

<declare-styleable name="SingInViewDemo">
    <attr name="sign_in_bg_clor" format="color" />
    <attr name="sign_in_pb_clor" format="color" />
    <attr name="sign_in_check_clor" format="color" />
    <attr name="sign_in_text_clor" format="color" />
    <attr name="sign_in_text_size" format="dimension" />
</declare-styleable>

<!--定义默认的res-style样式-->
<style name="def_sign_demo_in_style">
    <item name="sign_in_bg_clor">@android:color/white</item>
    <item name="sign_in_pb_clor">@android:color/holo_orange_dark</item>
    <item name="sign_in_check_clor">@android:color/white</item>
    <item name="sign_in_text_clor">@android:color/holo_purple</item>
    <item name="sign_in_text_size">12sp</item>
</style>

public class SingInViewDemo extends View {
    private static final int DEF_HEIGHT = 85;
    private static final int DEF_PADDING = 10;
    private static final int TEXT_MARGIN_TOP = 13;
    private static final float SECTION_SCALE = 1.2F / 2;
    private static final float SIGN_IN_BALL_SCALE = 1F / 6;
    private static final float SIGN_BG_RECT_SCALE = 1F / 4;
    private static final long DEF_ANIM_TIME = 4000;

    private int signInBgColor;
    private int signInPbColor;
    private int signInCheckColor;
    private int singInTextColor;
    private int singInTextSize;

    private Paint signInBgPaint;
    private Paint signInPbPaint;
    private Paint signInCheckPaint;
    private Paint signInTextPaint;

    private int viewWidth;
    private int viewHeight;
    private int viewPadding;
    private int signInBallRadio;
    private int signRectHeight;

    private RectF signInBgRectF;
    private int circleY;
    private int descY;

    private List<String> viewData;
    private List<Point> circlePoints;
    private List<Point> descPoints;
    private List<Path> signInPaths;
    private List<RectF> signInPbRectFs;


    private int currentSignTag;
    private ValueAnimator valueAnimator;
    private float persent;
    private RectF progressRectF;

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

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

    public SingInViewDemo(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        /**
         * 获取对应的属性
         */
        initAttrs(context, attrs, defStyleAttr);
        initTools();
    }

    private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SingInViewDemo, defStyleAttr, R.style.def_sign_demo_in_style);
        int indexCount = typedArray.getIndexCount();
        for (int i = 0; i < indexCount; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.SingInViewDemo_sign_in_bg_clor:
                    signInBgColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.SingInViewDemo_sign_in_pb_clor:
                    signInPbColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.SingInViewDemo_sign_in_check_clor:
                    signInCheckColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.SingInViewDemo_sign_in_text_clor:
                    singInTextColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.SingInViewDemo_sign_in_text_size:
                    singInTextSize = typedArray.getDimensionPixelSize(attr, 0);
                    break;
            }

        }
        /**
         * 释放
         */
        typedArray.recycle();

    }

    /**
     * onMeasure onSizeChanged
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 处理不同的测量规格
         * 本案例中未指定对应参数时,适配默认参数
         */
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int measure_height;
        if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
            measure_height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEF_HEIGHT, getResources().getDisplayMetrics());
            heightMeasureSpec = MeasureSpec.makeMeasureSpec(measure_height, MeasureSpec.EXACTLY);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        viewPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEF_PADDING, getResources().getDisplayMetrics());
        int textMarginTop = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, TEXT_MARGIN_TOP, getResources().getDisplayMetrics());

        viewWidth = w;
        viewHeight = h;
        signInBallRadio = (int) (viewHeight * SIGN_IN_BALL_SCALE / 2);
        signRectHeight = (int) (signInBallRadio * SIGN_BG_RECT_SCALE);
        /**
         * RectF(float left, float top, float right, float bottom)
         */
        signInBgRectF = new RectF(viewPadding + signInBallRadio, viewHeight * SECTION_SCALE - signInBallRadio - signRectHeight, viewWidth - viewPadding - signInBallRadio, viewHeight * SECTION_SCALE - signInBallRadio);

        circleY = (int) (signInBgRectF.top + signRectHeight / 2);
        descY = (int) (viewHeight * SECTION_SCALE) + signInBallRadio + textMarginTop;
        calculateCirclePoints(viewData);
        if (null != signInPbRectFs && signInPbRectFs.size() != 0) {
            progressRectF = signInPbRectFs.get(0);
        }
    }

    /**
     * 在onDraw中画出对应的图形
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawSignInBgRect(canvas);
        drawSignInPbRectNoAnim(canvas);
        drawSignInNormalCircle(canvas);
        drawSingInCheckCircle(canvas);
        drawSignDesc(canvas);
    }

    /**
     * 属性动画的形式绘制进度条
     *
     * @param canvas
     */
    private void drawSignInPbRectWithAnim(final Canvas canvas) {
        if (isNeedReturn()) {
            return;
        }
        final RectF targetRectF = signInPbRectFs.get(currentSignTag);
        RectF beforeRectF;
        if (currentSignTag >= 1) {
            beforeRectF = signInPbRectFs.get(currentSignTag - 1);
            progressRectF.left = beforeRectF.left;
        }
        /**
         * persent 控制进度
         */
        progressRectF.right = targetRectF.right * persent;
        canvas.drawRect(progressRectF, signInPbPaint);
    }


    private void drawSignInPbRectNoAnim(final Canvas canvas) {
        if (isNeedReturn()) {
            return;
        }
        canvas.drawRect(signInPbRectFs.get(currentSignTag), signInPbPaint);
    }

    private boolean isNeedReturn() {
        return currentSignTag < 0 || currentSignTag >= viewData.size();
    }

    private void drawSingInCheckCircle(Canvas canvas) {
        if (isNeedReturn()) {
            return;
        }
        for (int i = -1; i < currentSignTag; i++) {
            Point p = circlePoints.get(i + 1);
            Path path = signInPaths.get(i + 1);
            canvas.drawCircle(p.x, p.y, signInBallRadio, signInPbPaint);
            canvas.drawPath(path, signInCheckPaint);
        }
    }

    private void drawSignDesc(Canvas canvas) {
        if (null != viewData && viewData.size() > 0) {
            for (int i = 0; i < viewData.size(); i++) {
                Point p = descPoints.get(i);
                canvas.drawText(viewData.get(i), p.x, p.y, signInTextPaint);
            }
        }
    }

    private void drawSignInNormalCircle(Canvas canvas) {
        if (circlePoints != null && circlePoints.size() > 0) {
            for (Point p : circlePoints) {
                canvas.drawCircle(p.x, p.y, signInBallRadio, signInBgPaint);
            }
        }
    }

    private void drawSignInBgRect(Canvas canvas) {
        canvas.drawRect(signInBgRectF, signInBgPaint);
    }

    private void calculateCirclePoints(List<String> viewData) {
        if (null != viewData) {
            int intervalSize = viewData.size() - 1;
            /**
             * 一段线段的长度
             */
            int onePiece = (viewWidth - 2 * viewPadding - signInBallRadio * 2 * viewData.size()) / intervalSize;
            for (int i = 0; i < viewData.size(); i++) {
                Point circleP = new Point(viewPadding + i * onePiece + ((i + 1) * 2 - 1) * signInBallRadio, circleY);
                Point descP = new Point((int) (viewPadding + i * onePiece + ((i + 1) * 2 - 1) * signInBallRadio - signInTextPaint.measureText(viewData.get(i)) / 2), descY);

                Path signInPath = new Path();
                signInPath.moveTo(circleP.x - signInBallRadio / 2, circleP.y);
                signInPath.lineTo(circleP.x, circleP.y + signInBallRadio / 2);
                signInPath.lineTo(circleP.x + signInBallRadio / 2, circleP.y - signInBallRadio + signInBallRadio / 2);

                RectF signInPbRectF = new RectF(viewPadding + signInBallRadio, viewHeight * SECTION_SCALE - signInBallRadio - signRectHeight, circleP.x, viewHeight * SECTION_SCALE - signInBallRadio);

                signInPaths.add(signInPath);
                circlePoints.add(circleP);
                descPoints.add(descP);
                signInPbRectFs.add(signInPbRectF);
            }
        }
    }

    private void initTools() {
        circlePoints = new ArrayList<>();
        descPoints = new ArrayList<>();
        signInPaths = new ArrayList<>();
        signInPbRectFs = new ArrayList<>();
        currentSignTag = -1;
        valueAnimator = getValA(DEF_ANIM_TIME);

        signInBgPaint = creatPaint(signInBgColor, 0, Paint.Style.FILL, 0);
        signInPbPaint = creatPaint(signInPbColor, 0, Paint.Style.FILL, 0);
        signInCheckPaint = creatPaint(signInCheckColor, 0, Paint.Style.STROKE, 3);
        signInTextPaint = creatPaint(singInTextColor, singInTextSize, Paint.Style.FILL, 0);
    }

    private Paint creatPaint(int paintColor, int textSize, Paint.Style style, int lineWidth) {
        Paint paint = new Paint();
        paint.setColor(paintColor);
        paint.setAntiAlias(true);
        paint.setStrokeWidth(lineWidth);
        /**
         * paint.setDither 设置抖动
         * paint.setStrokeCap 设置笔触的样式
         * paint.setStrokeJoin 设置接合处的样式
         */
        paint.setDither(true);
        paint.setTextSize(textSize);
        paint.setStyle(style);
        paint.setStrokeCap(Paint.Cap.ROUND);
        paint.setStrokeJoin(Paint.Join.ROUND);

        return paint;
    }

    private ValueAnimator getValA(long defAnimTime) {
        /**
         * 创建对应的属性动画
         */
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1.0f);
        valueAnimator.setDuration(DEF_ANIM_TIME);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatCount(0);
        return valueAnimator;
    }

    public void setSignInData(List<String> signInData) {
        Log.e("--TAG---", "外界设置数据--->>");
        if (null != signInData) {
            viewData = signInData;
        }
    }

    public void signInEvent() {
        currentSignTag++;
        if (currentSignTag >= viewData.size()) { //横线的数目
            currentSignTag = viewData.size() - 1;
        }
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                persent = Float.valueOf(valueAnimator.getAnimatedValue().toString());
                invalidate();
                Log.e("TAG--->>", "persent--->" + persent);
            }
        });
        valueAnimator.start();
        invalidate();
    }

    public void resetSignView() {
        currentSignTag = -1;
        invalidate();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容