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 | 304 ++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 228 insertions(+), 76 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 bfd5bb8..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,37 +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.core.Core 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.util.Log +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 } @@ -55,11 +51,14 @@ override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + //渲染包裹列表 setupRecyclerView() + //初始化tab内容和数据 setupTabLayout() + //日期调整 setupDatePicker() + setupView(view) updateDateDisplay() - setupWeekView(view) loadPackages() } @@ -80,16 +79,9 @@ 3 -> DateType.YEAR else -> DateType.DAY } - // 切换视图 - binding.viewFlipperStats.displayedChild = when(currentDateType) { - DateType.DAY -> 0 - DateType.WEEK -> 1 - else -> 0 - } updateDateDisplay() - //加载按天统计包裹数量和列表 + updateCharts() loadPackages() - observePackages() } override fun onTabUnselected(tab: TabLayout.Tab?) {} override fun onTabReselected(tab: TabLayout.Tab?) {} @@ -113,25 +105,48 @@ 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 { @@ -155,10 +170,15 @@ axisLeft.apply { setDrawGridLines(true) axisMinimum = 0f - granularity = 1f + granularity = 0.5f // 将刻度间隔设为0.5 valueFormatter = object : ValueFormatter() { override fun getFormattedValue(value: Float): String { - return value.toInt().toString() + // 只有整数时才显示标签 + return if (value % 1 == 0f) { + value.toInt().toString() + } else { + "" + } } } } @@ -173,7 +193,17 @@ 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 -> @@ -181,58 +211,77 @@ } val dataSet = BarDataSet(entries, "包裹数量") - dataSet.color = resources.getColor(R.color.purple_500) - dataSet.valueTextSize = 12f + dataSet.apply { + color = resources.getColor(R.color.purple_500) + valueTextSize = 12f + valueFormatter = object : ValueFormatter() { + override fun getFormattedValue(value: Float): String { + return value.toInt().toString() + } + } + } val barData = BarData(dataSet) barChart.data = barData - // 设置X轴标签 + // 修改X轴标签显示 barChart.xAxis.apply { valueFormatter = object : ValueFormatter() { override fun getFormattedValue(value: Float): String { val position = value.toInt() if (position >= 0 && position < stats.size) { - return "第${stats[position].date}周" + 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 - setDrawGridLines(false) } - // 调整图表显示 - barChart.apply { - setVisibleXRangeMaximum(5f) - moveViewToX(0f) - - axisLeft.apply { - axisMinimum = 0f - granularity = 1f - setDrawGridLines(true) - } - - barData.barWidth = 0.6f - description.isEnabled = false - legend.isEnabled = false - - invalidate() + // 高亮当前月份 + 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(false) // 改为显示实际数量 + setUsePercentValues(false) setDrawEntryLabels(false) - // 增大饼图尺寸 - setExtraOffsets(20f, 10f, 80f, 10f) - minimumHeight = (resources.displayMetrics.density * 400).toInt() // 设置最小高度 + // 调整饼图边距 + setExtraOffsets(20f, 10f, 60f, 10f) // 配置图例 legend.apply { @@ -241,17 +290,24 @@ horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT orientation = Legend.LegendOrientation.VERTICAL setDrawInside(false) - xEntrySpace = 7f - yEntrySpace = 0f + xEntrySpace = 10f + yEntrySpace = 5f yOffset = 0f - textSize = 12f // 增大图例文字大小 + textSize = 14f } + + // 设置中心空白 + 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}(${stat.count})") } @@ -281,36 +337,132 @@ } private fun loadPackages() { - // 这里应该从数据库或网络加载数据 - // 根据当前选择的日期类型传入对应参数 -// val packages = when (currentDateType) { -// DateType.DAY -> Core.code.getPackagesByDay(currentDate.timeInMillis) -// DateType.WEEK -> Core.code.getPackagesByWeek(currentDate.timeInMillis) -// DateType.MONTH -> Core.code.getPackagesByMonth(currentDate.timeInMillis) -// DateType.YEAR -> Core.code.getPackagesByYear(currentDate.timeInMillis) -// } - val packages =Core.code.getPackagesByDay(currentDate.timeInMillis) - packageAdapter.updatePackages(packages) - binding.textPackageCount.text = "${packageAdapter.itemCount}个" - } - 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