Android仿今日头条极速版签到功能

 

前言:这个签到功能一开始想找找有没有现成的轮子,后来国内国外找了一圈也没发现类似的。只能自己动手撸一个。

 

注意:这个自定义view我并没有适配warp_content,也就是说,你只能给它固定的高度,比如200dp。否则它会默认填满你的整个屏幕。

另外  像dip2px这种代码,每位开发者自己应该都有,我就不补充了,像图片文件,我这边也不好补充。

 

这篇文章不会教你如何直接使用这个自定义view,因为每个人的需求都不一样,你肯定是要在我的基础上修修改改,所以你最好能够看懂这些代码。

 

对于自定义view基础比较薄弱的人,看懂其实也不难,我所认为对你最大的难点是:

Q:线条如何转折?

A: 使用Path

Q:线条如何变成矩形?

A:给画笔添加setStrokeCap和setStrokeJoin(具体去源码里搜索一下就看到了)

Q:进度条的动画效果如何实现?

A:使用属性动画。需要用到三个动画,并且监听动画进度,第一个动画播放完毕后  开始启动第二个动画。(后来我发现使用animatorSet更方便,你如果感兴趣可以试试)

 

我只是告诉你大致的思路,具体实现从源码中参考。

 

思路:  1.先画出最底层的线,再画出红色的线盖住它,我们只需要控制红色线的长度变化即可。

 

使用(只讲述API):

signInView.setSignInDay(int);//取值:1-10  ,分别让红线停留在对应1-10个小圆点所在的地方
signInView.getSignInDay();//返回当前传入的天数

代码:

/**
 * Created by Fushize on 2019/8/19.
 */
public class SignInView extends View {
    private int startWidth = 20;
    private int endWidth;
    private Paint paint;
    private Paint pointPaint;
    private Paint imgPaint;
    private Paint textPaint;
    private int signInDay;
    private float progress = 0;
    private float progress2 = 0;
    private float progress3 = 0;
    private int maxLine = 800;   //最大的长度
    private final int offSetY = 200;    //偏移的Y
    private int offSetX = 0;    //偏移的X
    private final int maxHeigh = 340;   //最大高度
    private final int circleRadius = 7; //小圆点的半径
    private int duration = 2000;    //动画时长
    public String[] strMoney = {"10", "20", "30", "40", "50", "60"};
    public String[] strMoney2 = {"880", "500", "300", "150"};
    private String[] strDay = {"1天", "2天", "3天", "4天", "5天", "6天"};
    private String[] strDay2 = {"30天", "21天", "14天", "7天"};
    private ObjectAnimator animator;
    private ObjectAnimator animator2;
    private ObjectAnimator animator3;

    private @SuppressLint("DrawAllocation")
    Bitmap sign_in_150;
    private @SuppressLint("DrawAllocation")
    Bitmap sign_in_300;
    private @SuppressLint("DrawAllocation")
    Bitmap sign_in_500;
    private @SuppressLint("DrawAllocation")
    Bitmap sign_in_880;


    public SignInView(Context context) {
        super(context, null);
        init();
    }


    // 创建 getter 方法
    public float getProgress() {
        return progress;
    }

