From 85a2d7f51a32cd747e27ab68bc8699a54419b6c7 Mon Sep 17 00:00:00 2001 From: cloudroam <cloudroam> Date: 星期一, 03 三月 2025 10:02:04 +0800 Subject: [PATCH] fix :包裹列表 --- app/src/main/java/com/example/firstapp/repository/PackageRepository.kt | 34 ++ app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt | 226 ++++++++++++++++ app/src/main/res/layout/layout_week_stats.xml | 63 ++++ app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt | 51 +++ app/src/main/java/com/example/firstapp/dao/PackageDao.kt | 49 +++ app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt | 38 ++ app/src/main/java/com/example/firstapp/model/DailyStat.kt | 16 + app/src/main/res/drawable/resource_package.xml | 15 + app/src/main/java/com/example/firstapp/model/CourierStat.kt | 15 + app/src/main/java/com/example/firstapp/model/PackageInfo.kt | 15 + app/src/main/java/com/example/firstapp/database/AppDatabase.kt | 45 ++ app/src/main/res/layout/fragment_dashboard.xml | 127 ++++++++ app/src/main/res/layout/item_package_dashboard.xml | 57 ++++ app/build.gradle | 3 14 files changed, 712 insertions(+), 42 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 82e8b39..75f7708 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -270,6 +270,7 @@ // 滚轮滑动控件级联 implementation 'com.contrarywind:Android-PickerView:4.1.9' - + //图形化 + implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' } \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt new file mode 100644 index 0000000..4abce71 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/adapter/PackageAdapter.kt @@ -0,0 +1,51 @@ +package com.example.firstapp.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.firstapp.R +import com.example.firstapp.model.PackageInfo +import java.text.SimpleDateFormat +import java.util.Locale + + +class PackageAdapter : RecyclerView.Adapter<PackageAdapter.PackageViewHolder>() { + + private var packages = listOf<PackageInfo>() + + fun updatePackages(newPackages: List<PackageInfo>) { + packages = newPackages + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PackageViewHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.item_package_dashboard, parent, false) + return PackageViewHolder(view) + } + + override fun onBindViewHolder(holder: PackageViewHolder, position: Int) { + holder.bind(packages[position]) + } + + override fun getItemCount() = packages.size + + class PackageViewHolder(view: View) : RecyclerView.ViewHolder(view) { + private val imgCourier: ImageView = view.findViewById(R.id.img_courier) + private val textCourierName: TextView = view.findViewById(R.id.text_courier_name) + private val textTrackingNumber: TextView = view.findViewById(R.id.text_tracking_number) + private val textTime: TextView = view.findViewById(R.id.text_time) + + fun bind(packageInfo: PackageInfo) { + imgCourier.setImageResource(packageInfo.courierIcon) + textCourierName.text = packageInfo.courierName + textTrackingNumber.text = packageInfo.trackingNumber + textTime.text = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()) + .format(packageInfo.receivedTime) + } + } +} + diff --git a/app/src/main/java/com/example/firstapp/dao/PackageDao.kt b/app/src/main/java/com/example/firstapp/dao/PackageDao.kt new file mode 100644 index 0000000..e156386 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/dao/PackageDao.kt @@ -0,0 +1,49 @@ +package com.example.firstapp.dao + +import androidx.room.* +import com.example.firstapp.model.PackageInfo +import com.example.firstapp.model.CourierStat +import com.example.firstapp.model.DailyStat +import kotlinx.coroutines.flow.Flow + +@Dao +interface PackageDao { + @Query("SELECT * FROM packages WHERE date(receivedTime/1000, 'unixepoch', 'localtime') = date(:date/1000, 'unixepoch', 'localtime') ORDER BY receivedTime DESC") + fun getPackagesByDay(date: Long): Flow<List<PackageInfo>> + + @Query(""" + SELECT * FROM packages + WHERE strftime('%Y-%W', receivedTime/1000, 'unixepoch', 'localtime') = + strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime') + ORDER BY receivedTime DESC + """) + fun getPackagesByWeek(date: Long): Flow<List<PackageInfo>> + + @Query(""" + SELECT * FROM CourierStat + WHERE EXISTS ( + SELECT 1 FROM packages p + WHERE p.courierName = CourierStat.courierName + AND strftime('%Y-%W', p.receivedTime/1000, 'unixepoch', 'localtime') = + strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime') + ) + """) + fun getCourierStatsByWeek(date: Long): Flow<List<CourierStat>> + + @Query(""" + SELECT * FROM DailyStat + WHERE EXISTS ( + SELECT 1 FROM packages p + WHERE date(p.receivedTime/1000, 'unixepoch', 'localtime') = DailyStat.date + AND strftime('%Y-%W', p.receivedTime/1000, 'unixepoch', 'localtime') = + strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime') + ) + """) + fun getDailyStatsByWeek(date: Long): Flow<List<DailyStat>> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insert(packageInfo: PackageInfo) + + @Update + fun update(packageInfo: PackageInfo) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/database/AppDatabase.kt b/app/src/main/java/com/example/firstapp/database/AppDatabase.kt index 2251240..b520136 100644 --- a/app/src/main/java/com/example/firstapp/database/AppDatabase.kt +++ b/app/src/main/java/com/example/firstapp/database/AppDatabase.kt @@ -7,6 +7,7 @@ import androidx.room.TypeConverters import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase +import com.example.firstapp.dao.PackageDao import com.example.firstapp.database.dao.CodeDao import com.example.firstapp.database.dao.KeywordDao import com.example.firstapp.database.dao.MsgDao @@ -20,11 +21,23 @@ import com.example.firstapp.utils.TAG_LIST import com.example.firstapp.database.ext.ConvertersDate - +import com.example.firstapp.model.PackageInfo +import com.example.firstapp.model.CourierStat +import com.example.firstapp.model.DailyStat @Database( - entities = [ Msg::class, Code::class, KeywordEntity::class, Reminder::class], + entities = [ + Msg::class, + Code::class, + KeywordEntity::class, + Reminder::class, + PackageInfo::class + ], + views = [ + CourierStat::class, + DailyStat::class + ], version = 20, exportSchema = false ) @@ -34,6 +47,7 @@ abstract fun codeDao(): CodeDao abstract fun keywordDao(): KeywordDao abstract fun reminderDao(): ReminderDao + abstract fun packageDao(): PackageDao companion object { @Volatile @@ -71,16 +85,6 @@ - - - - - - - - - - private val MIGRATION_MSG = object : Migration(19, 20) { override fun migrate(database: SupportSQLiteDatabase) { //database.execSQL("Create table Msg as Select id,type,`from`,content,(case when sim_info like 'SIM1%' then '0' when sim_info like 'SIM2%' then '1' else '-1' end) as sim_slot,sim_info,sub_id,time from Logs where 1 = 1") @@ -110,6 +114,23 @@ `isEnabled` INTEGER NOT NULL ) """) + + // 创建 CourierStat 视图 + database.execSQL(""" + CREATE VIEW IF NOT EXISTS CourierStat AS + SELECT courierName, COUNT(*) as count + FROM packages + GROUP BY courierName + """) + + // 创建 DailyStat 视图 + database.execSQL(""" + CREATE VIEW IF NOT EXISTS DailyStat AS + SELECT date(receivedTime/1000, 'unixepoch', 'localtime') as date, + COUNT(*) as count + FROM packages + GROUP BY date(receivedTime/1000, 'unixepoch', 'localtime') + """) // database.execSQL(""" // CREATE TABLE IF NOT EXISTS `reminders` ( // id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/app/src/main/java/com/example/firstapp/model/CourierStat.kt b/app/src/main/java/com/example/firstapp/model/CourierStat.kt new file mode 100644 index 0000000..c112e7c --- /dev/null +++ b/app/src/main/java/com/example/firstapp/model/CourierStat.kt @@ -0,0 +1,15 @@ +package com.example.firstapp.model + +import androidx.room.DatabaseView + +@DatabaseView( + """ + SELECT courierName, COUNT(*) as count + FROM packages + GROUP BY courierName + """ +) +data class CourierStat( + val courierName: String, + val count: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/model/DailyStat.kt b/app/src/main/java/com/example/firstapp/model/DailyStat.kt new file mode 100644 index 0000000..dc61551 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/model/DailyStat.kt @@ -0,0 +1,16 @@ +package com.example.firstapp.model + +import androidx.room.DatabaseView + +@DatabaseView( + """ + SELECT date(receivedTime/1000, 'unixepoch', 'localtime') as date, + COUNT(*) as count + FROM packages + GROUP BY date(receivedTime/1000, 'unixepoch', 'localtime') + """ +) +data class DailyStat( + val date: String, + val count: Int +) \ No newline at end of file diff --git a/app/src/main/java/com/example/firstapp/model/PackageInfo.kt b/app/src/main/java/com/example/firstapp/model/PackageInfo.kt new file mode 100644 index 0000000..d1e2005 --- /dev/null +++ b/app/src/main/java/com/example/firstapp/model/PackageInfo.kt @@ -0,0 +1,15 @@ +package com.example.firstapp.model + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "packages") +data class PackageInfo( + @PrimaryKey + val trackingNumber: String, // 快递单号 + val courierName: String, // 快递公司名称 + val receivedTime: Long, // 收件时间 + val courierIcon: Int, // 快递公司图标资源ID + val status: Int = 0 // 取件状态: 0-未取件, 1-已取件 +) + diff --git a/app/src/main/java/com/example/firstapp/repository/PackageRepository.kt b/app/src/main/java/com/example/firstapp/repository/PackageRepository.kt new file mode 100644 index 0000000..082178c --- /dev/null +++ b/app/src/main/java/com/example/firstapp/repository/PackageRepository.kt @@ -0,0 +1,34 @@ +package com.example.firstapp.repository + +import com.example.firstapp.dao.PackageDao +import com.example.firstapp.model.PackageInfo +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +class PackageRepository(private val packageDao: PackageDao) { + + fun getPackages(date: Long, dateType: String): Flow<List<PackageInfo>> { + return when(dateType) { + "DAY" -> packageDao.getPackagesByDay(date) + "WEEK" -> packageDao.getPackagesByWeek(date) + else -> packageDao.getPackagesByDay(date) + } + } + + fun getCourierStats(date: Long) = packageDao.getCourierStatsByWeek(date) + + fun getDailyStats(date: Long) = packageDao.getDailyStatsByWeek(date) + + suspend fun insert(packageInfo: PackageInfo) { + withContext(Dispatchers.IO) { + packageDao.insert(packageInfo) + } + } + + suspend fun update(packageInfo: PackageInfo) { + withContext(Dispatchers.IO) { + packageDao.update(packageInfo) + } + } +} \ No newline at end of file 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 0be390f..992fcb1 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 @@ -1,5 +1,6 @@ package com.example.firstapp.ui.dashboard +import com.example.firstapp.R import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -7,7 +8,20 @@ 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 java.util.* +import java.text.SimpleDateFormat class DashboardFragment : Fragment() { @@ -17,24 +31,216 @@ // 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 + enum class DateType { + DAY, WEEK, MONTH, YEAR + } + private val viewModel: DashboardViewModel by viewModels() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - val dashboardViewModel = - ViewModelProvider(this).get(DashboardViewModel::class.java) - _binding = FragmentDashboardBinding.inflate(inflater, container, false) - val root: View = binding.root - - val textView: TextView = binding.textDashboard - dashboardViewModel.text.observe(viewLifecycleOwner) { - textView.text = it - } - return root + return binding.root } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setupRecyclerView() + setupTabLayout() + setupDatePicker() + updateDateDisplay() + setupWeekView(view) + loadPackages() + } + + private fun setupRecyclerView() { + binding.recyclerPackages.apply { + layoutManager = LinearLayoutManager(context) + adapter = packageAdapter + } + } + + private fun setupTabLayout() { + binding.tabDateRange.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab?) { + currentDateType = when(tab?.position) { + 0 -> DateType.DAY + 1 -> DateType.WEEK + 2 -> DateType.MONTH + 3 -> DateType.YEAR + else -> DateType.DAY + } + // 切换视图 + binding.viewFlipperStats.displayedChild = when(currentDateType) { + DateType.DAY -> 0 + DateType.WEEK -> 1 + else -> 0 + } + updateDateDisplay() +// loadPackages() + observePackages() + } + override fun onTabUnselected(tab: TabLayout.Tab?) {} + override fun onTabReselected(tab: TabLayout.Tab?) {} + }) + } + + private fun setupDatePicker() { + binding.btnPreviousDate.setOnClickListener { + adjustDate(-1) + } + binding.btnNextDate.setOnClickListener { + adjustDate(1) + } + } + + private fun adjustDate(amount: Int) { + when (currentDateType) { + DateType.DAY -> currentDate.add(Calendar.DAY_OF_MONTH, amount) + DateType.WEEK -> currentDate.add(Calendar.WEEK_OF_YEAR, amount) + DateType.MONTH -> currentDate.add(Calendar.MONTH, amount) + DateType.YEAR -> currentDate.add(Calendar.YEAR, amount) + } + updateDateDisplay() + loadPackages() + } + + private fun updateDateDisplay() { + val dateFormat = when (currentDateType) { + DateType.DAY -> "yyyy年MM月dd日" + DateType.WEEK -> "yyyy年第ww周" + DateType.MONTH -> "yyyy年MM月" + DateType.YEAR -> "yyyy年" + } + binding.textCurrentDate.text = SimpleDateFormat(dateFormat, Locale.getDefault()) + .format(currentDate.time) + } + private fun setupWeekView(view: View) { + val weekStatsView = binding.layoutWeekStats.root + barChart = weekStatsView.findViewById(R.id.chart_daily_packages) + pieChart = weekStatsView.findViewById(R.id.chart_courier_distribution) + setupBarChart() + setupPieChart() + } + private fun setupBarChart() { + // 配置柱状图 + barChart.apply { + description.isEnabled = false + setDrawGridBackground(false) + legend.isEnabled = false + + // X轴设置 + xAxis.apply { + position = XAxis.XAxisPosition.BOTTOM + setDrawGridLines(false) + valueFormatter = IndexAxisValueFormatter(getDayLabels()) + } + + // Y轴设置 + axisLeft.apply { + setDrawGridLines(true) + axisMinimum = 0f + } + axisRight.isEnabled = false + } + + updateBarChartData() + } + private fun updateBarChartData() { + viewModel.getDailyStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats -> + val entries = stats.mapIndexed { index, stat -> + BarEntry(index.toFloat(), stat.count.toFloat()) + } + + val dataSet = BarDataSet(entries, "包裹数量") + dataSet.color = resources.getColor(R.color.purple_500) + + barChart.data = BarData(dataSet) + barChart.invalidate() + } + } + private fun setupPieChart() { + // 配置饼图 + pieChart.apply { + description.isEnabled = false + setUsePercentValues(true) + setDrawEntryLabels(false) + + legend.isEnabled = true + legend.verticalAlignment = Legend.LegendVerticalAlignment.CENTER + legend.horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT + legend.orientation = Legend.LegendOrientation.VERTICAL + } + + updatePieChartData() + } + private fun updatePieChartData() { + viewModel.getCourierStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats -> + val entries = stats.map { stat -> + PieEntry(stat.count.toFloat(), stat.courierName) + } + + val dataSet = PieDataSet(entries, "快递公司分布") + dataSet.colors = listOf( + resources.getColor(R.color.purple_500), + resources.getColor(R.color.teal_200) + ) + + pieChart.data = PieData(dataSet) + pieChart.invalidate() + } + } + private fun getDayLabels(): Array<String> { + return arrayOf("周一", "周二", "周三", "周四", "周五", "周六", "周日") + } + + 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) + } + } + binding.textPackageCount.text = "${packages.size}个" + } + } override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt index e47a4a6..dae2211 100644 --- a/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/com/example/firstapp/ui/dashboard/DashboardViewModel.kt @@ -1,13 +1,35 @@ package com.example.firstapp.ui.dashboard -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.asLiveData +import androidx.lifecycle.viewModelScope +import com.example.firstapp.AppDatabase +import com.example.firstapp.model.PackageInfo +import com.example.firstapp.repository.PackageRepository +import kotlinx.coroutines.launch -class DashboardViewModel : ViewModel() { - - private val _text = MutableLiveData<String>().apply { - value = "This is dashboard Fragment" +class DashboardViewModel(application: Application) : AndroidViewModel(application) { + + private val repository: PackageRepository + + init { + val packageDao = AppDatabase.getInstance(application).packageDao() + repository = PackageRepository(packageDao) } - val text: LiveData<String> = _text + + fun getPackages(date: Long, dateType: String) = + repository.getPackages(date, dateType).asLiveData() + + fun getCourierStats(date: Long) = repository.getCourierStats(date).asLiveData() + + fun getDailyStats(date: Long) = repository.getDailyStats(date).asLiveData() + + fun updatePackageStatus(packageInfo: PackageInfo) = viewModelScope.launch { + repository.update(packageInfo) + } + + fun insert(packageInfo: PackageInfo) = viewModelScope.launch { + repository.insert(packageInfo) + } } \ No newline at end of file diff --git a/app/src/main/res/drawable/resource_package.xml b/app/src/main/res/drawable/resource_package.xml new file mode 100644 index 0000000..d41543f --- /dev/null +++ b/app/src/main/res/drawable/resource_package.xml @@ -0,0 +1,15 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="48dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="48dp"> + + <path android:fillColor="#FFE89C" android:pathData="M787.6,387.8v157.7c0,16.6 -13.4,30 -30,30s-30,-13.4 -30,-30v-137.3l58.7,-29c0.6,1.8 0.9,3.6 1.1,5.6 0.1,1 0.2,2 0.2,3z"/> + + <path android:fillColor="#FFC477" android:pathData="M1024,299.6v424.7c0,26.9 -14.9,51 -39,63l-452.4,229.2a70.6,70.6 0,0 1,-31.4 7.4h-0.4c-11.1,-0.1 -22.1,-2.7 -32.2,-8l-0.2,-0.1 -430.7,-228.6C14.5,775.1 0,751.2 0,724.9V299.1a70.4,70.4 0,0 1,8.5 -33.6,70.4 70.4,0 0,1 29.3,-28.8L468.7,8c20,-10.4 43.9,-10.6 64,-0.6l0.2,0.1 452.2,229.1c13.3,6.6 23.8,17 30.5,29.4 5.5,10.1 8.5,21.6 8.5,33.6z"/> + + <path android:fillColor="#FFB655" android:pathData="M1024,299.6v424.7c0,26.9 -14.9,51 -39,63l-452.4,229.2a70.6,70.6 0,0 1,-31.4 7.4h-0.4V520.4l514.7,-254.3c5.5,10.1 8.5,21.6 8.5,33.6z"/> + + <path android:fillColor="#FFD399" android:pathData="M1015.5,266l-229.2,113.3 -56.4,27.9 -229.1,113.2L8.5,265.5a70.4,70.4 0,0 1,29.3 -28.8l184.7,-98 64,-34 182.1,-96.6A70.3,70.3 0,0 1,500.8 0a70.5,70.5 0,0 1,31.8 7.4l0.2,0.1 452.2,229.1c13.3,6.6 23.8,17 30.5,29.4z"/> + + <path android:fillColor="#FFFCE5" android:pathData="M787.6,387.8v157.7c0,16.6 -13.4,30 -30,30s-30,-13.4 -30,-30v-139.6l-226.8,-120 -278.3,-147.3 64,-34 214.3,113.4 270.8,143.3 0.1,0.1c0.6,0.3 1.1,0.7 1.7,1 0.3,0.2 0.6,0.4 0.9,0.6l0,0c2.3,1.6 4.4,3.5 6.1,5.5 0.1,0.1 0.2,0.2 0.2,0.3 0.4,0.5 0.8,1.1 1.2,1.6 0.2,0.3 0.4,0.5 0.6,0.8 0.3,0.4 0.5,0.8 0.8,1.2 0.3,0.4 0.5,0.9 0.8,1.3 0.1,0.3 0.3,0.5 0.4,0.8 0.3,0.6 0.6,1.3 0.9,1.9 0,0.1 0.1,0.2 0.1,0.3 0.3,0.8 0.6,1.7 0.9,2.5 0.6,1.8 0.9,3.6 1.1,5.6 0.1,1 0.2,2 0.2,3z"/> + + <path android:fillColor="#B5B51F" android:pathData="M352.2,602.8c-8.4,-14.3 -26.8,-19.1 -41,-10.7l-92.9,54.5 -19.5,-35.3c-8,-14.5 -26.2,-19.8 -40.7,-11.8s-19.8,26.2 -11.8,40.7l34.4,62.3c0.1,0.1 0.2,0.3 0.3,0.4 0,0.1 0.1,0.2 0.1,0.3 0.1,0.2 0.2,0.4 0.4,0.6 0.2,0.3 0.4,0.6 0.5,0.9 0.2,0.3 0.4,0.6 0.6,0.8 0.2,0.3 0.4,0.6 0.6,0.8 0.2,0.3 0.4,0.5 0.6,0.8 0.2,0.3 0.4,0.5 0.7,0.8 0.2,0.3 0.5,0.5 0.7,0.7 0.2,0.2 0.5,0.5 0.7,0.7 0.2,0.2 0.5,0.5 0.7,0.7 0.2,0.2 0.5,0.4 0.7,0.6 0.3,0.2 0.5,0.5 0.8,0.7 0.2,0.2 0.5,0.4 0.7,0.6 0.3,0.2 0.6,0.4 0.9,0.6 0.2,0.2 0.5,0.3 0.7,0.5 0.3,0.2 0.6,0.4 0.9,0.6 0.2,0.2 0.5,0.3 0.7,0.4 0.3,0.2 0.6,0.3 1,0.5 0.3,0.1 0.5,0.3 0.8,0.4 0.3,0.2 0.7,0.3 1,0.5 0.3,0.1 0.5,0.2 0.8,0.3 0.3,0.1 0.7,0.3 1,0.4 0.3,0.1 0.5,0.2 0.8,0.3 0.3,0.1 0.7,0.2 1,0.3 0.3,0.1 0.6,0.2 0.8,0.2 0.3,0.1 0.7,0.2 1,0.2 0.3,0.1 0.6,0.1 0.9,0.2 0.3,0.1 0.6,0.1 1,0.2 0.3,0.1 0.6,0.1 1,0.2 0.3,0 0.6,0.1 0.9,0.1 0.3,0 0.7,0.1 1,0.1 0.3,0 0.5,0 0.8,0 0.4,0 0.8,0 1.1,0 0.6,0 1.3,-0 1.9,-0.1 0.2,-0 0.3,-0 0.5,-0 0.5,-0 0.9,-0.1 1.4,-0.1 0.2,-0 0.5,-0.1 0.7,-0.1 0.4,-0.1 0.8,-0.1 1.2,-0.2 0.3,-0.1 0.6,-0.1 0.8,-0.2 0.3,-0.1 0.7,-0.2 1,-0.2 0.3,-0.1 0.6,-0.2 0.9,-0.3 0.3,-0.1 0.6,-0.2 1,-0.3 0.3,-0.1 0.6,-0.2 0.9,-0.3 0.3,-0.1 0.6,-0.2 0.9,-0.4 0.3,-0.1 0.6,-0.3 0.9,-0.4 0.3,-0.1 0.6,-0.3 0.9,-0.4 0.3,-0.1 0.6,-0.3 0.9,-0.4 0.2,-0.1 0.4,-0.2 0.6,-0.3 0.1,-0 0.2,-0.1 0.2,-0.1 0.2,-0.1 0.3,-0.2 0.4,-0.2l119.4,-70.1c14.3,-8.4 19.1,-26.8 10.7,-41z"/> + +</vector> diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml index 166ab0e..4d90e0c 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -6,17 +6,122 @@ android:layout_height="match_parent" tools:context=".ui.dashboard.DashboardFragment"> - <TextView - android:id="@+id/text_dashboard" + <!-- 时间范围选择器 --> + <com.google.android.material.tabs.TabLayout + android:id="@+id/tab_date_range" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="8dp" - android:layout_marginTop="8dp" - android:layout_marginEnd="8dp" - android:textAlignment="center" - android:textSize="20sp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent"> + + <com.google.android.material.tabs.TabItem + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="天" /> + + <com.google.android.material.tabs.TabItem + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="周" /> + + <com.google.android.material.tabs.TabItem + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="月" /> + + <com.google.android.material.tabs.TabItem + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="年" /> + </com.google.android.material.tabs.TabLayout> + + <!-- 日期选择器 --> + <LinearLayout + android:id="@+id/layout_date_picker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="16dp" + app:layout_constraintTop_toBottomOf="@id/tab_date_range"> + + <ImageButton + android:id="@+id/btn_previous_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:src="@android:drawable/ic_media_previous" /> + + <TextView + android:id="@+id/text_current_date" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center" + android:textSize="16sp" + tools:text="2025年1月14日" /> + + <ImageButton + android:id="@+id/btn_next_date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:src="@android:drawable/ic_media_next" /> + </LinearLayout> + + <!-- 包裹统计卡片 --> + <androidx.cardview.widget.CardView + android:id="@+id/card_package_stats" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="16dp" + app:cardCornerRadius="8dp" + app:cardElevation="4dp" + app:layout_constraintTop_toBottomOf="@id/layout_date_picker"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="16dp"> + + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:src="@drawable/resource_package" /> + + <TextView + android:id="@+id/text_package_count" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="16dp" + android:textSize="24sp" + android:textStyle="bold" + tools:text="4个" /> + </LinearLayout> + </androidx.cardview.widget.CardView> + + <!-- 包裹列表 --> + <ViewFlipper + android:id="@+id/view_flipper_stats" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_constraintTop_toBottomOf="@id/card_package_stats"> + + <!-- 日视图 --> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recycler_packages" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <!-- 周视图 --> + <include + android:id="@+id/layout_week_stats" + layout="@layout/layout_week_stats" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <!-- 月视图和年视图可以根据需要添加 --> + + </ViewFlipper> + </androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/item_package_dashboard.xml b/app/src/main/res/layout/item_package_dashboard.xml new file mode 100644 index 0000000..b0990bb --- /dev/null +++ b/app/src/main/res/layout/item_package_dashboard.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:layout_marginVertical="4dp" + app:cardCornerRadius="8dp" + app:cardElevation="2dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="12dp"> + + <ImageView + android:id="@+id/img_courier" + android:layout_width="40dp" + android:layout_height="40dp" + tools:src="@android:drawable/ic_menu_gallery" /> + + <LinearLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_weight="1" + android:orientation="vertical"> + + <TextView + android:id="@+id/text_courier_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="16sp" + android:textStyle="bold" + tools:text="某快递" /> + + <TextView + android:id="@+id/text_time" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textColor="@android:color/darker_gray" + android:textSize="12sp" + tools:text="2025-01-14 10:30" /> + </LinearLayout> + + <TextView + android:id="@+id/text_tracking_number" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textColor="@android:color/darker_gray" + tools:text="14-6-7023" /> + </LinearLayout> + +</androidx.cardview.widget.CardView> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_week_stats.xml b/app/src/main/res/layout/layout_week_stats.xml new file mode 100644 index 0000000..0a2d857 --- /dev/null +++ b/app/src/main/res/layout/layout_week_stats.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <!-- 包裹总数统计卡片 --> + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="16dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="16dp"> + + <ImageView + android:layout_width="48dp" + android:layout_height="48dp" + android:src="@drawable/resource_package" /> + + <TextView + android:id="@+id/text_total_packages" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:layout_marginStart="16dp" + android:textSize="24sp" + android:textStyle="bold" + android:text="4个" /> + </LinearLayout> + </androidx.cardview.widget.CardView> + + <!-- 每日包裹数量柱状图 --> + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="200dp" + android:layout_marginHorizontal="16dp" + android:layout_marginBottom="16dp"> + + <com.github.mikephil.charting.charts.BarChart + android:id="@+id/chart_daily_packages" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="8dp" /> + </androidx.cardview.widget.CardView> + + <!-- 快递公司占比饼图 --> + <androidx.cardview.widget.CardView + android:layout_width="match_parent" + android:layout_height="200dp" + android:layout_marginHorizontal="16dp" + android:layout_marginBottom="16dp"> + + <com.github.mikephil.charting.charts.PieChart + android:id="@+id/chart_courier_distribution" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="8dp" /> + </androidx.cardview.widget.CardView> +</LinearLayout> \ No newline at end of file -- Gitblit v1.9.3