| | |
| | | import java.util.* |
| | | import java.text.SimpleDateFormat |
| | | import android.graphics.Color |
| | | import android.view.Gravity |
| | | import android.widget.GridLayout |
| | | import android.widget.LinearLayout |
| | | import android.widget.Toast |
| | | import androidx.cardview.widget.CardView |
| | | import androidx.lifecycle.lifecycleScope |
| | |
| | | |
| | | // 创建52周x7天的数据矩阵 |
| | | val heatmapMatrix = Array(7) { IntArray(52) } |
| | | |
| | | // 填充数据 |
| | | stats.forEach { stat -> |
| | | val week = stat.weekOfYear - 1 // 0-51 |
| | | val dayOfWeek = stat.dayOfWeek - 1 // 0-6 |
| | | if (week in 0..51 && dayOfWeek in 0..6) { |
| | | heatmapMatrix[dayOfWeek][week] = stat.count |
| | | } |
| | | } |
| | | |
| | | // 更新UI |
| | | binding.layoutWeekStats.heatmapYearly.apply { |
| | | removeAllViews() |
| | | |
| | | val gridLayout = GridLayout(context).apply { |
| | | rowCount = 8 // 1行月份 + 7行星期 |
| | | columnCount = 53 // 1列星期 + 52列周数 |
| | | } |
| | | |
| | | // 添加月份标签 |
| | | val months = arrayOf("1月", "2月", "3月", "4月", "5月", "6月", |
| | | "7月", "8月", "9月", "10月", "11月", "12月") |
| | | months.forEachIndexed { index, month -> |
| | | val label = TextView(context).apply { |
| | | text = month |
| | | textSize = 10f |
| | | setPadding(0, 0, 8, 4) |
| | | val weekPosition = (index * 4.3).toInt() // 估算月份起始位置 |
| | | layoutParams = GridLayout.LayoutParams().apply { |
| | | columnSpec = GridLayout.spec(weekPosition + 1) |
| | | rowSpec = GridLayout.spec(0) |
| | | } |
| | | } |
| | | gridLayout.addView(label) |
| | | } |
| | | |
| | | // 添加星期标签 |
| | | val dayLabels = arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日") |
| | | dayLabels.forEachIndexed { index, label -> |
| | | val textView = TextView(context).apply { |
| | | text = label |
| | | textSize = 10f |
| | | setPadding(4, 0, 8, 0) |
| | | layoutParams = GridLayout.LayoutParams().apply { |
| | | columnSpec = GridLayout.spec(0) |
| | | rowSpec = GridLayout.spec(index + 1) |
| | | } |
| | | } |
| | | gridLayout.addView(textView) |
| | | } |
| | | |
| | | // 添加热力图单元格 |
| | | for (day in 0..6) { |
| | | for (week in 0..51) { |
| | | val count = heatmapMatrix[day][week] |
| | | val cell = View(context).apply { |
| | | layoutParams = GridLayout.LayoutParams().apply { |
| | | width = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size) |
| | | height = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size) |
| | | columnSpec = GridLayout.spec(week + 1) |
| | | rowSpec = GridLayout.spec(day + 1) |
| | | setMargins(1, 1, 1, 1) |
| | | } |
| | | setBackgroundColor(getHeatmapColor(count)) |
| | | } |
| | | gridLayout.addView(cell) |
| | | } |
| | | } |
| | | |
| | | // 创建图例(Legend) |
| | | val legendLayout = LinearLayout(context).apply { |
| | | orientation = LinearLayout.HORIZONTAL |
| | | gravity = Gravity.END or Gravity.CENTER_VERTICAL // 靠右对齐 |
| | | setPadding(8, 16, 8, 0) |
| | | layoutParams = LinearLayout.LayoutParams( |
| | | ViewGroup.LayoutParams.MATCH_PARENT, |
| | | ViewGroup.LayoutParams.WRAP_CONTENT |
| | | ) |
| | | } |
| | | |
| | | // 左侧“少”标签 |
| | | val labelLow = TextView(context).apply { |
| | | text = "少" |
| | | textSize = 10f |
| | | setPadding(0, 0, 8, 0) |
| | | } |
| | | legendLayout.addView(labelLow) |
| | | |
| | | // 渐变色块 + 数值 |
| | | val legendLevels = listOf(0, 5, 10, 15, 20) |
| | | legendLevels.forEach { level -> |
| | | val colorBox = View(context).apply { |
| | | setBackgroundColor(getHeatmapColor(level)) |
| | | val size = resources.getDimensionPixelSize(R.dimen.heatmap_cell_size) |
| | | layoutParams = LinearLayout.LayoutParams(size, size).apply { |
| | | marginEnd = 4 |
| | | } |
| | | } |
| | | |
| | | val label = TextView(context).apply { |
| | | // text = "$level" |
| | | textSize = 10f |
| | | setPadding(0, 0, 8, 0) |
| | | } |
| | | |
| | | legendLayout.addView(colorBox) |
| | | legendLayout.addView(label) |
| | | } |
| | | |
| | | // 右侧“多”标签 |
| | | val labelHigh = TextView(context).apply { |
| | | text = "多" |
| | | textSize = 10f |
| | | setPadding(8, 0, 0, 0) |
| | | } |
| | | legendLayout.addView(labelHigh) |
| | | |
| | | // 垂直组合 gridLayout + legendLayout |
| | | val container = LinearLayout(context).apply { |
| | | orientation = LinearLayout.VERTICAL |
| | | addView(gridLayout) |
| | | addView(legendLayout) |
| | | } |
| | | |
| | | addView(container) |
| | | } |
| | | } |
| | | } |
| | | |
| | | private fun getHeatmapColor(count: Int): Int { |
| | | return when { |
| | | count == 0 -> Color.parseColor("#EBECF1") // 最淡 |
| | | count < 5 -> Color.parseColor("#ACE7B1") |
| | | count < 10 -> Color.parseColor("#68C16F") |
| | | count < 15 -> Color.parseColor("#529F57") |
| | | else -> Color.parseColor("#356D40") // 最深 |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | private fun updateHeatmapData_bak() { |
| | | viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats -> |
| | | if (stats.isEmpty()) return@observe |
| | | |
| | | // 创建52周x7天的数据矩阵 |
| | | val heatmapMatrix = Array(7) { IntArray(52) } |
| | | |
| | | // 填充数据 |
| | | stats.forEach { stat -> |
| | |
| | | } |
| | | } |
| | | |
| | | private fun getHeatmapColor(count: Int): Int { |
| | | private fun getHeatmapColor_bak(count: Int): Int { |
| | | return when { |
| | | count == 0 -> Color.parseColor("#EBEDF0") |
| | | count == 1 -> Color.parseColor("#9BE9A8") |