Android 自定义view实现签到送积分

博客包含程序截图,介绍了点击签到、重复签到等操作,还提及自定义view,最后给出了DEMO下载地址。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程序截图

点击签到

重复签到

自定义view

package cn.llwy.com.signin.View;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import cn.llwy.com.signin.R;
import cn.llwy.com.signin.bean.SigninBean;
import cn.llwy.com.signin.utils.CalcUtils;

public class MysigninView extends View {

    //初始化签到动画执行时间为300毫秒
    private final static int ANIMATION_TIME=300;

    //动画执行间隔次数
    private final static int ANINMATION_INTERVAL=10;

    //线段高度
    private float mCompletedHeight = CalcUtils.dp2px(getContext(), 5f);

    //图标的宽度
    private float mIconWeight = CalcUtils.dp2px(getContext(), 10f);

    //图标的高度
    private float mIconHeight = CalcUtils.dp2px(getContext(), 10f);

    //up的宽度
    private float mUpWeight = CalcUtils.dp2px(getContext(), 20.5f);

    //up的高度
    private float mUpHeight = CalcUtils.dp2px(getContext(), 20.5f);

    //线段的长度
    private float mLineWeight = CalcUtils.dp2px(getContext(), 40f);

   //已经完成的图标
    private Drawable mCompleteIcon;

    //正在进行的图标
    private Drawable mAttentionIcon;

    //默认的图标
    private Drawable mDefaultIcon;



    //图标中心点Y
    private float mCenterY;

    //线段的左上方Y
    private float mLeftY;

    //线段的右下方
    private float mRightY;

    //数据源
    private List<SigninBean> list;
    private int mSinginNum=0;

    //图标中心点位置
    private List<Float> mCircleCenterPointPositionList;

   //未完成的线段Paint
    private Paint mUnCompletedPaint;

    // 完成的线段paint
    private Paint mCompletedPaint;
   //未完成颜色
    private int mUnCompletedLineColor = ContextCompat.getColor(getContext(), R.color.c_999999);
    // 天数颜色
    private int mUnCompletedTextColor = ContextCompat.getColor(getContext(), R.color.c_cccccc);
    //up魅力值颜色
    private int mCurrentTextColor = ContextCompat.getColor(getContext(), R.color.c_f7b93c);
    //完成的颜色

    private int mCompletedLineColor = ContextCompat.getColor(getContext(), R.color.c_41c961);

    private Paint mTextNumberPaint;

    private Paint mTextNumberPaintday;
    //是否执行动画
    private boolean isAnimation = false;

    //记录重绘次数
    private int mCount = 0;

    // 执行动画线段每次绘制的长度,线段的总长度除以总共执行的时间乘以每次执行的间隔时间
    private float mAnimationWeight = (mLineWeight / ANIMATION_TIME) * ANINMATION_INTERVAL;

    // 执行动画的位置
    private int mPosition;
    private int[] mMax;

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

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

