Android 实现签到足迹功能
作者:xiaolei123 发布时间:2023-06-21 15:16:24
标签:Android,签到
UI 妹纸又给了个图叫我做,我一看是这样的:
我们首先把这个控件划分成 几个部分:
1.底下部分的直线 :
2.左右两边的半圆弧度 :
3.线上面的小图标 :
4.最后的文字说明 :
首先我们把线画出来,大概这个样子
我们这里根据一个月得总天数,和一条线上需要画七个图,计算出总共需要画出的线条数,以及画出左边和右边的弧度,根据当前线是单数还是双数,来计算出是否是左半边的弧度,还是右半边的弧度,以及是否是最后的一条线,因为最后一条线不需要画弧度。
代码如下:
@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);
}
好了,这就是所有的思路。下面贴一下最新完整代码:
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集合比较多,单这篇看下面代码即可:
来源:https://mp.weixin.qq.com/s/NXebpysf2rUZcNC3_uT7fA


猜你喜欢
- 第一种方法:string s=abcdeabcdeabcde;string[] sArray=s.Split(c) ;foreach(str
- android studio 新建工程报错Error:Could not resolve all files for configurati
- 本文实例讲述了java数据结构与算法之冒泡排序。分享给大家供大家参考,具体如下:前面文章讲述的排序算法都是基于插入类的排序,这篇文章开始介绍
- 本文介绍了JAVA 根据设置的概率生成随机数的方法,分享给大家import java.util.ArrayList;import java.
- Java Json的各种处理一、net.sf.json1、Json转MapJSONObject jsonObject = JSONObjec
- 本文实例为大家分享了springboot实现异步任务的具体代码,供大家参考,具体内容如下1.什么异步任务同步:一定要等任务执行完了,得到结果
- 目录前言线程基础1、创建线程2、暂停线程3、线程等待4、线程终止C#中的lock关键字总结前言最近由于工作的需要,一直在使用C#的多线程进行
- 在上篇文章给大家介绍了WebService教程详解(一)使用工具的原因:1、 使用工具可以更好的了解WebService请求的过程 2、 使
- 当我们在做前后端分离的开发时,在使用fetch交换数据的时候,提示Access-Control-Allow-Origin跨域问题,解决方案跟
- 三个例子 —JAVA发送http get/post请求,调用http接口、方法例1:使用 HttpClient (commons-httpc
- 前言LayoutInflater在开发中使用频率很高,但是一直没有太知道LayoutInflater.from(context).infla
- 在上篇文章给大家介绍了Spring boot + mybatis + Vue.js + ElementUI 实现数据的增删改查实例代码(一)
- 一、递归的思路一个方法在执行时,调用自身被称为“递归”。递归相当于数学归纳法,有一个起始条件,有一个递推公式。递归可以分为:单路递归和多路递
- /// <summary> /// 将日期字
- 本文实例为大家分享了Unity实现俄罗斯方块游戏的具体代码,供大家参考,具体内容如下一、演示二、实现思路创建每一个方块可移动到的位置点,可以
- 一、JAVA简要概述先说一下java之父,詹姆斯·高斯林这是一个爱喝咖啡而又强大的男人。再来看一下JAVA有多火在TIOBE排行榜上JAVA
- 定义:当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。特
- 简介#要说Java中什么异常最容易出现,我想NullPointerException一定当仁不让,为了解决这种null值判断问题,Java8
- 前言想在锁屏上面实现弹窗,第一个想法就是利用 WindowManager 设置 Window 的 Flag,通过设置 Flag 的显示优先级
- Android 打开相册选择单张图片实现代码