Android 實(shí)現(xiàn)簽到足跡功能
UI 妹紙又給了個(gè)圖叫我做,我一看是這樣的:
我們首先把這個(gè)控件劃分成 幾個(gè)部分:
1.底下部分的直線 :
2.左右兩邊的半圓弧度 :
3.線上面的小圖標(biāo) :
4.最后的文字說(shuō)明 :
首先我們把線畫(huà)出來(lái),大概這個(gè)樣子
我們這里根據(jù)一個(gè)月得總天數(shù),和一條線上需要畫(huà)七個(gè)圖,計(jì)算出總共需要畫(huà)出的線條數(shù),以及畫(huà)出左邊和右邊的弧度,根據(jù)當(dāng)前線是單數(shù)還是雙數(shù),來(lái)計(jì)算出是否是左半邊的弧度,還是右半邊的弧度,以及是否是最后的一條線,因?yàn)樽詈笠粭l線不需要畫(huà)弧度。
代碼如下:
@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); // 這里是來(lái)判斷,是否需要畫(huà)出左半邊還是右半邊的半圓弧度? 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); } } } }
然后再在線上畫(huà)出禮物數(shù)量
// 這里是來(lái)判斷,本次這根線上畫(huà)出的禮物的點(diǎn),以及順序是順畫(huà),還是倒畫(huà)出。 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); } } }
這里有一個(gè)需要注意的地方,就是,在線為雙數(shù)的時(shí)候,這時(shí)候禮物的排列是需要反過(guò)來(lái)排列的,我這里使用了一個(gè)LinkedList來(lái)保存禮物的排列順序,然后我們通過(guò)計(jì)算平均數(shù),計(jì)算出每個(gè)禮物的位置。
/** * 畫(huà)出的按路線上的圖片,勾選,禮物 * @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); } }
這里也有一個(gè)需要注意的地方,就是,當(dāng)最后一條線是短的時(shí)候,這個(gè)時(shí)候,你的禮物的排列需要按照那條線的開(kāi)始位置和結(jié)束位置來(lái)平均計(jì)算每個(gè)禮物的位置。
最后,我們?cè)谧詈笠粭l線最后的位置,畫(huà)出文字
/** * 畫(huà)出文字 * @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 = "已累計(jì)簽到"+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); } /** * 設(shè)置本月天數(shù) * * @param monthDays */ public void setMonthDays(int monthDays) { this.monthDays = monthDays; if (monthDays == 0) { this.monthDays = 31; } postInvalidate(); } /** * 設(shè)置一共簽到了幾天 * * @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); // 這里是來(lái)判斷,是否需要畫(huà)出左半邊還是右半邊的半圓弧度? 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); } } // 這里是來(lái)判斷,本次這根線上畫(huà)出的禮物的點(diǎn),以及順序是順畫(huà),還是倒畫(huà)出。 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); } /** * 畫(huà)出的按路線上的圖片,勾選,禮物 * * @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); } } } /** * 這里畫(huà)出左邊半圓弧,還是右邊半圓弧 * * @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"/>
項(xiàng)目地址:
鏈接:https://pan.baidu.com/s/1IUh9og2T3IlxeXhaLLOKGg
提取碼:thoc
由于demo集合比較多,單這篇看下面代碼即可:
以上就是Android 實(shí)現(xiàn)簽到足跡功能的詳細(xì)內(nèi)容,更多關(guān)于Android 簽到功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android快速開(kāi)發(fā)系列 10個(gè)常用工具類實(shí)例代碼詳解
今天特此整理出10個(gè)基本每個(gè)項(xiàng)目中都會(huì)使用的工具類,用于快速開(kāi)發(fā),對(duì)android開(kāi)發(fā)常用工具類感興趣的朋友跟隨小編一起看看吧2018-09-09android AlertDialog的簡(jiǎn)單使用實(shí)例
本篇文章主要介紹了android AlertDialog的簡(jiǎn)單使用實(shí)例,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-01-01Android Canvas方法總結(jié)最全面詳解API(小結(jié))
本篇文章主要介紹了Android Canvas方法總結(jié)最全面詳解API(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11Flutter質(zhì)感設(shè)計(jì)之表單輸入
這篇文章主要為大家詳細(xì)介紹了Flutter質(zhì)感設(shè)計(jì)之表單輸入,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08Android aapt自動(dòng)打包工具詳細(xì)介紹
這篇文章主要介紹了Android aapt自動(dòng)打包工具詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2016-12-12深入解讀Android的Volley庫(kù)的功能結(jié)構(gòu)
這篇文章主要介紹了Android的Volley開(kāi)發(fā)框架的功能結(jié)構(gòu),Volley是Android開(kāi)發(fā)中網(wǎng)絡(luò)部分的一大利器,包含很多HTTP協(xié)議通信的相關(guān)操作,需要的朋友可以參考下2016-05-05