    public MysigninView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * init
     */
    private void init() {
        list = new ArrayList<>();

        mCircleCenterPointPositionList = new ArrayList<>();

        //未完成文字画笔
        mUnCompletedPaint = new Paint();
        mUnCompletedPaint.setAntiAlias(true);
        mUnCompletedPaint.setColor(mUnCompletedLineColor);
        mUnCompletedPaint.setStrokeWidth(2);
        mUnCompletedPaint.setStyle(Paint.Style.FILL);

        //已完成画笔文字
        mCompletedPaint = new Paint();
        mCompletedPaint.setAntiAlias(true);
        mCompletedPaint.setColor(mCompletedLineColor);
        mCompletedPaint.setStrokeWidth(2);
        mCompletedPaint.setStyle(Paint.Style.FILL);

        //number paint
        mTextNumberPaint = new Paint();
        mTextNumberPaint.setAntiAlias(true);
        mTextNumberPaint.setColor(mUnCompletedTextColor);
        mTextNumberPaint.setStyle(Paint.Style.FILL);
        mTextNumberPaint.setTextSize(CalcUtils.sp2px(getContext(), 12f));

        mTextNumberPaintday = new Paint();
        mTextNumberPaintday.setAntiAlias(true);
        mTextNumberPaintday.setColor(mUnCompletedTextColor);
        mTextNumberPaintday.setStyle(Paint.Style.FILL);
        mTextNumberPaintday.setTextSize(CalcUtils.sp2px(getContext(), 12f));

        //已经完成的icon
        mCompleteIcon = ContextCompat.getDrawable(getContext(), R.mipmap.signed);
        //正在进行的icon
        mAttentionIcon = ContextCompat.getDrawable(getContext(), R.mipmap.unsigned);
        //未完成的icon
        mDefaultIcon = ContextCompat.getDrawable(getContext(), R.mipmap.unsigned);
        //UP的icon

    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //图标的中中心Y点
        mCenterY = CalcUtils.dp2px(getContext(), 28f) + mIconHeight / 2;
        //获取左上方Y的位置,获取该点的意义是为了方便画矩形左上的Y位置
        mLeftY = mCenterY - (mCompletedHeight / 2);
        //获取右下方Y的位置,获取该点的意义是为了方便画矩形右下的Y位置
        mRightY = mCenterY + mCompletedHeight / 2;

        //计算图标中心点
        mCircleCenterPointPositionList.clear();
        //第一个点距离父控件左边14.5dp
        float size = mIconWeight / 2 + CalcUtils.dp2px(getContext(), 14.5f);
        mCircleCenterPointPositionList.add(size);
        for (int i = 1; i < mSinginNum; i++) {
            //从第二个点开始,每个点距离上一个点为图标的宽度加上线段的23dp的长度
            size = size +5 + mLineWeight;
            mCircleCenterPointPositionList.add(size);
        }
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected synchronized void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isAnimation) {
            drawSign(canvas);
        } else {
            drawUnSign(canvas);
        }
    }

    /**
     * 绘制签到(伴随签到动画)
     */
    @SuppressLint("DrawAllocation")
    private void drawSign(Canvas canvas) {
        for (int i = 0; i < mCircleCenterPointPositionList.size(); i++) {
            //绘制线段
            float preComplectedXPosition = mCircleCenterPointPositionList.get(i) ;
            if (i != mCircleCenterPointPositionList.size() - 1) {
                //最后一条不需要绘制
                if (list.get(i + 1).getState() == SigninBean.STEP_COMPLETED) {
                    //下一个是已完成,当前才需要绘制绿色
                    canvas.drawRect(preComplectedXPosition, mLeftY, preComplectedXPosition + mLineWeight,
                            mRightY, mCompletedPaint);
                } else {
                    //其余绘制灰色

                    //当前位置执行动画
                    if (i == mPosition - 1) {
                        //绿色开始绘制的地方,
                        float endX = preComplectedXPosition + mAnimationWeight * (mCount / ANINMATION_INTERVAL);
                        //绘制绿色
                        canvas.drawRect(preComplectedXPosition, mLeftY, endX, mRightY, mCompletedPaint);
                        //绘制灰色
                        canvas.drawRect(endX, mLeftY, preComplectedXPosition + mLineWeight,
                                mRightY, mUnCompletedPaint);
                    } else {
                        canvas.drawRect(preComplectedXPosition, mLeftY, preComplectedXPosition + mLineWeight,
                                mRightY, mUnCompletedPaint);
                    }
                }
            }

            //绘制图标
            float currentComplectedXPosition = mCircleCenterPointPositionList.get(i);
            Rect rect = new Rect((int) (currentComplectedXPosition - mIconWeight / 2),
                    (int) (mCenterY - mIconHeight / 2),
                    (int) (currentComplectedXPosition + mIconWeight / 2),
                    (int) (mCenterY + mIconHeight / 2));

            SigninBean signinBean = list.get(i);

            if (i == mPosition && mCount == ANIMATION_TIME) {
                //当前需要绘制成绿色了
                mCompleteIcon.setBounds(rect);
                mCompleteIcon.draw(canvas);
            } else {
                if (signinBean.getState() == SigninBean.STEP_UNDO) {
                    mDefaultIcon.setBounds(rect);
                    mDefaultIcon.draw(canvas);
                } else if (signinBean.getState() == SigninBean.STEP_CURRENT) {
                    mAttentionIcon.setBounds(rect);
                    mAttentionIcon.draw(canvas);
                } else if (signinBean.getState() == SigninBean.STEP_COMPLETED) {
                    mCompleteIcon.setBounds(rect);
                    mCompleteIcon.draw(canvas);
                }
            }

            //绘制图标
            if (signinBean.getState() == SigninBean.STEP_COMPLETED || (i == mPosition
                    && mCount == ANIMATION_TIME)) {
                //已经完成了或者是当前动画完成并且需要当前位置需要改变
                if (i == mMax[0] || i == mMax[1]) {
                    //是up的需要橙色
                    mTextNumberPaint.setColor(mCurrentTextColor);

                    mTextNumberPaintday.setColor(mCurrentTextColor);
                } else {
                    //普通完成的颜色
                    mTextNumberPaint.setColor(mCompletedLineColor);
                    mTextNumberPaintday.setColor(mCompletedLineColor);
                }
            } else {
                //还没签到的,颜色均为灰色
                mTextNumberPaint.setColor(mUnCompletedLineColor);
                mTextNumberPaintday.setColor(mUnCompletedLineColor);
            }

            canvas.drawText("+" + signinBean.getNumber(),
                    currentComplectedXPosition - CalcUtils.dp2px(getContext(), 5f),
                    mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 15f),
                    mTextNumberPaint);

            canvas.drawText(signinBean.getDay()+"天",
                    currentComplectedXPosition - CalcUtils.dp2px(getContext(), 5f),
                    mCenterY - mIconHeight / 2 + CalcUtils.dp2px(getContext(), 30f),
                    mTextNumberPaintday);

            //绘制UP
            if (i == mMax[0] || i == mMax[1]) {
                //需要UP才进行绘制
                Rect rectUp =
                        new Rect((int) (currentComplectedXPosition - mUpWeight / 2),
                                (int) (mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 8f) - mUpHeight),
                                (int) (currentComplectedXPosition + mUpWeight / 2),
                                (int) (mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 8f)));

            }
        }

        //记录重绘次数
        mCount = mCount + ANINMATION_INTERVAL;
        if (mCount <= ANIMATION_TIME) {
            //引起重绘
            postInvalidate();
        } else {
            //重绘完成
            isAnimation = false;
            mCount = 0;
        }
    }

    /**
     * 绘制初始状态的view
     */
    @SuppressLint("DrawAllocation")
    private void drawUnSign(Canvas canvas) {
        for (int i = 0; i < mCircleCenterPointPositionList.size(); i++) {
            //绘制线段
            float preComplectedXPosition = mCircleCenterPointPositionList.get(i) ;
            if (i != mCircleCenterPointPositionList.size() - 1) {
                //最后一条不需要绘制
                if (list.get(i + 1).getState() == SigninBean.STEP_COMPLETED) {
                    //下一个是已完成,当前才需要绘制绿色
                    canvas.drawRect(preComplectedXPosition, mLeftY, preComplectedXPosition + mLineWeight,
                            mRightY, mCompletedPaint);
                } else {
                    //其余绘制灰色
                    canvas.drawRect(preComplectedXPosition, mLeftY, preComplectedXPosition + mLineWeight,
                            mRightY, mUnCompletedPaint);
                }
            }

            //绘制图标
            float currentComplectedXPosition = mCircleCenterPointPositionList.get(i);
            Rect rect = new Rect((int) (currentComplectedXPosition - mIconWeight / 2),
                    (int) (mCenterY - mIconHeight / 2),
                    (int) (currentComplectedXPosition + mIconWeight / 2),
                    (int) (mCenterY + mIconHeight / 2));

            SigninBean signinBean = list.get(i);

            if (signinBean.getState() == SigninBean.STEP_UNDO) {
                mDefaultIcon.setBounds(rect);
                mDefaultIcon.draw(canvas);
            } else if (signinBean.getState() == SigninBean.STEP_CURRENT) {
                mAttentionIcon.setBounds(rect);
                mAttentionIcon.draw(canvas);
            } else if (signinBean.getState() == SigninBean.STEP_COMPLETED) {
                mCompleteIcon.setBounds(rect);
                mCompleteIcon.draw(canvas);
            }

            //绘制增加的分数数目
            if (signinBean.getState() == SigninBean.STEP_COMPLETED) {
                //已经完成了
                if (i == mMax[0] || i == mMax[1]) {
                    //是up的需要橙色
                    mTextNumberPaint.setColor(mCurrentTextColor);
                    mTextNumberPaintday.setColor(mCurrentTextColor);
                } else {
                    //普通完成的颜色
                    mTextNumberPaint.setColor(mCompletedLineColor);
                    mTextNumberPaintday.setColor(mCompletedLineColor);
                }
            } else {
                //还没签到的,颜色均为灰色
                mTextNumberPaint.setColor(mUnCompletedLineColor);
                mTextNumberPaintday.setColor(mUnCompletedLineColor);
            }
            canvas.drawText("+" + signinBean.getNumber(),
                    currentComplectedXPosition - CalcUtils.dp2px(getContext(), 5f),
                    mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 15f),
                    mTextNumberPaint);

            canvas.drawText(  signinBean.getDay()+"天",
                    currentComplectedXPosition - CalcUtils.dp2px(getContext(), 5f),
                    mCenterY - mIconHeight / 2 + CalcUtils.dp2px(getContext(), 30f),
                    mTextNumberPaintday);

            //绘制UP
            if (i == mMax[0] || i == mMax[1]) {
                //需要UP才进行绘制
                Rect rectUp =
                        new Rect((int) (currentComplectedXPosition - mUpWeight / 2),
                                (int) (mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 8f) - mUpHeight),
                                (int) (currentComplectedXPosition + mUpWeight / 2),
                                (int) (mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 8f)));

            }
        }
    }

    /**
     * 设置流程步数
     *
     * @param lists 流程步数
     */
    public void setStepNum(List<SigninBean> lists) {
        if (list == null) {
            return;
        }
        list = lists;
        mSinginNum = list.size();
        //找出最大的两个值的位置
        mMax = CalcUtils.findMax(lists);
        //引起重绘
        postInvalidate();
    }

    /**
     * 执行签到动画
     *
     * @param position 执行的位置
     */
    public void startSignAnimation(int position) {
        //线条从灰色变为绿色
        isAnimation = true;
        mPosition = position;
        //引起重绘
        postInvalidate();
    }

}

DEMO下载地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值