    // 创建 setter 方法
    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }


    public SignInView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SignInView);
        signInDay = ta.getInt(R.styleable.SignInView_signInDay, 0);

        ta.recycle();
        init();
    }


    public SignInView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @SuppressLint("ObjectAnimatorBinding")
    private void init() {
        maxLine = getContext().getResources().getDisplayMetrics().widthPixels - CommonUtil.dip2px(74);
        startWidth = CommonUtil.dip2px(20);
        offSetX = startWidth;
        endWidth = maxLine - startWidth;
        paint = new Paint();
        pointPaint = new Paint();
        imgPaint = new Paint();
        imgPaint.setStyle(STROKE);
        imgPaint.setAntiAlias(true);
        textPaint = new Paint();
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setAntiAlias(true);
        //最底层的线
        //上层的线
        paint.setStrokeWidth(36);
        paint.setStyle(STROKE);
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStrokeJoin(Paint.Join.ROUND);
        paint.setStrokeCap(Paint.Cap.ROUND);
        //画小圆点
        pointPaint.setStrokeWidth(5);
        pointPaint.setStyle(FILL);
        pointPaint.setStrokeJoin(Paint.Join.ROUND);
        pointPaint.setStrokeCap(Paint.Cap.ROUND);
        pointPaint.setColor(getResources().getColor(R.color.white));


        sign_in_150 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_50, null);
        sign_in_300 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_100, null);
        sign_in_500 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_200, null);
        sign_in_880 = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_300, null);


        animator = ObjectAnimator.ofFloat(this, "progress", 0.05f, 1);
        animator.setDuration(duration);
        // 执行动画
        animator.start();

        animator2 = ObjectAnimator.ofFloat(this, "progress2", 0, 1);
        animator2.setDuration(duration);

        animator3 = ObjectAnimator.ofFloat(this, "progress3", 0, 1);
        animator3.setDuration(duration);

        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation, boolean isReverse) {

            }

            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                if (signInDay >= 6) {
                    animator2.start();
                }
            }
        });

        animator2.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation, boolean isReverse) {

            }

            @Override
            public void onAnimationEnd(Animator animation, boolean isReverse) {
                if (signInDay >= 6) {
                    animator3.start();
                }
            }
        });

        animator2.addUpdateListener(valueAnimator -> {
            progress2 = (Float) animator2.getAnimatedValue();
            invalidate();
        });

        animator3.addUpdateListener(valueAnimator -> {
            progress3 = (Float) animator3.getAnimatedValue();
            invalidate();
        });


    }

    public void setSignInDay(int signInDay) {
        this.signInDay = signInDay;
        invalidate();
    }

    public int getSignInDay() {
        return signInDay;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        paint.setColor(getResources().getColor(R.color.no_signin));
        Path path = new Path();
        path.moveTo(offSetX, offSetY);

        path.lineTo(endWidth, offSetY);
        path.rLineTo(0, maxHeigh);
        int line = maxLine - offSetX * 2;
        path.rLineTo(-line, 0);
        canvas.drawPath(path, paint);

        paint.setColor(getResources().getColor(R.color.main_color));

        Path path1 = new Path();
        path1.moveTo(offSetX, offSetY);

        int yuliuWidth = (maxLine - startWidth * 2) / 7;   //预留的宽度

        if (signInDay == 1) {
            //第一天
            path1.lineTo(yuliuWidth * progress + startWidth, offSetY);
        } else if (signInDay <= 6) {
            //2-6天
            float x = (yuliuWidth) * (signInDay);
            x = x + 20;
            if (x > maxLine) {
                x = maxLine;
            }
            path1.lineTo(x * progress + startWidth, offSetY);
        } else {
            //六天以后
            path1.lineTo(endWidth * progress, offSetY);
            path1.rLineTo(0, maxHeigh * progress2);
            float v = 0;

            switch (signInDay) {
                case 10:
                    v = yuliuWidth * 7;
                    break;
                case 9:
                    v = yuliuWidth * 4.5f;
                    break;
                case 8:
                    v = yuliuWidth * 2.8f;
                    break;
                case 7:
                    v = yuliuWidth * 1.5f;
                    break;
            }
            path1.rLineTo(-v * progress3, 0);
        }
        canvas.drawPath(path1, paint);

        //画圆点
        for (int i = 0; i < 6; i++) {
            canvas.drawCircle(yuliuWidth * (i + 1) + startWidth, offSetY, circleRadius, pointPaint);
        }
        canvas.drawCircle(yuliuWidth + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
        canvas.drawCircle(yuliuWidth * 2.8f + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
        canvas.drawCircle(yuliuWidth * 4.5f + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);
        canvas.drawCircle(yuliuWidth * 6 + startWidth, offSetY + maxHeigh, circleRadius, pointPaint);


        //画金币图片
        @SuppressLint("DrawAllocation") Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.sign_in_10, null);
        for (int i = 0; i < strMoney.length; i++) {
            canvas.drawBitmap(bitmap, (yuliuWidth * (i + 1) + startWidth) - (bitmap.getWidth() / 2), 40, imgPaint);
        }


        textPaint.setTextSize(CommonUtil.dip2px(12));
        for (int i = 0; i < strMoney.length; i++) {
            textPaint.setColor(getResources().getColor(signInDay > i ? R.color.main_color : R.color.textcolor_aaa));
            //画奖励金额的文字
            canvas.drawText(strMoney[i], yuliuWidth * (i + 1) + startWidth - (textPaint.measureText(strMoney[i]) / 2), bitmap.getHeight() + 90, textPaint);
            //画已签到天数的文字
            canvas.drawText(signInDay > i ? "已签" : strDay[i], yuliuWidth * (i + 1) + startWidth - (textPaint.measureText(strDay[i]) / 2), bitmap.getHeight() + 200, textPaint);
        }

        //底部四张图片与文字
        textPaint.setColor(getResources().getColor(R.color.textcolor_aaa));
        for (int i = 0; i < strMoney2.length; i++) {
            switch (i) {
                case 0:
                    //第30天 奖励880

                    if (signInDay >= 10) {
                        textPaint.setColor(getResources().getColor(R.color.main_color));
                        canvas.drawText("已签", yuliuWidth + startWidth - (textPaint.measureText(strDay[i]) / 2), sign_in_880.getHeight() + maxHeigh + 200, textPaint);
                    } else {
                        //画已签到天数的文字
                        canvas.drawText(strDay2[i], yuliuWidth + startWidth - (textPaint.measureText(strDay[i]) / 2), sign_in_880.getHeight() + maxHeigh + 200, textPaint);
                    }
                    //画出金币图片
                    canvas.drawBitmap(sign_in_880, (yuliuWidth + startWidth) - (sign_in_880.getWidth() / 2), maxHeigh + 46, imgPaint);
                    //画奖励金额的文字
                    canvas.drawText(strMoney2[i], yuliuWidth + startWidth - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);

                    break;
                case 1:
                    //第21天 奖励500
                    if (signInDay >= 9) {
                        textPaint.setColor(getResources().getColor(R.color.main_color));
                        canvas.drawText("已签", (yuliuWidth * 2.8f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_500.getHeight() + maxHeigh + 200, textPaint);
                    } else {
                        canvas.drawText(strDay2[i], (yuliuWidth * 2.8f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_500.getHeight() + maxHeigh + 200, textPaint);
                    }

                    canvas.drawBitmap(sign_in_500, (yuliuWidth * 2.8f + startWidth) - (sign_in_500.getWidth() / 2), maxHeigh + 46, imgPaint);
                    canvas.drawText(strMoney2[i], (yuliuWidth * 2.8f + startWidth) - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);

                    break;
                case 2:
                    //第14天 奖励300
                    if (signInDay >= 8) {
                        textPaint.setColor(getResources().getColor(R.color.main_color));
                        canvas.drawText("已签", (yuliuWidth * 4.5f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_300.getHeight() + maxHeigh + 200, textPaint);
                    } else {
                        canvas.drawText(strDay2[i], (yuliuWidth * 4.5f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_300.getHeight() + maxHeigh + 200, textPaint);
                    }

                    canvas.drawBitmap(sign_in_300, (yuliuWidth * 4.5f + startWidth) - (sign_in_300.getWidth() / 2), maxHeigh + 46, imgPaint);
                    canvas.drawText(strMoney2[i], (yuliuWidth * 4.5f + startWidth) - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);

                    break;
                case 3:
                    //第7天 奖励150
                    if (signInDay >= 7) {
                        textPaint.setColor(getResources().getColor(R.color.main_color));
                        canvas.drawText("已签", (yuliuWidth * 6f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_150.getHeight() + maxHeigh + 200, textPaint);
                    } else {
                        canvas.drawText(strDay2[i], (yuliuWidth * 6f + startWidth) - (textPaint.measureText(strDay[i]) / 2), sign_in_150.getHeight() + maxHeigh + 200, textPaint);
                    }

                    canvas.drawBitmap(sign_in_150, (yuliuWidth * 6 + startWidth) - (sign_in_150.getWidth() / 2), maxHeigh + 46, imgPaint);
                    canvas.drawText(strMoney2[i], (yuliuWidth * 6 + startWidth) - (textPaint.measureText(strMoney2[i]) / 2), maxHeigh + 160, textPaint);

                    break;
            }

        }
    }
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值