From f67cf3b81a00f732ca743431258ae6b78f5f40ab Mon Sep 17 00:00:00 2001 From: tj <1378534974@qq.com> Date: 星期四, 17 四月 2025 15:05:28 +0800 Subject: [PATCH] 11、我的 切换头像 点击切换头像没有显示允许存储权限的窗口,华为的手机目前有 49、首页 实时刷新 点击全部取件或其他分类后,回到上一层,内容没有刷新(5个分类) 52、数据统计 数据统计 1.周月年的柱状图统计逻辑需要修改为只统计快递类的数据 2.按年的图形统计,右下方加上图示说明 53、首页 首页登录 点击用户协议、隐私政策无反应 --- app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt | 10 + app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt | 73 ++++++++++-- app/src/main/java/com/example/firstapp/ui/dashboard/DashboardFragment.kt | 151 +++++++++++++++++++++++++ app/src/main/AndroidManifest.xml | 3 app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt | 14 +- app/src/main/res/layout/layout_week_stats.xml | 26 ++-- app/src/main/java/com/example/firstapp/activity/LoginActivity.kt | 2 app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt | 34 ++++- 8 files changed, 273 insertions(+), 40 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17c8460..4004bcd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -72,6 +72,9 @@ <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <!-- 允许语言识别。 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> + <!-- Android 13 及以上 --> + <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> + <application android:name=".App" android:allowBackup="true" diff --git a/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt b/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt index 36ac653..fcb8a6b 100644 --- a/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt +++ b/app/src/main/java/com/example/firstapp/activity/LoginActivity.kt @@ -55,10 +55,12 @@ binding.tvUserAgreement.setOnClickListener { // 打开用户协议 + startContentActivity("用户协议", "服务使用协议") } binding.tvPrivacyPolicy.setOnClickListener { // 打开隐私政策 + startContentActivity("隐私协议", "隐私保护政策") } } diff --git a/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt b/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt index c3b0d52..e63ba6a 100644 --- a/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt +++ b/app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt @@ -202,7 +202,7 @@ CAST(strftime('%W', dates.date) AS INTEGER) as weekOfYear, COUNT(code.id) as count FROM dates - LEFT JOIN code ON date(code.createTime) = dates.date + LEFT JOIN code ON date(code.createTime) = dates.date and code.category='快递' GROUP BY dates.date ORDER BY dates.date """) @@ -237,12 +237,12 @@ ) SELECT strftime('%m', month_start) as date, - COUNT(code.id) as count, + COUNT(c.id) as count, strftime('%Y-%m-%d', month_start) as weekStart - FROM months - LEFT JOIN code ON strftime('%Y-%m', code.createTime) = strftime('%Y-%m', months.month_start) - GROUP BY months.month_start - ORDER BY months.month_start ASC + FROM months m + LEFT JOIN code c ON strftime('%Y-%m', c.createTime) = strftime('%Y-%m', m.month_start) and c.category='快递' + GROUP BY m.month_start + ORDER BY m.month_start ASC """) fun getYearMonthlyStats(date: Long): Flow<List<DailyStat>> @@ -335,7 +335,7 @@ COUNT(c.id) AS count, '' AS weekStart FROM dates d - LEFT JOIN code c ON date(c.createTime) = d.date_value + LEFT JOIN code c ON date(c.createTime) = d.date_value and c.category='快递' GROUP BY d.date_value ORDER BY d.date_value ASC """) 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 0c5316d..b689d58 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 @@ -21,7 +21,9 @@ 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 @@ -767,6 +769,153 @@ // 创建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 -> @@ -843,7 +992,7 @@ } } - 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") diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt index 03440a8..d86a5c4 100644 --- a/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt @@ -124,6 +124,7 @@ } } + private fun setupAdapters() { binding.expressRecycler.apply { layoutManager = LinearLayoutManager(context) @@ -438,9 +439,16 @@ ) // 加载数据 - homeViewModel.loadExpressData() + homeViewModel.loadAllCategoryData() +// homeViewModel.loadExpressData() +// homeViewModel.loadFinanceData() +// homeViewModel.loadIncomeData() +// homeViewModel.loadFlightData() +// homeViewModel.loadTrainData() + // 检查未读提醒数量 homeViewModel.checkUnreadReminders() + } override fun onPause() { diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt index b367f26..e1357b3 100644 --- a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt @@ -192,17 +192,37 @@ loadDataByType("火车票") } + + fun loadAllCategoryData() { + getFullCategories().forEach { category -> + loadDataByType(category.name) + } + } + + + fun getFullCategories(): List<CategoryConfig> { + return listOf( + CategoryConfig(1, "快递", 0, true), + CategoryConfig(2, "还款", 1, true), + CategoryConfig(3, "收入", 2, true), + CategoryConfig(4, "航班", 3, true), + CategoryConfig(5, "火车票", 4, true) + ) + } + private fun loadCategories() { viewModelScope.launch { try { // 默认完整分类列表 - val fullCategories = listOf( - CategoryConfig(1, "快递", 0, true), - CategoryConfig(2, "还款", 1, true), - CategoryConfig(3, "收入", 2, true), - CategoryConfig(4, "航班", 3, true), - CategoryConfig(5, "火车票", 4, true) - ) +// val fullCategories = listOf( +// CategoryConfig(1, "快递", 0, true), +// CategoryConfig(2, "还款", 1, true), +// CategoryConfig(3, "收入", 2, true), +// CategoryConfig(4, "航班", 3, true), +// CategoryConfig(5, "火车票", 4, true) +// ) + + val fullCategories=getFullCategories() // 基础分类(非会员可见) val basicCategories = listOf( diff --git a/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt b/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt index a7f4af1..608eef6 100644 --- a/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt +++ b/app/src/main/java/com/example/firstapp/ui/profile/EditProfileActivity.kt @@ -36,15 +36,41 @@ private var selectedImageUri: Uri? = null private var loadingDialog: AlertDialog? = null - private val pickImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode == Activity.RESULT_OK) { - result.data?.data?.let { uri -> - selectedImageUri = uri - Glide.with(this) - .load(uri) - .circleCrop() - .into(binding.ivAvatar) - } +// private val pickImage = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> +// if (result.resultCode == Activity.RESULT_OK) { +// result.data?.data?.let { uri -> +// selectedImageUri = uri +// Glide.with(this) +// .load(uri) +// .circleCrop() +// .into(binding.ivAvatar) +// } +// } +// } + + private val pickImageLauncher = registerForActivityResult( + ActivityResultContracts.GetContent() + ) { uri: Uri? -> + uri?.let { + selectedImageUri = uri + // 这里直接处理选中的头像 + Glide.with(this) + .load(it) + .into(binding.ivAvatar) // 替换成你的 ImageView id + + // 如果需要上传可以用 contentResolver.openInputStream(uri) + } + } + + + // 👇 就放在这里 + private val permissionLauncher = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { isGranted: Boolean -> + if (isGranted) { + openGallery() + } else { + Toast.makeText(this, "需要权限才能选择头像", Toast.LENGTH_SHORT).show() } } @@ -78,13 +104,29 @@ } binding.ivAvatar.setOnClickListener { - checkAndRequestPermission() +// checkAndRequestPermission() + checkStoragePermission() } binding.btnSaveBottom.setOnClickListener { saveAndFinish() } } + + fun checkStoragePermission() { + val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + Manifest.permission.READ_MEDIA_IMAGES + } else { + Manifest.permission.READ_EXTERNAL_STORAGE + } + + if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { + permissionLauncher.launch(permission) + } else { + openGallery() + } + } + private fun saveAndFinish() { lifecycleScope.launch { @@ -194,9 +236,16 @@ } } - private fun openGallery() { + private fun openGallery_bak() { val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) - pickImage.launch(intent) +// pickImage.launch(intent) + } + + private fun openGallery() { + pickImageLauncher.launch("image/*") +// val intent = Intent(Intent.ACTION_PICK) +// intent.type = "image/*" +// startActivityForResult(intent, 1001) } private fun showLoading() { diff --git a/app/src/main/res/layout/layout_week_stats.xml b/app/src/main/res/layout/layout_week_stats.xml index 2499968..a328499 100644 --- a/app/src/main/res/layout/layout_week_stats.xml +++ b/app/src/main/res/layout/layout_week_stats.xml @@ -43,20 +43,22 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="16dp" - android:layout_marginBottom="16dp"> + android:layout_marginBottom="16dp" + android:padding="16dp" + > - <HorizontalScrollView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:padding="16dp"> +<!-- <HorizontalScrollView--> +<!-- android:layout_width="match_parent"--> +<!-- android:layout_height="wrap_content"--> +<!-- android:padding="16dp">--> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <!-- 热力图将在这里动态添加 --> - </LinearLayout> - </HorizontalScrollView> +<!-- <LinearLayout--> +<!-- android:layout_width="wrap_content"--> +<!-- android:layout_height="wrap_content"--> +<!-- android:orientation="horizontal">--> +<!-- <!– 热力图将在这里动态添加 –>--> +<!-- </LinearLayout>--> +<!-- </HorizontalScrollView>--> </androidx.cardview.widget.CardView> -- Gitblit v1.9.3