周签到任务 View
This commit is contained in:
parent
517004aab3
commit
d4a5b60a6d
|
|
@ -5,6 +5,7 @@ data class DayStatus(
|
||||||
val day: Int, // 第几天 (1-7)
|
val day: Int, // 第几天 (1-7)
|
||||||
val reward: String, // 奖励数值 ("100", "300"等)
|
val reward: String, // 奖励数值 ("100", "300"等)
|
||||||
val isCompleted: Boolean = false, // 是否已完成
|
val isCompleted: Boolean = false, // 是否已完成
|
||||||
|
val isPastDay: Boolean = false,
|
||||||
val isToday: Boolean = false, // 是否是今天
|
val isToday: Boolean = false, // 是否是今天
|
||||||
val icon: String = if (isCompleted) "✓" else "G" // 图标
|
val icon: String = if (isCompleted) "✓" else "G" // 图标
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import com.ama.core.common.util.dp
|
||||||
import com.ama.core.common.util.sp
|
import com.ama.core.common.util.sp
|
||||||
import com.gamedog.vididin.R
|
import com.gamedog.vididin.R
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
|
import com.ama.core.architecture.BaseApp
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class WeekStatusView @JvmOverloads constructor(
|
class WeekStatusView @JvmOverloads constructor(
|
||||||
|
|
@ -16,22 +17,24 @@ class WeekStatusView @JvmOverloads constructor(
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : View(context, attrs, defStyleAttr) {
|
) : View(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
// 默认颜色配置(匹配图片样式)
|
private var currentDay = 1
|
||||||
private var completedColor = Color.parseColor("#4CAF50") // 已完成绿色
|
private var dayStatusList: List<DayStatus> = emptyList()
|
||||||
private var pendingColor = Color.parseColor("#FF9800") // 未完成橙色
|
|
||||||
private var textColor = Color.parseColor("#757575") // 文字灰色
|
|
||||||
private var rewardTextColor = Color.parseColor("#FF9800") // 奖励数值橙色
|
|
||||||
private var todayHighlightColor = Color.parseColor("#4CAF50") // 今天高亮边框颜色
|
|
||||||
|
|
||||||
|
private var completedColor = Color.parseColor("#4CAF50")
|
||||||
|
private var pendingColor = Color.parseColor("#FF9800")
|
||||||
|
private var dayColor = Color.parseColor("#FF999999")
|
||||||
|
private var dayPastColor = Color.parseColor("#FFD4D4D4")
|
||||||
|
private var rewardTextColor = Color.parseColor("#FF9800")
|
||||||
|
private var todayHighlightColor = Color.parseColor("#4CAF50")
|
||||||
private var bgRectColor = Color.parseColor("#ffffedd7")
|
private var bgRectColor = Color.parseColor("#ffffedd7")
|
||||||
|
|
||||||
|
|
||||||
|
private var circleRadius = 46.5F
|
||||||
// 默认尺寸配置
|
private var dayTextSize = 12f.sp.toFloat()
|
||||||
private var circleRadius = 40f.dp.toFloat()
|
|
||||||
private var textSize = 12f.sp.toFloat()
|
|
||||||
private var rewardTextSize = 14f.sp.toFloat()
|
private var rewardTextSize = 14f.sp.toFloat()
|
||||||
private var itemSpacing = 16f.dp.toFloat()
|
private var HoriMagin = 5F.dp.toFloat()
|
||||||
|
private var vertiMagin = 5F.dp.toFloat()
|
||||||
|
private var componentGap = 15F.dp.toFloat()
|
||||||
private var todayStrokeWidth = 3f.dp.toFloat()
|
private var todayStrokeWidth = 3f.dp.toFloat()
|
||||||
private var bgRectHeight = 10.dp.toFloat()
|
private var bgRectHeight = 10.dp.toFloat()
|
||||||
|
|
||||||
|
|
@ -49,16 +52,10 @@ class WeekStatusView @JvmOverloads constructor(
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
textAlign = Paint.Align.CENTER
|
textAlign = Paint.Align.CENTER
|
||||||
}
|
}
|
||||||
private val todayHighlightPaint = Paint().apply {
|
|
||||||
isAntiAlias = true
|
|
||||||
style = Paint.Style.STROKE
|
|
||||||
color = todayHighlightColor
|
|
||||||
strokeWidth = todayStrokeWidth.toFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据
|
|
||||||
private var dayStatusList: List<DayStatus> = emptyList()
|
|
||||||
private var currentDay = 1
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setupAttributes(attrs)
|
setupAttributes(attrs)
|
||||||
|
|
@ -70,19 +67,16 @@ class WeekStatusView @JvmOverloads constructor(
|
||||||
|
|
||||||
completedColor = typedArray.getColor(R.styleable.WeekStatusView_completedColor, completedColor)
|
completedColor = typedArray.getColor(R.styleable.WeekStatusView_completedColor, completedColor)
|
||||||
pendingColor = typedArray.getColor(R.styleable.WeekStatusView_pendingColor, pendingColor)
|
pendingColor = typedArray.getColor(R.styleable.WeekStatusView_pendingColor, pendingColor)
|
||||||
textColor = typedArray.getColor(R.styleable.WeekStatusView_textColor, textColor)
|
dayColor = typedArray.getColor(R.styleable.WeekStatusView_textColor, dayColor)
|
||||||
rewardTextColor = typedArray.getColor(R.styleable.WeekStatusView_rewardTextColor, rewardTextColor)
|
rewardTextColor = typedArray.getColor(R.styleable.WeekStatusView_rewardTextColor, rewardTextColor)
|
||||||
todayHighlightColor = typedArray.getColor(R.styleable.WeekStatusView_todayHighlightColor, todayHighlightColor)
|
todayHighlightColor = typedArray.getColor(R.styleable.WeekStatusView_todayHighlightColor, todayHighlightColor)
|
||||||
|
|
||||||
circleRadius = typedArray.getDimension(R.styleable.WeekStatusView_circleRadius, circleRadius)
|
circleRadius = typedArray.getDimension(R.styleable.WeekStatusView_circleRadius, circleRadius)
|
||||||
textSize = typedArray.getDimension(R.styleable.WeekStatusView_textSize, textSize)
|
dayTextSize = typedArray.getDimension(R.styleable.WeekStatusView_textSize, dayTextSize)
|
||||||
rewardTextSize = typedArray.getDimension(R.styleable.WeekStatusView_rewardTextSize, rewardTextSize)
|
rewardTextSize = typedArray.getDimension(R.styleable.WeekStatusView_rewardTextSize, rewardTextSize)
|
||||||
itemSpacing = typedArray.getDimension(R.styleable.WeekStatusView_itemSpacing, itemSpacing)
|
vertiMagin = typedArray.getDimension(R.styleable.WeekStatusView_itemSpacing, vertiMagin)
|
||||||
todayStrokeWidth = typedArray.getDimension(R.styleable.WeekStatusView_todayStrokeWidth, todayStrokeWidth)
|
todayStrokeWidth = typedArray.getDimension(R.styleable.WeekStatusView_todayStrokeWidth, todayStrokeWidth)
|
||||||
|
|
||||||
todayHighlightPaint.strokeWidth = todayStrokeWidth
|
|
||||||
todayHighlightPaint.color = todayHighlightColor
|
|
||||||
|
|
||||||
typedArray.recycle()
|
typedArray.recycle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +87,7 @@ class WeekStatusView @JvmOverloads constructor(
|
||||||
|
|
||||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||||
val desiredWidth = MeasureSpec.getSize(widthMeasureSpec)
|
val desiredWidth = MeasureSpec.getSize(widthMeasureSpec)
|
||||||
val desiredHeight = (circleRadius * 2 + textSize * 3 + itemSpacing * 2).toInt()
|
val desiredHeight = (circleRadius * 2 + dayTextSize + rewardTextSize + vertiMagin * 2 + 2*componentGap).toInt()
|
||||||
|
|
||||||
setMeasuredDimension(
|
setMeasuredDimension(
|
||||||
resolveSize(desiredWidth, widthMeasureSpec),
|
resolveSize(desiredWidth, widthMeasureSpec),
|
||||||
|
|
@ -103,95 +97,71 @@ class WeekStatusView @JvmOverloads constructor(
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas) {
|
override fun onDraw(canvas: Canvas) {
|
||||||
super.onDraw(canvas)
|
super.onDraw(canvas)
|
||||||
|
|
||||||
if (dayStatusList.isEmpty()) return
|
if (dayStatusList.isEmpty()) return
|
||||||
|
|
||||||
val totalWidth = measuredWidth.toFloat()
|
val totalWidth = measuredWidth.toFloat()
|
||||||
val totalHeight = measuredHeight.toFloat()
|
val totalItemCount = dayStatusList.size
|
||||||
val totalItems = dayStatusList.size
|
val itemHoriGapPixel = (totalWidth - 2 * circleRadius - 2 * HoriMagin) / (totalItemCount - 1)
|
||||||
val availableWidth = totalWidth - (itemSpacing * (totalItems - 1))
|
|
||||||
val itemWidth = availableWidth / totalItems
|
|
||||||
|
|
||||||
// bg
|
// bg rect
|
||||||
canvas.drawRect(itemWidth/2, (totalHeight - bgRectHeight)/2, totalWidth - itemWidth/2, totalHeight - (totalHeight - bgRectHeight)/2, bgPaint)
|
val bgTop = vertiMagin + dayTextSize + componentGap + (circleRadius - bgRectHeight/2)
|
||||||
|
canvas.drawRect(circleRadius + HoriMagin, bgTop,
|
||||||
|
totalWidth - circleRadius - HoriMagin, bgTop + bgRectHeight, bgPaint)
|
||||||
|
|
||||||
dayStatusList.forEachIndexed { index, dayStatus ->
|
dayStatusList.forEachIndexed { index, dayStatus ->
|
||||||
val centerX = itemWidth / 2 + index * (itemWidth + itemSpacing)
|
val centerX = HoriMagin + circleRadius + itemHoriGapPixel * index
|
||||||
drawDayStatus(canvas, dayStatus, centerX)
|
drawDayStatusItem(canvas, dayStatus, centerX)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawDayStatus(canvas: Canvas, dayStatus: DayStatus, centerX: Float) {
|
private fun drawDayStatusItem(canvas: Canvas, dayStatus: DayStatus, centerX: Float) {
|
||||||
val centerY = height / 2f
|
// day text 'dia'
|
||||||
val circleCenterY = centerY - circleRadius
|
textPaint.color = if (dayStatus.isCompleted) dayPastColor else dayColor
|
||||||
|
textPaint.textSize = dayTextSize
|
||||||
|
canvas.drawText(dayStatus.displayText, centerX, vertiMagin + dayTextSize/2, textPaint)
|
||||||
|
|
||||||
// 绘制圆形背景
|
// icons
|
||||||
circlePaint.color = if (dayStatus.isCompleted) completedColor else pendingColor
|
val iconRes = if (dayStatus.isCompleted) R.mipmap.task_week_view_done else R.mipmap.task_week_view_ongoing
|
||||||
canvas.drawCircle(centerX, circleCenterY, circleRadius, circlePaint)
|
canvas.drawBitmap(getBitmap(iconRes)!!, centerX - circleRadius, vertiMagin + dayTextSize + componentGap, circlePaint)
|
||||||
|
|
||||||
// 绘制图标(对勾或G)
|
// reward text
|
||||||
textPaint.color = Color.WHITE
|
|
||||||
textPaint.textSize = circleRadius * 0.6f
|
|
||||||
val iconBounds = Rect()
|
|
||||||
textPaint.getTextBounds(dayStatus.icon, 0, dayStatus.icon.length, iconBounds)
|
|
||||||
val iconY = circleCenterY + (iconBounds.height() / 2) - iconBounds.bottom
|
|
||||||
canvas.drawText(dayStatus.icon, centerX, iconY, textPaint)
|
|
||||||
|
|
||||||
// 绘制天数文字 (Dia X)
|
|
||||||
textPaint.color = textColor
|
|
||||||
textPaint.textSize = textSize
|
|
||||||
canvas.drawText(dayStatus.displayText, centerX, circleCenterY + circleRadius + textSize + 5, textPaint)
|
|
||||||
|
|
||||||
// 绘制奖励数值
|
|
||||||
textPaint.color = rewardTextColor
|
textPaint.color = rewardTextColor
|
||||||
textPaint.textSize = rewardTextSize
|
textPaint.textSize = rewardTextSize
|
||||||
canvas.drawText(dayStatus.reward, centerX, circleCenterY + circleRadius + textSize + rewardTextSize + 15, textPaint)
|
canvas.drawText(dayStatus.reward, centerX, vertiMagin + dayTextSize + componentGap + 2*circleRadius + componentGap, textPaint)
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是今天,绘制高亮边框
|
fun getBitmap(resId: Int): Bitmap? {
|
||||||
if (dayStatus.isToday) {
|
return try {
|
||||||
canvas.drawCircle(centerX, circleCenterY, circleRadius + 2, todayHighlightPaint)
|
BitmapFactory.decodeResource(BaseApp.appContext().resources, resId)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 公开API方法 ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新显示的数据
|
// --------------------- public ---------------------
|
||||||
* @param data 新的天数状态列表
|
|
||||||
*/
|
|
||||||
fun updateData(data: List<DayStatus>) {
|
fun updateData(data: List<DayStatus>) {
|
||||||
dayStatusList = data
|
dayStatusList = data
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据目标日期更新状态(自动计算完成状态)
|
|
||||||
* @param targetDate 目标日期
|
|
||||||
*/
|
|
||||||
fun updateForDate(targetDate: Date) {
|
fun updateForDate(targetDate: Date) {
|
||||||
val targetDay = getDayOfWeekFromDate(targetDate)
|
val targetDay = getDayOfWeekFromDate(targetDate)
|
||||||
dayStatusList = generateWeekData(targetDay)
|
dayStatusList = generateWeekData(targetDay)
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 刷新为当前日期
|
|
||||||
*/
|
|
||||||
fun refreshToCurrentDate() {
|
fun refreshToCurrentDate() {
|
||||||
updateForDate(Date())
|
updateForDate(Date())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前显示的数据
|
|
||||||
*/
|
|
||||||
fun getCurrentData(): List<DayStatus> = dayStatusList
|
fun getCurrentData(): List<DayStatus> = dayStatusList
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取当前高亮的天数(今天)
|
|
||||||
*/
|
|
||||||
fun getCurrentDay(): Int = currentDay
|
fun getCurrentDay(): Int = currentDay
|
||||||
|
|
||||||
// ==================== 工具方法 ====================
|
|
||||||
private fun generateWeekData(targetDay: Int): List<DayStatus> {
|
private fun generateWeekData(targetDay: Int): List<DayStatus> {
|
||||||
val rewards = listOf("100", "300", "300", "500", "300", "300", "800")
|
val rewards = listOf("100", "300", "300", "500", "300", "300", "800")
|
||||||
|
|
||||||
|
|
@ -216,8 +186,6 @@ class WeekStatusView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertCalendarDayToWeekDay(calendarDay: Int): Int {
|
private fun convertCalendarDayToWeekDay(calendarDay: Int): Int {
|
||||||
// Calendar: 周日=1, 周一=2, ..., 周六=7
|
|
||||||
// 转换为: 周一=1, 周二=2, ..., 周日=7
|
|
||||||
return if (calendarDay == 1) 7 else calendarDay - 1
|
return if (calendarDay == 1) 7 else calendarDay - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -346,14 +346,14 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
android:background="@drawable/vididinapp_feature_message_bg_task_login"
|
||||||
android:layout_marginTop="10dp">
|
android:layout_marginTop="10dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="10dp"
|
android:padding="10dp">
|
||||||
android:background="@drawable/vididinapp_feature_message_bg_task_login">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatImageView
|
<androidx.appcompat.widget.AppCompatImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|
@ -379,15 +379,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="15dp"
|
android:layout_marginHorizontal="15dp"
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
app:completedColor="#FF4CAF50"
|
/>
|
||||||
app:pendingColor="#FFFF9800"
|
|
||||||
app:textColor="#FF757575"
|
|
||||||
app:rewardTextColor="#FFFF9800"
|
|
||||||
app:todayHighlightColor="#FF4CAF50"
|
|
||||||
app:circleRadius="20dp"
|
|
||||||
app:textSize="12sp"
|
|
||||||
app:rewardTextSize="14sp"
|
|
||||||
app:itemSpacing="8dp" />
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatTextView
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
Loading…
Reference in New Issue