Android 自定义view实现签到功能

今天给大家奉上一篇Android 自定义view实现签到功能

程序截图

Android 自定义view实现签到功能_第1张图片

点击签到

Android 自定义view实现签到功能_第2张图片

Android 自定义view实现签到功能_第3张图片

Android 自定义view实现签到功能_第4张图片

点击签到,实现签到动画,且点亮签到图标。不允许多次签到,大致上功能就是这样

项目结构

Android 自定义view实现签到功能_第5张图片

大致上就两个核心文件一个自定义view,一个单位换算工具类

工具类

package cn.llwy.com.signin.utils;

import android.content.Context;
import java.util.List;

import cn.llwy.com.signin.bean.SigninBean;

/**
 * description: 单位换算工具.
 *
 * @author 刘明昆.
 * @date 2018/8/30.
 */
public class CalcUtils {
    public static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }

    public static int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 寻到最大两个值的位置
     */
    public static int[] findMax(List steps) {
        int[] value = new int[2];
        int[] position = new int[2];
        int temValue;
        int temPosition;
        for (int i = 0; i < steps.size(); i++) {
            if (steps.get(i).getNumber() > value[1]) {
                //比较出大的放到value[0]中
                value[1] = steps.get(i).getNumber();
                position[1] = i;
            }
            if (value[1] > value[0]) {
                //把最大的放到value[0]中,交换位置
                temValue = value[0];
                value[0] = value[1];
                value[1] = temValue;

                temPosition = position[0];
                position[0] = position[1];
                position[1] = temPosition;
            }
        }
        return position;
    }
}

自定义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(), 2f);

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

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

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

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

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

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

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

    //默认的图标
    private Drawable mDefaultIcon;

    //UP图标
    private Drawable mUpIcon;

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

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

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

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

    //图标中心点位置
    private List 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 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(), 8f));

        //已经完成的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
        mUpIcon = ContextCompat.getDrawable(getContext(), R.mipmap.up);
    }

    @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 + mIconWeight + 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) + mIconWeight / 2;
            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);
                } else {
                    //普通完成的颜色
                    mTextNumberPaint.setColor(mCompletedLineColor);
                }
            } else {
                //还没签到的,颜色均为灰色
                mTextNumberPaint.setColor(mUnCompletedLineColor);
            }

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

            //绘制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)));
                mUpIcon.setBounds(rectUp);
                mUpIcon.draw(canvas);
            }
        }

        //记录重绘次数
        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) + mIconWeight / 2;
            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);
                } else {
                    //普通完成的颜色
                    mTextNumberPaint.setColor(mCompletedLineColor);
                }
            } else {
                //还没签到的,颜色均为灰色
                mTextNumberPaint.setColor(mUnCompletedLineColor);
            }
            canvas.drawText("+" + signinBean.getNumber(),
                    currentComplectedXPosition + CalcUtils.dp2px(getContext(), 2f),
                    mCenterY - mIconHeight / 2 - CalcUtils.dp2px(getContext(), 0.5f),
                    mTextNumberPaint);

            //绘制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)));
                mUpIcon.setBounds(rectUp);
                mUpIcon.draw(canvas);
            }
        }
    }

    /**
     * 设置流程步数
     *
     * @param lists 流程步数
     */
    public void setStepNum(List 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();
    }

}

MainActivity

package cn.llwy.com.signin;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

import cn.llwy.com.signin.View.MysigninView;
import cn.llwy.com.signin.bean.SigninBean;

public class MainActivity extends AppCompatActivity {
    private MysigninView msigninView;
    private TextView txt;
    private ArrayList signinBen = new ArrayList<>();
    boolean flag=true;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Init();

        InitData();
        InitListtener();
    }

    private void InitListtener() {
        txt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(flag) {
                    msigninView.startSignAnimation(2);
                    flag=false;
                    txt.setBackground(getResources().getDrawable(R.mipmap.signed));
                }else{
                    Toast.makeText(MainActivity.this,"当前已签到,不允许重复签到",Toast.LENGTH_LONG).show();
                }
            }
        });
    }

    private void InitData() {
        signinBen.add(new SigninBean(SigninBean.STEP_COMPLETED, 1));
        signinBen.add(new SigninBean(SigninBean.STEP_COMPLETED, 2));
        signinBen.add(new SigninBean(SigninBean.STEP_UNDO, 5));
        signinBen.add(new SigninBean(SigninBean.STEP_UNDO, 5));
        signinBen.add(new SigninBean(SigninBean.STEP_UNDO, 5));
        signinBen.add(new SigninBean(SigninBean.STEP_UNDO, 5));
        signinBen.add(new SigninBean(SigninBean.STEP_UNDO, 10));
        msigninView.setStepNum(signinBen);
    }

    private void Init() {
        msigninView = findViewById(R.id.signin_view);
        txt = findViewById(R.id.txt_click);
    }
}

最后感谢大家使用宝贵的时间来观看我的博客。谢谢大家!

Demo下载地址

你可能感兴趣的:(移动开发)