From cf9bc941487df8dfb0780d3e8f1281f4e397b5fa Mon Sep 17 00:00:00 2001
From: cloudroam <cloudroam>
Date: 星期四, 06 三月 2025 10:33:57 +0800
Subject: [PATCH] fix: 3
---
app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt | 354 ++++++++++++++++++++++++++++++++++++++++++++++++-----------
1 files changed, 288 insertions(+), 66 deletions(-)
diff --git a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
index 992fcb1..cecb616 100644
--- a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt
@@ -7,35 +7,33 @@
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import com.example.firstapp.databinding.FragmentDashboardBinding
import com.google.android.material.tabs.TabLayout
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.firstapp.adapter.PackageAdapter
-import com.example.firstapp.model.PackageInfo
import com.github.mikephil.charting.charts.BarChart
import com.github.mikephil.charting.charts.PieChart
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.data.*
-import com.github.mikephil.charting.formatter.IndexAxisValueFormatter
+import com.github.mikephil.charting.formatter.ValueFormatter
import java.util.*
import java.text.SimpleDateFormat
+import android.graphics.Color
+import android.widget.GridLayout
+import com.example.firstapp.model.DailyStat
class DashboardFragment : Fragment() {
private var _binding: FragmentDashboardBinding? = null
-
- // This property is only valid between onCreateView and
- // onDestroyView.
private val binding get() = _binding!!
-
private val packageAdapter = PackageAdapter()
private var currentDate = Calendar.getInstance()
private var currentDateType = DateType.DAY
private lateinit var barChart: BarChart
private lateinit var pieChart: PieChart
+ private lateinit var heatmapView: View
enum class DateType {
DAY, WEEK, MONTH, YEAR
}
@@ -53,11 +51,14 @@
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
+ //渲染包裹列表
setupRecyclerView()
+ //初始化tab内容和数据
setupTabLayout()
+ //日期调整
setupDatePicker()
+ setupView(view)
updateDateDisplay()
- setupWeekView(view)
loadPackages()
}
@@ -78,15 +79,9 @@
3 -> DateType.YEAR
else -> DateType.DAY
}
- // 切换视图
- binding.viewFlipperStats.displayedChild = when(currentDateType) {
- DateType.DAY -> 0
- DateType.WEEK -> 1
- else -> 0
- }
updateDateDisplay()
-// loadPackages()
- observePackages()
+ updateCharts()
+ loadPackages()
}
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {}
@@ -110,91 +105,230 @@
DateType.YEAR -> currentDate.add(Calendar.YEAR, amount)
}
updateDateDisplay()
+ updateCharts()
loadPackages()
}
private fun updateDateDisplay() {
val dateFormat = when (currentDateType) {
DateType.DAY -> "yyyy年MM月dd日"
- DateType.WEEK -> "yyyy年第ww周"
+ DateType.WEEK -> {
+ // 获取本周的起始和结束日期
+ val calendar = currentDate.clone() as Calendar
+ calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY)
+ val startDate = SimpleDateFormat("MM月dd日", Locale.getDefault()).format(calendar.time)
+
+ calendar.add(Calendar.DAY_OF_WEEK, 6)
+ val endDate = SimpleDateFormat("MM月dd日", Locale.getDefault()).format(calendar.time)
+
+ "$startDate-$endDate"
+ }
DateType.MONTH -> "yyyy年MM月"
DateType.YEAR -> "yyyy年"
}
- binding.textCurrentDate.text = SimpleDateFormat(dateFormat, Locale.getDefault())
- .format(currentDate.time)
+
+ if (currentDateType == DateType.WEEK) {
+ binding.textCurrentDate.text = dateFormat
+ } else {
+ binding.textCurrentDate.text = SimpleDateFormat(dateFormat, Locale.getDefault())
+ .format(currentDate.time)
+ }
}
- private fun setupWeekView(view: View) {
+ private fun setupView(view: View) {
val weekStatsView = binding.layoutWeekStats.root
barChart = weekStatsView.findViewById(R.id.chart_daily_packages)
pieChart = weekStatsView.findViewById(R.id.chart_courier_distribution)
+ heatmapView = weekStatsView.findViewById(R.id.heatmap_yearly)
+
+ // 初始化时隐藏统计视图
+ weekStatsView.visibility = View.GONE
+
setupBarChart()
setupPieChart()
+ setupHeatmap()
+ updateCharts()
}
private fun setupBarChart() {
- // 配置柱状图
barChart.apply {
description.isEnabled = false
setDrawGridBackground(false)
legend.isEnabled = false
+
+ // 增大图表高度
+ minimumHeight = (resources.displayMetrics.density * 300).toInt()
// X轴设置
xAxis.apply {
position = XAxis.XAxisPosition.BOTTOM
setDrawGridLines(false)
- valueFormatter = IndexAxisValueFormatter(getDayLabels())
+ granularity = 1f
+ labelRotationAngle = -45f
+ textSize = 10f
}
// Y轴设置
axisLeft.apply {
setDrawGridLines(true)
axisMinimum = 0f
+ granularity = 0.5f // 将刻度间隔设为0.5
+ valueFormatter = object : ValueFormatter() {
+ override fun getFormattedValue(value: Float): String {
+ // 只有整数时才显示标签
+ return if (value % 1 == 0f) {
+ value.toInt().toString()
+ } else {
+ ""
+ }
+ }
+ }
}
axisRight.isEnabled = false
+
+ // 设置图表交互
+ setTouchEnabled(true)
+ isDragEnabled = true
+ setScaleEnabled(true)
}
updateBarChartData()
}
private fun updateBarChartData() {
- viewModel.getDailyStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+ val statsFlow = when (currentDateType) {
+ DateType.WEEK -> {
+ viewModel.getWeeklyStats(currentDate.timeInMillis, 6)
+ }
+ DateType.MONTH -> {
+ viewModel.getYearMonthlyStats(currentDate.timeInMillis)
+ }
+ else -> return
+ }
+
+ statsFlow.observe(viewLifecycleOwner) { stats ->
+ if (stats.isEmpty()) return@observe
+
val entries = stats.mapIndexed { index, stat ->
BarEntry(index.toFloat(), stat.count.toFloat())
}
val dataSet = BarDataSet(entries, "包裹数量")
- dataSet.color = resources.getColor(R.color.purple_500)
+ dataSet.apply {
+ color = resources.getColor(R.color.purple_500)
+ valueTextSize = 12f
+ valueFormatter = object : ValueFormatter() {
+ override fun getFormattedValue(value: Float): String {
+ return value.toInt().toString()
+ }
+ }
+ }
- barChart.data = BarData(dataSet)
+ val barData = BarData(dataSet)
+ barChart.data = barData
+
+ // 修改X轴标签显示
+ barChart.xAxis.apply {
+ valueFormatter = object : ValueFormatter() {
+ override fun getFormattedValue(value: Float): String {
+ val position = value.toInt()
+ if (position >= 0 && position < stats.size) {
+ return when(currentDateType) {
+ DateType.WEEK -> {
+ val weekStat = stats[position]
+ val calendar = Calendar.getInstance()
+ calendar.timeInMillis = weekStat.weekStart!!
+ SimpleDateFormat("MM/dd", Locale.getDefault()).format(calendar.time)
+ }
+ DateType.MONTH -> {
+ // 显示月份标签(1-12月)
+ "${position + 1}月"
+ }
+ else -> ""
+ }
+ }
+ return ""
+ }
+ }
+ position = XAxis.XAxisPosition.BOTTOM
+ setDrawGridLines(false)
+ labelCount = stats.size
+ granularity = 1f
+ labelRotationAngle = -45f
+ textSize = 10f
+ }
+
+ // 高亮当前月份
+ if (currentDateType == DateType.MONTH) {
+ val currentMonth = currentDate.get(Calendar.MONTH)
+ dataSet.setColors(List(stats.size) { index ->
+ if (index == currentMonth) resources.getColor(R.color.purple_500)
+ else resources.getColor(R.color.purple_200)
+ })
+ } else if (currentDateType == DateType.WEEK) {
+ // 保持周视图的高亮逻辑
+ val highlightIndex = 3f
+ dataSet.setColors(List(stats.size) { index ->
+ if (index == 3) resources.getColor(R.color.purple_500)
+ else resources.getColor(R.color.purple_200)
+ })
+ }
+
barChart.invalidate()
}
}
private fun setupPieChart() {
- // 配置饼图
pieChart.apply {
description.isEnabled = false
- setUsePercentValues(true)
+ setUsePercentValues(false)
setDrawEntryLabels(false)
+
+ // 调整饼图边距
+ setExtraOffsets(20f, 10f, 60f, 10f)
+
+ // 配置图例
+ legend.apply {
+ isEnabled = true
+ verticalAlignment = Legend.LegendVerticalAlignment.CENTER
+ horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT
+ orientation = Legend.LegendOrientation.VERTICAL
+ setDrawInside(false)
+ xEntrySpace = 10f
+ yEntrySpace = 5f
+ yOffset = 0f
+ textSize = 14f
+ }
- legend.isEnabled = true
- legend.verticalAlignment = Legend.LegendVerticalAlignment.CENTER
- legend.horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT
- legend.orientation = Legend.LegendOrientation.VERTICAL
+ // 设置中心空白
+ holeRadius = 45f
+ transparentCircleRadius = 50f
}
updatePieChartData()
}
private fun updatePieChartData() {
- viewModel.getCourierStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+ viewModel.getCourierStats(
+ currentDate.timeInMillis,
+ currentDateType.name
+ ).observe(viewLifecycleOwner) { stats ->
val entries = stats.map { stat ->
- PieEntry(stat.count.toFloat(), stat.courierName)
+ PieEntry(stat.count.toFloat(), "${stat.courierName}(${stat.count})")
}
val dataSet = PieDataSet(entries, "快递公司分布")
dataSet.colors = listOf(
resources.getColor(R.color.purple_500),
- resources.getColor(R.color.teal_200)
+ resources.getColor(R.color.teal_200),
+ resources.getColor(R.color.purple_200),
+ resources.getColor(R.color.teal_700)
)
+ dataSet.valueTextSize = 14f // 增大数值文字大小
- pieChart.data = PieData(dataSet)
+ val pieData = PieData(dataSet)
+ pieData.setValueFormatter(object : ValueFormatter() {
+ override fun getFormattedValue(value: Float): String {
+ return value.toInt().toString()
+ }
+ })
+
+ pieChart.data = pieData
pieChart.invalidate()
}
}
@@ -203,44 +337,132 @@
}
private fun loadPackages() {
- // 这里应该从数据库或网络加载数据
- // 这里使用模拟数据作为示例
- val mockPackages = listOf(
- PackageInfo(
- trackingNumber = "14-6-7023",
- courierName = "某快递",
- receivedTime = System.currentTimeMillis(),
- courierIcon = R.drawable.data
- ),
- PackageInfo(
- trackingNumber = "230721",
- courierName = "京东",
- receivedTime = System.currentTimeMillis() - 3600000,
- courierIcon = R.drawable.data
- )
- )
-
- packageAdapter.updatePackages(mockPackages)
- binding.textPackageCount.text = "${mockPackages.size}个"
- }
- private fun observePackages() {
viewModel.getPackages(
currentDate.timeInMillis,
currentDateType.name
).observe(viewLifecycleOwner) { packages ->
- when (currentDateType) {
- DateType.WEEK -> {
- // 更新图表数据
- updateBarChartData()
- updatePieChartData()
- }
- else -> {
- packageAdapter.updatePackages(packages)
- }
- }
+ packageAdapter.updatePackages(packages)
binding.textPackageCount.text = "${packages.size}个"
}
}
+ private fun setupHeatmap() {
+ heatmapView.visibility = View.GONE
+ }
+
+ private fun updateHeatmapData() {
+ viewModel.getYearlyHeatmap(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats ->
+ if (stats.isEmpty()) return@observe
+
+ // 创建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 = 7
+ columnCount = 52
+ }
+
+ // 添加日期标签
+ val dayLabels = arrayOf("周日", "周一", "周二", "周三", "周四", "周五", "周六")
+ for (i in 0..6) {
+ val label = TextView(context).apply {
+ text = dayLabels[i]
+ textSize = 10f
+ setPadding(0, 0, 8, 0)
+ }
+ gridLayout.addView(label)
+ }
+
+ // 添加热力图单元格
+ for (day in 0..6) {
+ for (week in 0..51) {
+ val count = heatmapMatrix[day][week]
+ val cell = View(context).apply {
+ layoutParams = ViewGroup.LayoutParams(
+ resources.getDimensionPixelSize(R.dimen.heatmap_cell_size),
+ resources.getDimensionPixelSize(R.dimen.heatmap_cell_size)
+ )
+ setBackgroundColor(getHeatmapColor(count))
+ setPadding(1, 1, 1, 1)
+ }
+ gridLayout.addView(cell)
+ }
+ }
+
+ addView(gridLayout)
+ }
+ }
+ }
+
+ private fun getHeatmapColor(count: Int): Int {
+ // 根据数量返回不同深浅的颜色
+ return when {
+ count == 0 -> Color.parseColor("#EBEDF0")
+ count <= 2 -> Color.parseColor("#9BE9A8")
+ count <= 4 -> Color.parseColor("#40C463")
+ count <= 6 -> Color.parseColor("#30A14E")
+ else -> Color.parseColor("#216E39")
+ }
+ }
+
+ private fun updateCharts() {
+ when (currentDateType) {
+ DateType.DAY -> {
+ // 日视图显示包裹列表,隐藏统计图表
+ binding.recyclerPackages.visibility = View.VISIBLE
+ binding.layoutWeekStats.root.visibility = View.GONE
+ binding.layoutYearStats.root.visibility = View.GONE
+ }
+ DateType.WEEK, DateType.MONTH -> {
+ // 周和月视图显示柱状图和饼图,隐藏包裹列表
+ binding.recyclerPackages.visibility = View.GONE
+ binding.layoutWeekStats.root.visibility = View.VISIBLE
+ binding.layoutYearStats.root.visibility = View.GONE
+ binding.layoutWeekStats.chartDailyPackages.visibility = View.VISIBLE
+ binding.layoutWeekStats.heatmapYearly.visibility = View.GONE
+ updateBarChartData()
+ updatePieChartData()
+ }
+ DateType.YEAR -> {
+ // 年视图显示热力图和饼图,隐藏包裹列表和柱状图
+ binding.recyclerPackages.visibility = View.GONE
+ binding.layoutWeekStats.root.visibility = View.VISIBLE
+ binding.layoutYearStats.root.visibility = View.VISIBLE
+ binding.layoutWeekStats.chartDailyPackages.visibility = View.GONE
+ binding.layoutWeekStats.heatmapYearly.visibility = View.VISIBLE
+ updateHeatmapData()
+ updatePieChartData()
+ }
+ }
+ }
+
+ private fun updateYearlyStats() {
+ viewModel.getYearlyStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats: List<DailyStat> ->
+ if (stats.isEmpty()) return@observe
+
+ // 更新年度包裹总数
+ binding.layoutYearStats.textTotalPackages.text = "${stats.sumOf { it.count }}个"
+
+ // 更新平均每天包裹数
+ val avgDaily = stats.sumOf { it.count }.toFloat() / 365
+ binding.layoutYearStats.textDailyAverage.text = String.format("%.2f", avgDaily)
+ }
+ }
+
override fun onDestroyView() {
super.onDestroyView()
_binding = null
--
Gitblit v1.9.3