From 517004aab3e257b4d56de5ba472008f841020364 Mon Sep 17 00:00:00 2001 From: renhaoting <370797079@qq.com> Date: Wed, 19 Nov 2025 16:41:32 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AD=BE=E5=88=B0=E6=97=A5=E5=8E=86=E5=88=9D?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vididin/main/fragments/task/DayStatus.kt | 12 + .../main/fragments/task/WeekStatusView.kt | 224 ++++++++++++++++++ ...inapp_feature_message_fragment_message.xml | 16 ++ app/src/main/res/values/attrs.xml | 18 ++ .../ama/core/common/widget/PopMenuIconView.kt | 11 +- 5 files changed, 274 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/gamedog/vididin/main/fragments/task/DayStatus.kt create mode 100644 app/src/main/java/com/gamedog/vididin/main/fragments/task/WeekStatusView.kt create mode 100644 app/src/main/res/values/attrs.xml diff --git a/app/src/main/java/com/gamedog/vididin/main/fragments/task/DayStatus.kt b/app/src/main/java/com/gamedog/vididin/main/fragments/task/DayStatus.kt new file mode 100644 index 0000000..5b5074a --- /dev/null +++ b/app/src/main/java/com/gamedog/vididin/main/fragments/task/DayStatus.kt @@ -0,0 +1,12 @@ +package com.gamedog.vididin.main.fragments.task + +// DayStatus.kt +data class DayStatus( + val day: Int, // 第几天 (1-7) + val reward: String, // 奖励数值 ("100", "300"等) + val isCompleted: Boolean = false, // 是否已完成 + val isToday: Boolean = false, // 是否是今天 + val icon: String = if (isCompleted) "✓" else "G" // 图标 +) { + val displayText: String get() = "Dia $day" +} \ No newline at end of file diff --git a/app/src/main/java/com/gamedog/vididin/main/fragments/task/WeekStatusView.kt b/app/src/main/java/com/gamedog/vididin/main/fragments/task/WeekStatusView.kt new file mode 100644 index 0000000..ffa3ebf --- /dev/null +++ b/app/src/main/java/com/gamedog/vididin/main/fragments/task/WeekStatusView.kt @@ -0,0 +1,224 @@ +package com.gamedog.vididin.main.fragments.task + +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.view.View +import com.ama.core.common.util.dp +import com.ama.core.common.util.sp +import com.gamedog.vididin.R +import android.graphics.* +import java.util.* + +class WeekStatusView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { + + // 默认颜色配置(匹配图片样式) + private var completedColor = Color.parseColor("#4CAF50") // 已完成绿色 + 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 bgRectColor = Color.parseColor("#ffffedd7") + + + + // 默认尺寸配置 + private var circleRadius = 40f.dp.toFloat() + private var textSize = 12f.sp.toFloat() + private var rewardTextSize = 14f.sp.toFloat() + private var itemSpacing = 16f.dp.toFloat() + private var todayStrokeWidth = 3f.dp.toFloat() + private var bgRectHeight = 10.dp.toFloat() + + // Paints + private val bgPaint = Paint().apply { + isAntiAlias = true + style = Paint.Style.FILL + color = bgRectColor + } + private val circlePaint = Paint().apply { + isAntiAlias = true + style = Paint.Style.FILL + } + private val textPaint = Paint().apply { + isAntiAlias = true + textAlign = Paint.Align.CENTER + } + private val todayHighlightPaint = Paint().apply { + isAntiAlias = true + style = Paint.Style.STROKE + color = todayHighlightColor + strokeWidth = todayStrokeWidth.toFloat() + } + + // 数据 + private var dayStatusList: List = emptyList() + private var currentDay = 1 + + init { + setupAttributes(attrs) + setupDefaultData() + } + + private fun setupAttributes(attrs: AttributeSet?) { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.WeekStatusView) + + completedColor = typedArray.getColor(R.styleable.WeekStatusView_completedColor, completedColor) + pendingColor = typedArray.getColor(R.styleable.WeekStatusView_pendingColor, pendingColor) + textColor = typedArray.getColor(R.styleable.WeekStatusView_textColor, textColor) + rewardTextColor = typedArray.getColor(R.styleable.WeekStatusView_rewardTextColor, rewardTextColor) + todayHighlightColor = typedArray.getColor(R.styleable.WeekStatusView_todayHighlightColor, todayHighlightColor) + + circleRadius = typedArray.getDimension(R.styleable.WeekStatusView_circleRadius, circleRadius) + textSize = typedArray.getDimension(R.styleable.WeekStatusView_textSize, textSize) + rewardTextSize = typedArray.getDimension(R.styleable.WeekStatusView_rewardTextSize, rewardTextSize) + itemSpacing = typedArray.getDimension(R.styleable.WeekStatusView_itemSpacing, itemSpacing) + todayStrokeWidth = typedArray.getDimension(R.styleable.WeekStatusView_todayStrokeWidth, todayStrokeWidth) + + todayHighlightPaint.strokeWidth = todayStrokeWidth + todayHighlightPaint.color = todayHighlightColor + + typedArray.recycle() + } + + private fun setupDefaultData() { + currentDay = getCurrentDayOfWeek() + dayStatusList = generateWeekData(currentDay) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val desiredWidth = MeasureSpec.getSize(widthMeasureSpec) + val desiredHeight = (circleRadius * 2 + textSize * 3 + itemSpacing * 2).toInt() + + setMeasuredDimension( + resolveSize(desiredWidth, widthMeasureSpec), + resolveSize(desiredHeight, heightMeasureSpec) + ) + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + if (dayStatusList.isEmpty()) return + + val totalWidth = measuredWidth.toFloat() + val totalHeight = measuredHeight.toFloat() + val totalItems = dayStatusList.size + val availableWidth = totalWidth - (itemSpacing * (totalItems - 1)) + val itemWidth = availableWidth / totalItems + + // bg + canvas.drawRect(itemWidth/2, (totalHeight - bgRectHeight)/2, totalWidth - itemWidth/2, totalHeight - (totalHeight - bgRectHeight)/2, bgPaint) + + dayStatusList.forEachIndexed { index, dayStatus -> + val centerX = itemWidth / 2 + index * (itemWidth + itemSpacing) + drawDayStatus(canvas, dayStatus, centerX) + } + } + + private fun drawDayStatus(canvas: Canvas, dayStatus: DayStatus, centerX: Float) { + val centerY = height / 2f + val circleCenterY = centerY - circleRadius + + // 绘制圆形背景 + circlePaint.color = if (dayStatus.isCompleted) completedColor else pendingColor + canvas.drawCircle(centerX, circleCenterY, circleRadius, circlePaint) + + // 绘制图标(对勾或G) + 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.textSize = rewardTextSize + canvas.drawText(dayStatus.reward, centerX, circleCenterY + circleRadius + textSize + rewardTextSize + 15, textPaint) + + // 如果是今天,绘制高亮边框 + if (dayStatus.isToday) { + canvas.drawCircle(centerX, circleCenterY, circleRadius + 2, todayHighlightPaint) + } + } + + // ==================== 公开API方法 ==================== + + /** + * 更新显示的数据 + * @param data 新的天数状态列表 + */ + fun updateData(data: List) { + dayStatusList = data + invalidate() + } + + /** + * 根据目标日期更新状态(自动计算完成状态) + * @param targetDate 目标日期 + */ + fun updateForDate(targetDate: Date) { + val targetDay = getDayOfWeekFromDate(targetDate) + dayStatusList = generateWeekData(targetDay) + invalidate() + } + + /** + * 刷新为当前日期 + */ + fun refreshToCurrentDate() { + updateForDate(Date()) + } + + /** + * 获取当前显示的数据 + */ + fun getCurrentData(): List = dayStatusList + + /** + * 获取当前高亮的天数(今天) + */ + fun getCurrentDay(): Int = currentDay + + // ==================== 工具方法 ==================== + private fun generateWeekData(targetDay: Int): List { + val rewards = listOf("100", "300", "300", "500", "300", "300", "800") + + return (1..7).map { day -> + DayStatus( + day = day, + reward = rewards[day - 1], + isCompleted = day < targetDay, + isToday = day == targetDay + ) + } + } + + private fun getCurrentDayOfWeek(): Int { + val calendar = Calendar.getInstance() + return convertCalendarDayToWeekDay(calendar.get(Calendar.DAY_OF_WEEK)) + } + + private fun getDayOfWeekFromDate(date: Date): Int { + val calendar = Calendar.getInstance().apply { time = date } + return convertCalendarDayToWeekDay(calendar.get(Calendar.DAY_OF_WEEK)) + } + + private fun convertCalendarDayToWeekDay(calendarDay: Int): Int { + // Calendar: 周日=1, 周一=2, ..., 周六=7 + // 转换为: 周一=1, 周二=2, ..., 周日=7 + return if (calendarDay == 1) 7 else calendarDay - 1 + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/vididinapp_feature_message_fragment_message.xml b/app/src/main/res/layout/vididinapp_feature_message_fragment_message.xml index 554268c..388235e 100644 --- a/app/src/main/res/layout/vididinapp_feature_message_fragment_message.xml +++ b/app/src/main/res/layout/vididinapp_feature_message_fragment_message.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#666666" tools:ignore="Overdraw"> @@ -372,6 +373,21 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/common/src/main/java/com/ama/core/common/widget/PopMenuIconView.kt b/core/common/src/main/java/com/ama/core/common/widget/PopMenuIconView.kt index 7f57d5f..ac3fe11 100644 --- a/core/common/src/main/java/com/ama/core/common/widget/PopMenuIconView.kt +++ b/core/common/src/main/java/com/ama/core/common/widget/PopMenuIconView.kt @@ -32,13 +32,12 @@ class PopMenuIconView @JvmOverloads constructor( private val mMenuItemList = mutableListOf() private var isMenuShowing = false private var itemSpacing = 0 - private var itemSize = 0 + private var mBinding: LayoutPopIconMenuViewBinding? = null init { - itemSize = 30.dp itemSpacing = 20.dp mBinding = LayoutPopIconMenuViewBinding.inflate(LayoutInflater.from(context), this, true) mBinding?.run { @@ -58,14 +57,12 @@ class PopMenuIconView @JvmOverloads constructor( private fun updateMenuItems() { mBinding!!.llMenuContainer.removeAllViews() - mMenuItemList.forEachIndexed { index, menuItem -> + mMenuItemList.asReversed().forEachIndexed { index, menuItem -> ImageView(context).apply { setImageResource(menuItem.iconResId) - layoutParams = LinearLayout.LayoutParams(itemSize, itemSize).apply { - if (/*index < mMenuItemList.size - 1*/true) { - bottomMargin = itemSpacing - } + layoutParams = LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply { + bottomMargin = itemSpacing } setOnClickListener {