Android 实现签到足迹功能

作者:xiaolei123 时间:2023-06-21 15:16:24 

UI 妹纸又给了个图叫我做,我一看是这样的:

Android 实现签到足迹功能

我们首先把这个控件划分成 几个部分:

1.底下部分的直线 :

2.左右两边的半圆弧度 :

3.线上面的小图标 :

4.最后的文字说明 :

首先我们把线画出来,大概这个样子

Android 实现签到足迹功能

我们这里根据一个月得总天数,和一条线上需要画七个图,计算出总共需要画出的线条数,以及画出左边和右边的弧度,根据当前线是单数还是双数,来计算出是否是左半边的弧度,还是右半边的弧度,以及是否是最后的一条线,因为最后一条线不需要画弧度。

代码如下:


   @Override
   protected void onDraw(Canvas canvas)
{
       paint.setColor(backColor);
       paint.setStrokeWidth(strokeWidth);
       int rowCount = (monthDays % 7 == 0 ? monthDays / 7 : monthDays / 7 + 1);
       int rowHeigh = height / (rowCount);
       int startX = 0 + rowHeigh / 2;
       int endX = width - rowHeigh / 2;
       int days = 0;

for (int a = 0; a < rowCount; a++)
       {
           if (a + 1 == rowCount)
           {
               endX = (endX - startX) / 7 * (monthDays % 7) + checkBitmap.getWidth() / 2;
           }
           paint.setStrokeWidth(strokeWidth);
           int y = rowHeigh * a + rowHeigh / 2;
           canvas.drawLine(startX, y, endX, y, paint);

paint.setColor(rashColor);
           paint.setStrokeWidth(1);
           canvas.drawLine(startX, y, endX, y, paint);
           // 这里是来判断,是否需要画出左半边还是右半边的半圆弧度?
           if (a % 2 != 0)
           {
               if (a + 1 != rowCount)
               {
                   drawLeftOrRightArc(true, canvas, 0 + strokeWidth, y, 0 + rowHeigh + strokeWidth, y + rowHeigh);
               }
           } else
           {
               if (a + 1 != rowCount)
               {
                   drawLeftOrRightArc(false, canvas, endX - rowHeigh / 2 - strokeWidth, y, endX + rowHeigh / 2 - strokeWidth, y + rowHeigh);
               }
           }
       }
   }

然后再在线上画出礼物数量


           // 这里是来判断,本次这根线上画出的礼物的点,以及顺序是顺画,还是倒画出。
           bitmapList.clear();
           for (int b = 0; b < (a + 1 == rowCount ? (monthDays % 7) : 7); b++)
           {
               days++;
               if (days <= signInCount)
               {
                   if (days == 3 || days == 8 || days == 14 || days == 21 || days == monthDays)
                   {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), openGiftBitmap);
                   } else
                   {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), checkBitmap);
                   }
               } else
               {
                   if (days == 3 || days == 8 || days == 14 || days == 21 || days == monthDays)
                   {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), closeGiftBitmap);
                   } else
                   {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), uncheckBitmap);
                   }
               }
           }

这里有一个需要注意的地方,就是,在线为双数的时候,这时候礼物的排列是需要反过来排列的,我这里使用了一个LinkedList来保存礼物的排列顺序,然后我们通过计算平均数,计算出每个礼物的位置。


   /**
    * 画出的按路线上的图片,勾选,礼物
    * @param bitmapList
    * @param startX
    * @param endX
    * @param y
    * @param canvas
    */
   private void drawImgs(List<Bitmap> bitmapList, float startX, float endX, float y, Canvas canvas)
{
       startX = startX - bitmapList.get(0).getWidth() / 2;
       int count = bitmapList.size();
       float bitmap_width = (endX - startX) / (count - 1);
       for (int a = 0; a < count; a++)
       {
           Bitmap bitmap = bitmapList.get(a);
           canvas.drawBitmap(bitmap, startX + (bitmap_width * a), y - bitmap.getHeight() / 2, paint);
       }
   }

这里也有一个需要注意的地方,就是,当最后一条线是短的时候,这个时候,你的礼物的排列需要按照那条线的开始位置和结束位置来平均计算每个礼物的位置。

最后,我们在最后一条线最后的位置,画出文字


/**
    * 画出文字
    * @param canvas
    * @param y
    * @param x
    */
   private void drawText(Canvas canvas, float y, float x)
{
       int oldColor = paint.getColor();
       Paint.Style old_style = paint.getStyle();

paint.setStyle(Paint.Style.FILL);
       paint.setColor(textColor);
       String drawText = "已累计签到"+signInCount+"天";
       paint.setTextSize(DensityUtil.sp2px(getContext(), 15));
       int textHeigh = getStringHeight(drawText);
       int textWidth = getStringWidth(drawText);
       canvas.drawText(drawText, x + textWidth/2, y + textHeigh / 2, paint);

paint.setColor(oldColor);
       paint.setStyle(old_style);
   }

Android 实现签到足迹功能

好了,这就是所有的思路。下面贴一下最新完整代码:


package com.sjl.keeplive.track;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

import com.sjl.keeplive.R;

import java.util.LinkedList;
import java.util.List;

public class SignInView extends View {
   private int width, height;
   private int monthDays = 31;//本月有31天
   private Paint paint;
   private RectF oval = new RectF();
   private float strokeWidth = 10;
   private Bitmap checkBitmap, uncheckBitmap, closeGiftBitmap, openGiftBitmap;
   private int backColor = Color.parseColor("#C3DEEA"),
           rashColor = Color.parseColor("#B2CADB"),
           textColor = Color.parseColor("#60ADE5");
   private List<Bitmap> bitmapList = new LinkedList<>();
   private int signInCount = 9;

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

public SignInView(Context context, @Nullable AttributeSet attrs) {
       this(context, attrs, 0);
   }

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

private void init(Context context, AttributeSet attrs) {
       paint = new Paint();
       paint.setAntiAlias(true);
       strokeWidth = DensityUtil.dip2px(6);
       checkBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_sign_in_check_img);
       uncheckBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_sign_in_uncheck_img);
       closeGiftBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_close_gift_img);
       openGiftBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon_open_gift_img);
   }

@Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       height = MeasureSpec.getSize(heightMeasureSpec);
       width = MeasureSpec.getSize(widthMeasureSpec);
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
   }

/**
    * 设置本月天数
    *
    * @param monthDays
    */
   public void setMonthDays(int monthDays) {
       this.monthDays = monthDays;
       if (monthDays == 0) {
           this.monthDays = 31;
       }
       postInvalidate();
   }

/**
    * 设置一共签到了几天
    *
    * @param days
    */
   public void setProgress(int days) {
       this.signInCount = days;
       postInvalidate();
   }

@Override
   protected void onDraw(Canvas canvas) {
       paint.setColor(backColor);
       paint.setStrokeWidth(strokeWidth);
       int rowCount = (monthDays % 7 == 0 ? monthDays / 7 : monthDays / 7 + 1);
       int rowHeigh = height / (rowCount);
       int startX = 0 + rowHeigh / 2;
       int endX = width - rowHeigh / 2;
       int days = 0;

for (int a = 0; a < rowCount; a++) {
           if (a + 1 == rowCount) {
               endX = (endX - startX) / 7 * (monthDays % 7 == 0 ? 7 : (monthDays % 7)) + checkBitmap.getWidth() / 2;
           }
           paint.setStrokeWidth(strokeWidth);
           int y = rowHeigh * a + rowHeigh / 2;
           canvas.drawLine(startX, y, endX, y, paint);

paint.setColor(rashColor);
           paint.setStrokeWidth(1);
           canvas.drawLine(startX, y, endX, y, paint);
           // 这里是来判断,是否需要画出左半边还是右半边的半圆弧度?
           if (a % 2 != 0) {
               if (a + 1 != rowCount) {
                   drawLeftOrRightArc(true, canvas, 0 + strokeWidth, y, 0 + rowHeigh + strokeWidth, y + rowHeigh);
               }
           } else {
               if (a + 1 != rowCount) {
                   drawLeftOrRightArc(false, canvas, endX - rowHeigh / 2 - strokeWidth, y, endX + rowHeigh / 2 - strokeWidth, y + rowHeigh);
               }
           }

// 这里是来判断,本次这根线上画出的礼物的点,以及顺序是顺画,还是倒画出。
           bitmapList.clear();
           int lastDay = (monthDays % 7) == 0 ? 7 : (monthDays % 7);
           for (int b = 0; b < (a + 1 == rowCount ? (lastDay) : 7); b++) {
               days++;
               if (days <= signInCount) {
                   if (days == 3 || days == 8 || days == 14 || days == 21 || days == monthDays) {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), openGiftBitmap);
                   } else {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), checkBitmap);
                   }
               } else {
                   if (days == 3 || days == 8 || days == 14 || days == 21 || days == monthDays) {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), closeGiftBitmap);
                   } else {
                       bitmapList.add(a % 2 != 0 ? 0 : bitmapList.size(), uncheckBitmap);
                   }
               }
           }

drawImgs(bitmapList, startX, endX, y, canvas);
       }
       super.onDraw(canvas);
   }

/**
    * 画出的按路线上的图片,勾选,礼物
    *
    * @param bitmapList
    * @param startX
    * @param endX
    * @param y
    * @param canvas
    */
   private void drawImgs(List<Bitmap> bitmapList, float startX, float endX, float y, Canvas canvas) {
       if (!bitmapList.isEmpty()) {
           startX = startX - bitmapList.get(0).getWidth() / 2;
           int count = bitmapList.size();
           float bitmap_width = (endX - startX) / (count - 1);
           for (int a = 0; a < count; a++) {
               Bitmap bitmap = bitmapList.get(a);
               canvas.drawBitmap(bitmap, startX + (bitmap_width * a), y - bitmap.getHeight() / 2, paint);
           }
       }
   }

/**
    * 这里画出左边半圆弧,还是右边半圆弧
    *
    * @param isLeft
    * @param canvas
    * @param left
    * @param top
    * @param right
    * @param bottom
    */
   private void drawLeftOrRightArc(boolean isLeft, Canvas canvas, float left, float top, float right, float bottom) {
       paint.setStrokeWidth(strokeWidth);
       paint.setColor(backColor);

if (isLeft) {
           paint.setStyle(Paint.Style.STROKE);
           oval.setEmpty();
           oval.set(left, top, right, bottom);
           canvas.drawArc(oval, 90, 180, false, paint);
           paint.setStrokeWidth(1);
           paint.setColor(rashColor);
           canvas.drawArc(oval, 90, 180, false, paint);
       } else {
           paint.setStyle(Paint.Style.STROKE);
           oval.setEmpty();
           oval.set(left, top, right, bottom);
           canvas.drawArc(oval, 270, 180, false, paint);

paint.setStrokeWidth(1);
           paint.setColor(rashColor);

canvas.drawArc(oval, 270, 180, false, paint);
       }
       paint.setStrokeWidth(strokeWidth);
       paint.setColor(backColor);
   }
}

布局文件使用:


<com.sjl.keeplive.track.SignInView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:visibility="visible"/>

项目地址:

链接:https://pan.baidu.com/s/1IUh9og2T3IlxeXhaLLOKGg
提取码:thoc

由于demo集合比较多,单这篇看下面代码即可:

Android 实现签到足迹功能

来源:https://mp.weixin.qq.com/s/NXebpysf2rUZcNC3_uT7fA

标签:Android,签到
0
投稿

猜你喜欢

  • Swift洗牌动画效果的实现方法

    2023-06-21 14:01:56
  • Flutter路由传递参数及解析实现

    2023-06-22 11:48:45
  • android中使用SharedPreferences进行数据存储的操作方法

    2023-06-16 17:37:42
  • Flutter瀑布流仿写原生的复用机制详解

    2023-06-20 17:02:08
  • JSON.toJSONString()空字段不忽略修改的问题

    2023-06-16 03:12:37
  • Java进阶:Struts多模块的技巧

    2023-06-18 09:40:47
  • C#创建临时文件的方法

    2023-06-16 14:32:36
  • opencv3/C++图像滤波实现方式

    2023-06-23 15:37:08
  • Unity打开淘宝app并跳转到商品页面功能的实现方法

    2023-06-17 01:05:18
  • 使用C#发送Http请求实现模拟登陆实例

    2023-06-22 22:25:07
  • C#实现简单俄罗斯方块

    2023-06-18 07:18:36
  • 输出的文本实现对齐的方法(超简单)

    2023-06-19 03:57:09
  • Flutter模仿实现微信底部导航栏流程详解

    2023-06-21 11:46:12
  • springsecurity 企业微信登入的实现示例

    2023-06-16 16:39:35
  • Winform 实现进度条弹窗和任务控制

    2023-06-20 04:27:09
  • Flutter实现抽屉动画

    2023-06-18 01:49:19
  • OpenCV实现直线拟合

    2023-06-22 15:22:37
  • C#中使用split分割字符串的几种方法小结

    2023-06-18 17:23:21
  • Flutter 如何正确显示SnackBar

    2023-06-23 13:00:40
  • Spring Cloud中FeignClient实现文件上传功能

    2023-06-23 07:57:09
  • asp之家 软件编程 m.aspxhome.com