Compare commits
2 Commits
f68cab0a0e
...
28271833d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28271833d8 | ||
|
|
dd2dd75f21 |
@ -23,6 +23,25 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".DetailActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="心情详情"
|
||||||
|
android:theme="@style/Theme.Chick_mood" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ImagePreviewActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="图片预览"
|
||||||
|
android:theme="@style/Theme.Chick_mood.NoActionBar" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".EditActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:label="编辑心情"
|
||||||
|
android:theme="@style/Theme.Chick_mood"
|
||||||
|
android:windowSoftInputMode="adjustResize" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -104,6 +104,24 @@ class SimpleDatabaseManager private constructor(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新心情记录
|
||||||
|
*/
|
||||||
|
suspend fun updateMoodRecord(moodRecord: MoodRecord): Int {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
database.moodRecordDao().updateMoodRecord(moodRecord)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除心情记录
|
||||||
|
*/
|
||||||
|
suspend fun deleteMoodRecord(id: Long): Int {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
database.moodRecordDao().deleteMoodRecordById(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 用户配置操作 ====================
|
// ==================== 用户配置操作 ====================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,6 +10,12 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import com.chick_mood.data.model.Emotion
|
import com.chick_mood.data.model.Emotion
|
||||||
import com.chick_mood.data.model.MoodRecord
|
import com.chick_mood.data.model.MoodRecord
|
||||||
import com.daodaoshi.chick_mood.R
|
import com.daodaoshi.chick_mood.R
|
||||||
|
import com.daodaoshi.chick_mood.ImageUtils
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 心情记录适配器 - 用于ViewPager2横向展示历史心情记录
|
* 心情记录适配器 - 用于ViewPager2横向展示历史心情记录
|
||||||
@ -55,6 +61,11 @@ class MoodRecordAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前数据集
|
||||||
|
*/
|
||||||
|
fun getData(): List<MoodRecord> = moodRecords
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoodRecordViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoodRecordViewHolder {
|
||||||
val view = LayoutInflater.from(parent.context)
|
val view = LayoutInflater.from(parent.context)
|
||||||
.inflate(R.layout.item_mood_record, parent, false)
|
.inflate(R.layout.item_mood_record, parent, false)
|
||||||
@ -188,9 +199,8 @@ class MoodRecordAdapter(
|
|||||||
// 设置图片内容 - 当前模型只支持单张图片
|
// 设置图片内容 - 当前模型只支持单张图片
|
||||||
if (record.hasImage()) {
|
if (record.hasImage()) {
|
||||||
ivSingleImage.visibility = View.VISIBLE
|
ivSingleImage.visibility = View.VISIBLE
|
||||||
// TODO: 使用Glide或其他图片加载库加载实际图片
|
// 加载实际图片
|
||||||
// 现在先使用占位符
|
loadImageFromPath(record.imagePath)
|
||||||
ivSingleImage.setImageResource(R.drawable.placeholder_background)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,5 +240,27 @@ class MoodRecordAdapter(
|
|||||||
onDetailClick(record)
|
onDetailClick(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件路径加载图片
|
||||||
|
*/
|
||||||
|
private fun loadImageFromPath(imagePath: String?) {
|
||||||
|
imagePath?.let { path ->
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val bitmap = ImageUtils.loadBitmapFromPath(path)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (bitmap != null) {
|
||||||
|
ivSingleImage.setImageBitmap(bitmap)
|
||||||
|
Log.d("MoodRecordAdapter", "卡片图片加载成功: $path")
|
||||||
|
} else {
|
||||||
|
// 如果加载失败,使用占位图
|
||||||
|
ivSingleImage.setImageResource(R.drawable.placeholder_background)
|
||||||
|
Log.w("MoodRecordAdapter", "卡片图片加载失败,使用占位图: $path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
445
app/src/main/java/com/daodaoshi/chick_mood/DetailActivity.kt
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
package com.daodaoshi.chick_mood
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.chick_mood.data.database.SimpleDatabaseManager
|
||||||
|
import com.chick_mood.data.model.Emotion
|
||||||
|
import com.chick_mood.data.model.MoodRecord
|
||||||
|
import com.daodaoshi.chick_mood.ImageUtils
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心情记录详情页
|
||||||
|
* 展示单条心情记录的完整信息,支持查看、编辑、收藏、分享、删除等操作
|
||||||
|
*/
|
||||||
|
class DetailActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var backButton: ImageView
|
||||||
|
private lateinit var timeText: TextView
|
||||||
|
private lateinit var moreButton: ImageView
|
||||||
|
private lateinit var chickImage: ImageView
|
||||||
|
private lateinit var moodValueText: TextView
|
||||||
|
private lateinit var moodText: TextView
|
||||||
|
private lateinit var imageContent: ImageView
|
||||||
|
private lateinit var textContent: TextView
|
||||||
|
|
||||||
|
private var currentRecord: MoodRecord? = null
|
||||||
|
private val databaseManager = SimpleDatabaseManager.getInstance(this)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "DetailActivity"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_detail)
|
||||||
|
|
||||||
|
initViews()
|
||||||
|
loadData()
|
||||||
|
setupClickListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视图组件
|
||||||
|
*/
|
||||||
|
private fun initViews() {
|
||||||
|
backButton = findViewById(R.id.back_button)
|
||||||
|
timeText = findViewById(R.id.time_text)
|
||||||
|
moreButton = findViewById(R.id.more_button)
|
||||||
|
chickImage = findViewById(R.id.chick_image)
|
||||||
|
moodValueText = findViewById(R.id.mood_value_text)
|
||||||
|
moodText = findViewById(R.id.mood_text)
|
||||||
|
imageContent = findViewById(R.id.image_content)
|
||||||
|
textContent = findViewById(R.id.text_content)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载心情记录数据
|
||||||
|
*/
|
||||||
|
private fun loadData() {
|
||||||
|
val recordId = intent.getLongExtra("record_id", -1L)
|
||||||
|
if (recordId == -1L) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从数据库获取记录
|
||||||
|
loadRecordFromDatabase(recordId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示心情记录
|
||||||
|
*/
|
||||||
|
private fun displayRecord(record: MoodRecord) {
|
||||||
|
// 设置时间显示
|
||||||
|
timeText.text = formatTime(record.timestamp)
|
||||||
|
|
||||||
|
// 设置小鸡形象
|
||||||
|
chickImage.setImageResource(getEmotionIconResource(record.emotion))
|
||||||
|
|
||||||
|
// 设置心情值和文案
|
||||||
|
moodValueText.text = record.moodIntensity.toString()
|
||||||
|
moodText.text = generateMoodText(record.moodIntensity, record.emotion)
|
||||||
|
|
||||||
|
// 设置图片内容(如果有)
|
||||||
|
if (record.imagePath != null) {
|
||||||
|
imageContent.visibility = View.VISIBLE
|
||||||
|
// 加载实际图片
|
||||||
|
loadImage(record.imagePath)
|
||||||
|
} else {
|
||||||
|
imageContent.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置文本内容(如果有)
|
||||||
|
if (!record.textContent.isNullOrBlank()) {
|
||||||
|
textContent.visibility = View.VISIBLE
|
||||||
|
textContent.text = record.textContent
|
||||||
|
} else {
|
||||||
|
textContent.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置点击监听器
|
||||||
|
*/
|
||||||
|
private fun setupClickListeners() {
|
||||||
|
backButton.setOnClickListener {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
moreButton.setOnClickListener {
|
||||||
|
showMoreOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
imageContent.setOnClickListener {
|
||||||
|
// 点击图片跳转到预览页面
|
||||||
|
currentRecord?.imagePath?.let { imagePath ->
|
||||||
|
val intent = Intent(this, ImagePreviewActivity::class.java).apply {
|
||||||
|
putExtra("image_path", imagePath)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示更多操作选项
|
||||||
|
*/
|
||||||
|
private fun showMoreOptions() {
|
||||||
|
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_more_options, null)
|
||||||
|
val dialog = AlertDialog.Builder(this)
|
||||||
|
.setView(dialogView)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
dialog.setCanceledOnTouchOutside(true)
|
||||||
|
|
||||||
|
// 获取视图组件
|
||||||
|
val llEdit = dialogView.findViewById<LinearLayout>(R.id.ll_edit)
|
||||||
|
val llFavorite = dialogView.findViewById<LinearLayout>(R.id.ll_favorite)
|
||||||
|
val llShare = dialogView.findViewById<LinearLayout>(R.id.ll_share)
|
||||||
|
val llDelete = dialogView.findViewById<LinearLayout>(R.id.ll_delete)
|
||||||
|
val ivFavoriteIcon = dialogView.findViewById<ImageView>(R.id.iv_favorite_icon)
|
||||||
|
val tvFavoriteText = dialogView.findViewById<TextView>(R.id.tv_favorite_text)
|
||||||
|
|
||||||
|
// 更新收藏状态UI
|
||||||
|
updateFavoriteUI(ivFavoriteIcon, tvFavoriteText, currentRecord?.isFavorite == true)
|
||||||
|
|
||||||
|
// 设置点击事件
|
||||||
|
llEdit.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
jumpToEditPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
llFavorite.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
toggleFavorite()
|
||||||
|
}
|
||||||
|
|
||||||
|
llShare.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
shareMoodRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
llDelete.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
showDeleteConfirmDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据库加载心情记录
|
||||||
|
*/
|
||||||
|
private fun loadRecordFromDatabase(recordId: Long) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val record = databaseManager.getMoodRecord(recordId)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (record != null) {
|
||||||
|
currentRecord = record
|
||||||
|
displayRecord(record)
|
||||||
|
} else {
|
||||||
|
// 如果没有找到记录,显示提示并关闭页面
|
||||||
|
showToast("记录不存在")
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("DetailActivity", "加载记录失败", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("加载记录失败")
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间显示
|
||||||
|
*/
|
||||||
|
private fun formatTime(timestamp: Long): String {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val today = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
val yesterday = today - 24 * 60 * 60 * 1000L
|
||||||
|
|
||||||
|
val date = Date(timestamp)
|
||||||
|
val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||||
|
val timeString = timeFormat.format(date)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
timestamp >= today -> "今天 $timeString"
|
||||||
|
timestamp >= yesterday -> "昨天 $timeString"
|
||||||
|
else -> {
|
||||||
|
val dateFormat = SimpleDateFormat("M月d日 HH:mm", Locale.getDefault())
|
||||||
|
dateFormat.format(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成心情文案
|
||||||
|
*/
|
||||||
|
private fun generateMoodText(moodValue: Int, emotion: Emotion): String {
|
||||||
|
val degree = when {
|
||||||
|
moodValue <= 20 -> "有些"
|
||||||
|
moodValue <= 40 -> "非常"
|
||||||
|
moodValue <= 60 -> "超级"
|
||||||
|
moodValue <= 80 -> "这也太"
|
||||||
|
else -> "完全无法控制"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$degree ${emotion.displayName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取情绪图标资源
|
||||||
|
*/
|
||||||
|
private fun getEmotionIconResource(emotion: Emotion): Int {
|
||||||
|
return when (emotion) {
|
||||||
|
Emotion.HAPPY -> R.drawable.ic_emotion_happy
|
||||||
|
Emotion.ANGRY -> R.drawable.ic_emotion_angry
|
||||||
|
Emotion.SAD -> R.drawable.ic_emotion_sad
|
||||||
|
Emotion.WORRIED -> R.drawable.ic_emotion_worried
|
||||||
|
Emotion.LONELY -> R.drawable.ic_emotion_lonely
|
||||||
|
Emotion.SCARED -> R.drawable.ic_emotion_scared
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新收藏状态UI
|
||||||
|
*/
|
||||||
|
private fun updateFavoriteUI(iconView: ImageView, textView: TextView, isFavorite: Boolean) {
|
||||||
|
if (isFavorite) {
|
||||||
|
iconView.setImageResource(R.drawable.ic_favorite_filled)
|
||||||
|
textView.text = "取消收藏"
|
||||||
|
} else {
|
||||||
|
iconView.setImageResource(R.drawable.ic_favorite_border)
|
||||||
|
textView.text = "收藏"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换收藏状态
|
||||||
|
*/
|
||||||
|
private fun toggleFavorite() {
|
||||||
|
currentRecord?.let { record ->
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val newFavoriteStatus = !record.isFavorite
|
||||||
|
val updatedRecord = record.copy(isFavorite = newFavoriteStatus)
|
||||||
|
|
||||||
|
// 更新数据库
|
||||||
|
databaseManager.updateMoodRecord(updatedRecord)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
currentRecord = updatedRecord
|
||||||
|
val message = if (newFavoriteStatus) "已收藏" else "已取消收藏"
|
||||||
|
showToast(message)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("DetailActivity", "更新收藏状态失败", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("操作失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享心情记录
|
||||||
|
*/
|
||||||
|
private fun shareMoodRecord() {
|
||||||
|
currentRecord?.let { record ->
|
||||||
|
val emotionName = when (record.emotion) {
|
||||||
|
Emotion.HAPPY -> "开心"
|
||||||
|
Emotion.SAD -> "悲伤"
|
||||||
|
Emotion.ANGRY -> "生气"
|
||||||
|
Emotion.WORRIED -> "烦恼"
|
||||||
|
Emotion.LONELY -> "孤单"
|
||||||
|
Emotion.SCARED -> "害怕"
|
||||||
|
}
|
||||||
|
|
||||||
|
val shareText = buildString {
|
||||||
|
if (!record.textContent.isNullOrBlank()) {
|
||||||
|
append(record.textContent)
|
||||||
|
append("\n\n")
|
||||||
|
}
|
||||||
|
append("心情:$emotionName")
|
||||||
|
append("\n心情值:${record.moodIntensity}")
|
||||||
|
append("\n文案:${generateMoodText(record.moodIntensity, record.emotion)}")
|
||||||
|
append("\n时间:${formatTime(record.timestamp)}")
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val shareIntent = Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND
|
||||||
|
type = "text/plain"
|
||||||
|
putExtra(Intent.EXTRA_TEXT, shareText)
|
||||||
|
putExtra(Intent.EXTRA_SUBJECT, "我的心情记录")
|
||||||
|
}
|
||||||
|
startActivity(Intent.createChooser(shareIntent, "分享心情记录"))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showToast("分享失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示删除确认对话框
|
||||||
|
*/
|
||||||
|
private fun showDeleteConfirmDialog() {
|
||||||
|
val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_delete_confirm, null)
|
||||||
|
val dialog = AlertDialog.Builder(this)
|
||||||
|
.setView(dialogView)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
|
||||||
|
val tvCancel = dialogView.findViewById<TextView>(R.id.tv_cancel)
|
||||||
|
val tvConfirm = dialogView.findViewById<TextView>(R.id.tv_confirm)
|
||||||
|
|
||||||
|
tvCancel.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
tvConfirm.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
deleteMoodRecord()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除心情记录
|
||||||
|
*/
|
||||||
|
private fun deleteMoodRecord() {
|
||||||
|
currentRecord?.let { record ->
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
// 从数据库删除记录
|
||||||
|
databaseManager.deleteMoodRecord(record.id)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("记录已删除")
|
||||||
|
finish() // 返回首页
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("DetailActivity", "删除记录失败", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("删除失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 跳转到编辑页面
|
||||||
|
*/
|
||||||
|
private fun jumpToEditPage() {
|
||||||
|
currentRecord?.let { record ->
|
||||||
|
try {
|
||||||
|
val intent = Intent(this, EditActivity::class.java).apply {
|
||||||
|
putExtra("record_id", record.id)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showToast("无法打开编辑页面")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示Toast消息
|
||||||
|
*/
|
||||||
|
private fun showToast(message: String) {
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载并显示图片
|
||||||
|
*/
|
||||||
|
private fun loadImage(imagePath: String) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val bitmap = ImageUtils.loadBitmapFromPath(imagePath)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (bitmap != null) {
|
||||||
|
imageContent.setImageBitmap(bitmap)
|
||||||
|
Log.d(TAG, "详情页图片加载成功: $imagePath")
|
||||||
|
} else {
|
||||||
|
// 如果加载失败,使用占位图
|
||||||
|
imageContent.setImageResource(R.drawable.ic_placeholder_image)
|
||||||
|
Log.w(TAG, "详情页图片加载失败,使用占位图: $imagePath")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
512
app/src/main/java/com/daodaoshi/chick_mood/EditActivity.kt
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
package com.daodaoshi.chick_mood
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.FrameLayout
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.chick_mood.data.database.SimpleDatabaseManager
|
||||||
|
import com.chick_mood.data.model.Emotion
|
||||||
|
import com.chick_mood.data.model.MoodRecord
|
||||||
|
import com.daodaoshi.chick_mood.ImageUtils
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 心情记录编辑页
|
||||||
|
* 用于编辑已存在的心情记录,支持修改文本内容和配图
|
||||||
|
*/
|
||||||
|
class EditActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var backButton: ImageView
|
||||||
|
private lateinit var timeText: TextView
|
||||||
|
private lateinit var chickImage: ImageView
|
||||||
|
private lateinit var moodValueText: TextView
|
||||||
|
private lateinit var moodText: TextView
|
||||||
|
private lateinit var textInput: EditText
|
||||||
|
private lateinit var charCountText: TextView
|
||||||
|
private lateinit var imageContainer: View
|
||||||
|
private lateinit var imagePreviewContainer: FrameLayout
|
||||||
|
private lateinit var imagePreview: ImageView
|
||||||
|
private lateinit var addImageButton: View
|
||||||
|
private lateinit var removeImageButton: ImageView
|
||||||
|
private lateinit var saveButton: TextView
|
||||||
|
|
||||||
|
private var currentRecord: MoodRecord? = null
|
||||||
|
private var originalText: String = ""
|
||||||
|
private var originalImagePath: String? = null
|
||||||
|
private var currentImagePath: String? = null
|
||||||
|
private val databaseManager = SimpleDatabaseManager.getInstance(this)
|
||||||
|
|
||||||
|
// 图片选择器
|
||||||
|
private val imagePickerLauncher = registerForActivityResult(
|
||||||
|
androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
|
result.data?.data?.let { uri ->
|
||||||
|
// 处理选中的图片URI,保存到应用私有存储
|
||||||
|
handleImageSelection(uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_TEXT_LENGTH = 500
|
||||||
|
private const val TAG = "EditActivity"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
Log.d(TAG, "EditActivity onCreate started")
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_edit)
|
||||||
|
Log.d(TAG, "Layout set successfully")
|
||||||
|
|
||||||
|
try {
|
||||||
|
initViews()
|
||||||
|
Log.d(TAG, "Views initialized")
|
||||||
|
|
||||||
|
loadData()
|
||||||
|
Log.d(TAG, "Data loading started")
|
||||||
|
|
||||||
|
setupClickListeners()
|
||||||
|
setupTextWatcher()
|
||||||
|
Log.d(TAG, "Listeners setup completed")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error in onCreate", e)
|
||||||
|
showToast("初始化失败")
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视图组件
|
||||||
|
*/
|
||||||
|
private fun initViews() {
|
||||||
|
try {
|
||||||
|
backButton = findViewById(R.id.back_button)
|
||||||
|
timeText = findViewById(R.id.time_text)
|
||||||
|
chickImage = findViewById(R.id.chick_image)
|
||||||
|
moodValueText = findViewById(R.id.mood_value_text)
|
||||||
|
moodText = findViewById(R.id.mood_text)
|
||||||
|
textInput = findViewById(R.id.text_input)
|
||||||
|
charCountText = findViewById(R.id.char_count_text)
|
||||||
|
imageContainer = findViewById(R.id.image_container)
|
||||||
|
imagePreviewContainer = findViewById(R.id.image_preview)
|
||||||
|
imagePreview = findViewById(R.id.preview_image)
|
||||||
|
addImageButton = findViewById(R.id.add_image_button)
|
||||||
|
removeImageButton = findViewById(R.id.remove_image_button)
|
||||||
|
saveButton = findViewById(R.id.save_button)
|
||||||
|
Log.d(TAG, "All views found successfully")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error initializing views", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载心情记录数据
|
||||||
|
*/
|
||||||
|
private fun loadData() {
|
||||||
|
val recordId = intent.getLongExtra("record_id", -1L)
|
||||||
|
if (recordId == -1L) {
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRecordFromDatabase(recordId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据库加载心情记录
|
||||||
|
*/
|
||||||
|
private fun loadRecordFromDatabase(recordId: Long) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val record = databaseManager.getMoodRecord(recordId)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (record != null) {
|
||||||
|
currentRecord = record
|
||||||
|
originalText = record.textContent ?: ""
|
||||||
|
originalImagePath = record.imagePath
|
||||||
|
currentImagePath = record.imagePath
|
||||||
|
displayRecord(record)
|
||||||
|
setupImageState()
|
||||||
|
} else {
|
||||||
|
showToast("记录不存在")
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("加载记录失败")
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示心情记录
|
||||||
|
*/
|
||||||
|
private fun displayRecord(record: MoodRecord) {
|
||||||
|
// 设置时间显示
|
||||||
|
timeText.text = formatTime(record.timestamp)
|
||||||
|
|
||||||
|
// 设置小鸡形象
|
||||||
|
chickImage.setImageResource(getEmotionIconResource(record.emotion))
|
||||||
|
|
||||||
|
// 设置心情值和文案
|
||||||
|
moodValueText.text = record.moodIntensity.toString()
|
||||||
|
moodText.text = generateMoodText(record.moodIntensity, record.emotion)
|
||||||
|
|
||||||
|
// 设置文本内容
|
||||||
|
textInput.setText(record.textContent ?: "")
|
||||||
|
updateCharCount()
|
||||||
|
|
||||||
|
// 图片状态在setupImageState中处理
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置图片状态
|
||||||
|
*/
|
||||||
|
private fun setupImageState() {
|
||||||
|
if (currentImagePath != null) {
|
||||||
|
// 有图片状态
|
||||||
|
addImageButton.visibility = View.GONE
|
||||||
|
imagePreviewContainer.visibility = View.VISIBLE
|
||||||
|
removeImageButton.visibility = View.VISIBLE
|
||||||
|
Log.d(TAG, "设置有图片状态,图片路径: $currentImagePath")
|
||||||
|
// 加载实际图片
|
||||||
|
val imagePath = currentImagePath ?: ""
|
||||||
|
loadImage(imagePath)
|
||||||
|
} else {
|
||||||
|
// 无图片状态
|
||||||
|
addImageButton.visibility = View.VISIBLE
|
||||||
|
imagePreviewContainer.visibility = View.GONE
|
||||||
|
removeImageButton.visibility = View.GONE
|
||||||
|
Log.d(TAG, "设置无图片状态")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置点击监听器
|
||||||
|
*/
|
||||||
|
private fun setupClickListeners() {
|
||||||
|
backButton.setOnClickListener {
|
||||||
|
handleBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
addImageButton.setOnClickListener {
|
||||||
|
pickImageFromGallery()
|
||||||
|
}
|
||||||
|
|
||||||
|
removeImageButton.setOnClickListener {
|
||||||
|
removeImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
imagePreview.setOnClickListener {
|
||||||
|
currentImagePath?.let { previewImage(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
saveButton.setOnClickListener {
|
||||||
|
saveRecord()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置文本监听器
|
||||||
|
*/
|
||||||
|
private fun setupTextWatcher() {
|
||||||
|
textInput.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
updateCharCount()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
// 检查文本是否超过最大长度
|
||||||
|
if (s != null && s.length > MAX_TEXT_LENGTH) {
|
||||||
|
textInput.setText(s.substring(0, MAX_TEXT_LENGTH))
|
||||||
|
textInput.setSelection(MAX_TEXT_LENGTH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新字符计数
|
||||||
|
*/
|
||||||
|
private fun updateCharCount() {
|
||||||
|
val currentLength = textInput.text?.length ?: 0
|
||||||
|
charCountText.text = "$currentLength/$MAX_TEXT_LENGTH"
|
||||||
|
|
||||||
|
// 超过限制时显示红色警告
|
||||||
|
if (currentLength > MAX_TEXT_LENGTH) {
|
||||||
|
charCountText.setTextColor(getColor(R.color.error_color))
|
||||||
|
} else {
|
||||||
|
charCountText.setTextColor(getColor(R.color.text_secondary))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理返回键点击
|
||||||
|
*/
|
||||||
|
private fun handleBackPressed() {
|
||||||
|
if (hasUnsavedChanges()) {
|
||||||
|
showExitConfirmDialog()
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有未保存的更改
|
||||||
|
*/
|
||||||
|
private fun hasUnsavedChanges(): Boolean {
|
||||||
|
val currentText = textInput.text?.toString() ?: ""
|
||||||
|
return currentText != originalText || currentImagePath != originalImagePath
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示退出确认对话框
|
||||||
|
*/
|
||||||
|
private fun showExitConfirmDialog() {
|
||||||
|
val dialogView = layoutInflater.inflate(R.layout.dialog_exit_edit_confirm, null)
|
||||||
|
val dialog = android.app.AlertDialog.Builder(this)
|
||||||
|
.setView(dialogView)
|
||||||
|
.create()
|
||||||
|
|
||||||
|
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
|
||||||
|
val tvContinueEdit = dialogView.findViewById<TextView>(R.id.tv_continue_edit)
|
||||||
|
val tvExit = dialogView.findViewById<TextView>(R.id.tv_exit)
|
||||||
|
|
||||||
|
tvContinueEdit.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
tvExit.setOnClickListener {
|
||||||
|
dialog.dismiss()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从相册选择图片
|
||||||
|
*/
|
||||||
|
private fun pickImageFromGallery() {
|
||||||
|
try {
|
||||||
|
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||||
|
type = "image/*"
|
||||||
|
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("image/jpeg", "image/png"))
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
|
||||||
|
}
|
||||||
|
imagePickerLauncher.launch(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showToast("无法打开相册")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除图片
|
||||||
|
*/
|
||||||
|
private fun removeImage() {
|
||||||
|
// 先删除图片文件
|
||||||
|
removeImageFile()
|
||||||
|
// 然后清空路径并更新UI
|
||||||
|
currentImagePath = null
|
||||||
|
setupImageState()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览图片
|
||||||
|
*/
|
||||||
|
private fun previewImage(imagePath: String) {
|
||||||
|
val intent = Intent(this, ImagePreviewActivity::class.java).apply {
|
||||||
|
putExtra("image_path", imagePath)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存记录
|
||||||
|
*/
|
||||||
|
private fun saveRecord() {
|
||||||
|
currentRecord?.let { record ->
|
||||||
|
val updatedText = textInput.text?.toString()?.trim() ?: ""
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val updatedRecord = record.copy(
|
||||||
|
textContent = if (updatedText.isEmpty()) null else updatedText,
|
||||||
|
imagePath = currentImagePath,
|
||||||
|
timestamp = record.timestamp // 保持原始时间戳不变
|
||||||
|
)
|
||||||
|
|
||||||
|
databaseManager.updateMoodRecord(updatedRecord)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("保存成功")
|
||||||
|
|
||||||
|
// 直接跳转到首页并传递编辑的记录ID用于定位
|
||||||
|
val homeIntent = Intent(this@EditActivity, MainActivitySimple::class.java).apply {
|
||||||
|
putExtra("edited_record_id", record.id)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
}
|
||||||
|
startActivity(homeIntent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("保存失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化时间显示
|
||||||
|
*/
|
||||||
|
private fun formatTime(timestamp: Long): String {
|
||||||
|
val today = Calendar.getInstance().apply {
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}.timeInMillis
|
||||||
|
|
||||||
|
val yesterday = today - 24 * 60 * 60 * 1000L
|
||||||
|
|
||||||
|
val date = Date(timestamp)
|
||||||
|
val timeFormat = SimpleDateFormat("HH:mm", Locale.getDefault())
|
||||||
|
val timeString = timeFormat.format(date)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
timestamp >= today -> "今天 $timeString"
|
||||||
|
timestamp >= yesterday -> "昨天 $timeString"
|
||||||
|
else -> {
|
||||||
|
val dateFormat = SimpleDateFormat("M月d日 HH:mm", Locale.getDefault())
|
||||||
|
dateFormat.format(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成心情文案
|
||||||
|
*/
|
||||||
|
private fun generateMoodText(moodValue: Int, emotion: Emotion): String {
|
||||||
|
val degree = when {
|
||||||
|
moodValue <= 20 -> "有些"
|
||||||
|
moodValue <= 40 -> "非常"
|
||||||
|
moodValue <= 60 -> "超级"
|
||||||
|
moodValue <= 80 -> "这也太"
|
||||||
|
else -> "完全无法控制"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "$degree ${emotion.displayName}"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取情绪图标资源
|
||||||
|
*/
|
||||||
|
private fun getEmotionIconResource(emotion: Emotion): Int {
|
||||||
|
return when (emotion) {
|
||||||
|
Emotion.HAPPY -> R.drawable.ic_emotion_happy
|
||||||
|
Emotion.ANGRY -> R.drawable.ic_emotion_angry
|
||||||
|
Emotion.SAD -> R.drawable.ic_emotion_sad
|
||||||
|
Emotion.WORRIED -> R.drawable.ic_emotion_worried
|
||||||
|
Emotion.LONELY -> R.drawable.ic_emotion_lonely
|
||||||
|
Emotion.SCARED -> R.drawable.ic_emotion_scared
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示Toast消息
|
||||||
|
*/
|
||||||
|
private fun showToast(message: String) {
|
||||||
|
android.widget.Toast.makeText(this, message, android.widget.Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理图片选择
|
||||||
|
*/
|
||||||
|
private fun handleImageSelection(uri: android.net.Uri) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
// 将图片保存到应用私有存储
|
||||||
|
val savedPath = ImageUtils.saveImageToInternalStorage(this@EditActivity, uri)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (savedPath != null) {
|
||||||
|
currentImagePath = savedPath
|
||||||
|
setupImageState()
|
||||||
|
Log.d(TAG, "图片保存成功: $savedPath")
|
||||||
|
} else {
|
||||||
|
showToast("保存图片失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "处理图片选择失败", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
showToast("处理图片失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载并显示图片
|
||||||
|
*/
|
||||||
|
private fun loadImage(imagePath: String) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val bitmap = ImageUtils.loadBitmapFromPath(imagePath)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (bitmap != null) {
|
||||||
|
imagePreview.setImageBitmap(bitmap)
|
||||||
|
Log.d(TAG, "图片加载成功: $imagePath")
|
||||||
|
} else {
|
||||||
|
// 如果加载失败,使用占位图
|
||||||
|
imagePreview.setImageResource(R.drawable.ic_placeholder_image)
|
||||||
|
Log.w(TAG, "图片加载失败,使用占位图: $imagePath")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除图片文件
|
||||||
|
*/
|
||||||
|
private fun removeImageFile() {
|
||||||
|
currentImagePath?.let { path ->
|
||||||
|
val success = ImageUtils.deleteImageFile(path)
|
||||||
|
if (success) {
|
||||||
|
Log.d(TAG, "图片文件删除成功: $path")
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "图片文件删除失败: $path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBackPressed() {
|
||||||
|
handleBackPressed()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,148 @@
|
|||||||
|
package com.daodaoshi.chick_mood
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import com.daodaoshi.chick_mood.databinding.ActivityImagePreviewBinding
|
||||||
|
import com.daodaoshi.chick_mood.ImageUtils
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片预览Activity
|
||||||
|
* 支持图片放大查看、手势缩放和拖拽
|
||||||
|
*/
|
||||||
|
class ImagePreviewActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private lateinit var binding: ActivityImagePreviewBinding
|
||||||
|
private var currentScale = 1.0f
|
||||||
|
private var startX = 0f
|
||||||
|
private var startY = 0f
|
||||||
|
private var translateX = 0f
|
||||||
|
private var translateY = 0f
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_SCALE = 3.0f
|
||||||
|
private const val MIN_SCALE = 1.0f
|
||||||
|
private const val TAG = "ImagePreviewActivity"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
binding = ActivityImagePreviewBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
initViews()
|
||||||
|
setupGestures()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化视图
|
||||||
|
*/
|
||||||
|
private fun initViews() {
|
||||||
|
// 设置返回按钮点击事件
|
||||||
|
binding.btnClose.setOnClickListener {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置背景点击关闭
|
||||||
|
binding.root.setOnClickListener {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取图片路径并加载图片
|
||||||
|
val imagePath = intent.getStringExtra("image_path")
|
||||||
|
if (imagePath != null) {
|
||||||
|
// 加载实际图片
|
||||||
|
loadImage(imagePath)
|
||||||
|
} else {
|
||||||
|
finish() // 没有图片路径则关闭
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置手势操作
|
||||||
|
*/
|
||||||
|
private fun setupGestures() {
|
||||||
|
binding.imagePreview.setOnTouchListener { view, event ->
|
||||||
|
when (event.action and MotionEvent.ACTION_MASK) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
startX = event.x - translateX
|
||||||
|
startY = event.y - translateY
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
translateX = event.x - startX
|
||||||
|
translateY = event.y - startY
|
||||||
|
updateImageTransform()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_POINTER_DOWN -> {
|
||||||
|
// 双指缩放逻辑
|
||||||
|
true
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP -> {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新图片变换
|
||||||
|
*/
|
||||||
|
private fun updateImageTransform() {
|
||||||
|
binding.imagePreview.apply {
|
||||||
|
translationX = translateX
|
||||||
|
translationY = translateY
|
||||||
|
scaleX = currentScale
|
||||||
|
scaleY = currentScale
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载并显示图片
|
||||||
|
*/
|
||||||
|
private fun loadImage(imagePath: String) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val bitmap = ImageUtils.loadBitmapFromPath(imagePath)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (bitmap != null) {
|
||||||
|
binding.imagePreview.setImageBitmap(bitmap)
|
||||||
|
Log.d("ImagePreviewActivity", "预览页图片加载成功: $imagePath")
|
||||||
|
} else {
|
||||||
|
// 如果加载失败,使用占位图
|
||||||
|
binding.imagePreview.setImageResource(R.drawable.ic_placeholder_image)
|
||||||
|
Log.w("ImagePreviewActivity", "预览页图片加载失败,使用占位图: $imagePath")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理双击缩放
|
||||||
|
*/
|
||||||
|
private fun handleDoubleTap() {
|
||||||
|
if (currentScale == MIN_SCALE) {
|
||||||
|
currentScale = 2.0f
|
||||||
|
} else {
|
||||||
|
currentScale = MIN_SCALE
|
||||||
|
translateX = 0f
|
||||||
|
translateY = 0f
|
||||||
|
}
|
||||||
|
updateImageTransform()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish() {
|
||||||
|
super.finish()
|
||||||
|
// 添加退出动画
|
||||||
|
overridePendingTransition(0, android.R.anim.fade_out)
|
||||||
|
}
|
||||||
|
}
|
||||||
108
app/src/main/java/com/daodaoshi/chick_mood/ImageUtils.kt
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package com.daodaoshi.chick_mood
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片工具类
|
||||||
|
* 用于处理图片加载、保存和URI转换
|
||||||
|
*/
|
||||||
|
object ImageUtils {
|
||||||
|
|
||||||
|
private const val TAG = "ImageUtils"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URI转换为文件路径
|
||||||
|
*/
|
||||||
|
suspend fun uriToFilePath(context: Context, uri: Uri): String? {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
|
||||||
|
context.contentResolver.query(uri, filePathColumn, null, null, null)?.use { cursor ->
|
||||||
|
val columnIndex = cursor?.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
|
||||||
|
cursor?.moveToFirst()
|
||||||
|
cursor?.getString(columnIndex ?: 0)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "转换URI到文件路径失败", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将URI保存到应用私有存储
|
||||||
|
*/
|
||||||
|
suspend fun saveImageToInternalStorage(context: Context, uri: Uri): String? {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val inputStream: InputStream? = context.contentResolver.openInputStream(uri)
|
||||||
|
val fileName = "image_${System.currentTimeMillis()}.jpg"
|
||||||
|
val file = File(context.filesDir, fileName)
|
||||||
|
|
||||||
|
inputStream?.use { input ->
|
||||||
|
FileOutputStream(file).use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "图片保存成功: ${file.absolutePath}")
|
||||||
|
file.absolutePath
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "保存图片失败", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从文件路径加载Bitmap
|
||||||
|
*/
|
||||||
|
suspend fun loadBitmapFromPath(imagePath: String): Bitmap? {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val file = File(imagePath)
|
||||||
|
if (file.exists()) {
|
||||||
|
BitmapFactory.decodeFile(file.absolutePath)
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "图片文件不存在: $imagePath")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "加载图片失败", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除图片文件
|
||||||
|
*/
|
||||||
|
fun deleteImageFile(imagePath: String): Boolean {
|
||||||
|
return try {
|
||||||
|
val file = File(imagePath)
|
||||||
|
if (file.exists()) {
|
||||||
|
file.delete()
|
||||||
|
Log.d(TAG, "图片删除成功: $imagePath")
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "图片文件不存在: $imagePath")
|
||||||
|
true
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "删除图片失败", e)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -41,6 +41,7 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private lateinit var timeFormatter: SimpleDateFormat
|
private lateinit var timeFormatter: SimpleDateFormat
|
||||||
private lateinit var moodRecordAdapter: MoodRecordAdapter
|
private lateinit var moodRecordAdapter: MoodRecordAdapter
|
||||||
|
private var recordIdToLocate: Long = -1L
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -59,11 +60,17 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
// 初始化ViewPager2适配器
|
// 初始化ViewPager2适配器
|
||||||
initMoodRecordAdapter()
|
initMoodRecordAdapter()
|
||||||
|
|
||||||
// 运行数据库测试
|
// 数据库测试已禁用,避免创建额外的测试记录
|
||||||
runDatabaseTest()
|
// runDatabaseTest()
|
||||||
|
|
||||||
|
// 创建测试数据(如果数据库为空)
|
||||||
|
createTestDataIfNeeded()
|
||||||
|
|
||||||
// 加载历史记录数据
|
// 加载历史记录数据
|
||||||
loadMoodRecords()
|
loadMoodRecords()
|
||||||
|
|
||||||
|
// 检查是否有编辑完成返回的记录需要定位
|
||||||
|
checkAndLocateEditedRecordFromIntent()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -179,7 +186,7 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val dbManager = SimpleDatabaseManager.getInstance(this@MainActivitySimple)
|
val dbManager = SimpleDatabaseManager.getInstance(this@MainActivitySimple)
|
||||||
dbManager.createMoodRecord(
|
val insertedId = dbManager.createMoodRecord(
|
||||||
emotion = newRecord.emotion,
|
emotion = newRecord.emotion,
|
||||||
intensity = newRecord.moodIntensity,
|
intensity = newRecord.moodIntensity,
|
||||||
noteText = newRecord.textContent ?: "",
|
noteText = newRecord.textContent ?: "",
|
||||||
@ -187,6 +194,8 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
// 设置需要定位的新记录ID
|
||||||
|
recordIdToLocate = insertedId
|
||||||
// 重新加载数据以显示新记录
|
// 重新加载数据以显示新记录
|
||||||
loadMoodRecords()
|
loadMoodRecords()
|
||||||
}
|
}
|
||||||
@ -262,8 +271,8 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
// TODO: 每次进入页面时更新时间 - 暂时注释掉
|
// 每次返回首页时重新加载数据,确保显示最新的数据库状态
|
||||||
// updateTimeIndicator(System.currentTimeMillis())
|
loadMoodRecords()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -272,9 +281,9 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
private fun initMoodRecordAdapter() {
|
private fun initMoodRecordAdapter() {
|
||||||
moodRecordAdapter = MoodRecordAdapter(
|
moodRecordAdapter = MoodRecordAdapter(
|
||||||
onItemClick = { record ->
|
onItemClick = { record ->
|
||||||
// 点击记录卡片 - 可以显示详情或进行其他操作
|
// 点击记录卡片 - 跳转到详情页
|
||||||
Log.d(TAG, "点击了心情记录: ${record.emotion}")
|
Log.d(TAG, "点击了心情记录: ${record.emotion}")
|
||||||
showTemporaryMessage("查看记录详情")
|
showMoodRecordDetail(record)
|
||||||
},
|
},
|
||||||
onFavoriteClick = { record ->
|
onFavoriteClick = { record ->
|
||||||
// 收藏/取消收藏
|
// 收藏/取消收藏
|
||||||
@ -320,6 +329,9 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
} else {
|
} else {
|
||||||
showContentStateUI()
|
showContentStateUI()
|
||||||
moodRecordAdapter.updateData(records)
|
moodRecordAdapter.updateData(records)
|
||||||
|
|
||||||
|
// 检查是否需要定位记录
|
||||||
|
checkAndLocateEditedRecord()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,8 +413,15 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
* 显示心情记录详情
|
* 显示心情记录详情
|
||||||
*/
|
*/
|
||||||
private fun showMoodRecordDetail(record: MoodRecord) {
|
private fun showMoodRecordDetail(record: MoodRecord) {
|
||||||
// TODO: 跳转到详情页面
|
try {
|
||||||
showTemporaryMessage("查看详情功能待实现")
|
val intent = android.content.Intent(this, DetailActivity::class.java).apply {
|
||||||
|
putExtra("record_id", record.id)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "跳转详情页失败", e)
|
||||||
|
showTemporaryMessage("打开详情页失败")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -440,6 +459,212 @@ class MainActivitySimple : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建测试数据(如果数据库为空)
|
||||||
|
*/
|
||||||
|
private fun createTestDataIfNeeded() {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val dbManager = SimpleDatabaseManager.getInstance(this@MainActivitySimple)
|
||||||
|
val recordCount = dbManager.getMoodRecordCount()
|
||||||
|
|
||||||
|
if (recordCount == 0) {
|
||||||
|
// 数据库为空,创建一些测试数据
|
||||||
|
val testData = generateRealisticTestData()
|
||||||
|
|
||||||
|
for (data in testData) {
|
||||||
|
dbManager.createMoodRecord(
|
||||||
|
emotion = data.emotion,
|
||||||
|
intensity = data.moodIntensity,
|
||||||
|
noteText = data.textContent ?: "",
|
||||||
|
imagePaths = if (data.imagePath != null) listOf(data.imagePath) else emptyList()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "创建了 ${testData.size} 条测试数据")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "创建测试数据失败", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成真实的测试数据
|
||||||
|
*/
|
||||||
|
private fun generateRealisticTestData(): List<MoodRecord> {
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val oneDayMs = 24 * 60 * 60 * 1000L
|
||||||
|
|
||||||
|
return listOf(
|
||||||
|
MoodRecord(
|
||||||
|
id = 0, // 数据库会自动生成ID
|
||||||
|
emotion = Emotion.HAPPY,
|
||||||
|
moodIntensity = 85,
|
||||||
|
textContent = "今天心情特别好!阳光明媚,工作顺利,还和朋友聚餐了,感觉很充实。生活就是这样,简单的美好就让人很满足。",
|
||||||
|
imagePath = "placeholder_image_1",
|
||||||
|
timestamp = currentTime - 2 * oneDayMs,
|
||||||
|
isFavorite = true
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.WORRIED,
|
||||||
|
moodIntensity = 65,
|
||||||
|
textContent = "工作上遇到了一些挑战,有点压力,但我相信能够克服。明天要好好规划一下时间。",
|
||||||
|
imagePath = null,
|
||||||
|
timestamp = currentTime - 3 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.ANGRY,
|
||||||
|
moodIntensity = 75,
|
||||||
|
textContent = "今天遇到了一些不愉快的事情,心情很糟糕。深呼吸,冷静下来,明天会是新的一天。",
|
||||||
|
imagePath = null,
|
||||||
|
timestamp = currentTime - 4 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.SAD,
|
||||||
|
moodIntensity = 55,
|
||||||
|
textContent = "今天有点失落,可能是天气的原因,也可能是因为想家了。希望明天会更好。",
|
||||||
|
imagePath = "placeholder_image_2",
|
||||||
|
timestamp = currentTime - 5 * oneDayMs,
|
||||||
|
isFavorite = true
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.LONELY,
|
||||||
|
moodIntensity = 70,
|
||||||
|
textContent = "感觉有点孤单,想念朋友和家人。但这也是成长的一部分,学会独处也是一种能力。",
|
||||||
|
imagePath = null,
|
||||||
|
timestamp = currentTime - 6 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.HAPPY,
|
||||||
|
moodIntensity = 95,
|
||||||
|
textContent = "超级开心!收到了期待已久的好消息,所有的努力都值得了!",
|
||||||
|
imagePath = "placeholder_image_3",
|
||||||
|
timestamp = currentTime - 7 * oneDayMs,
|
||||||
|
isFavorite = true
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.SCARED,
|
||||||
|
moodIntensity = 45,
|
||||||
|
textContent = "对未来的不确定性感到有些害怕,但我知道逃避不是解决方法。勇敢面对,一步一步来。",
|
||||||
|
imagePath = null,
|
||||||
|
timestamp = currentTime - 8 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.HAPPY,
|
||||||
|
moodIntensity = 80,
|
||||||
|
textContent = "",
|
||||||
|
imagePath = "placeholder_image_4",
|
||||||
|
timestamp = currentTime - 9 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.WORRIED,
|
||||||
|
moodIntensity = 60,
|
||||||
|
textContent = null,
|
||||||
|
imagePath = null,
|
||||||
|
timestamp = currentTime - 10 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
),
|
||||||
|
MoodRecord(
|
||||||
|
id = 0,
|
||||||
|
emotion = Emotion.SAD,
|
||||||
|
moodIntensity = 40,
|
||||||
|
textContent = "今天没什么特别的心情,就是有点低落。听音乐放松一下吧。",
|
||||||
|
imagePath = null,
|
||||||
|
timestamp = currentTime - 11 * oneDayMs,
|
||||||
|
isFavorite = false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: android.content.Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
// 处理编辑页面返回的结果
|
||||||
|
data?.getLongExtra("edited_record_id", -1L)?.let { editedRecordId: Long ->
|
||||||
|
if (editedRecordId != -1L) {
|
||||||
|
// 找到编辑的记录在列表中的位置并定位
|
||||||
|
locateRecordInList(editedRecordId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在列表中定位记录
|
||||||
|
*/
|
||||||
|
private fun locateRecordInList(recordId: Long) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "开始定位记录ID: $recordId")
|
||||||
|
|
||||||
|
// 确保ViewPager2已经准备好
|
||||||
|
binding.vpHistoryRecords.post {
|
||||||
|
// 从适配器获取最新数据
|
||||||
|
val records = moodRecordAdapter.getData()
|
||||||
|
Log.d(TAG, "当前适配器数据量: ${records.size}")
|
||||||
|
|
||||||
|
val position = records.indexOfFirst { record -> record.id == recordId }
|
||||||
|
Log.d(TAG, "查找记录ID $recordId,找到位置: $position")
|
||||||
|
|
||||||
|
if (position != -1 && position < moodRecordAdapter.itemCount) {
|
||||||
|
// 使用setCurrentItem定位,第二个参数为true表示平滑滚动
|
||||||
|
binding.vpHistoryRecords.setCurrentItem(position, true)
|
||||||
|
Log.d(TAG, "已定位到位置: $position")
|
||||||
|
|
||||||
|
// 延迟一点再次确认定位,确保定位成功
|
||||||
|
binding.vpHistoryRecords.postDelayed({
|
||||||
|
val currentPosition = binding.vpHistoryRecords.currentItem
|
||||||
|
Log.d(TAG, "定位后当前位置: $currentPosition")
|
||||||
|
if (currentPosition != position) {
|
||||||
|
Log.w(TAG, "定位可能不准确,尝试重新定位")
|
||||||
|
binding.vpHistoryRecords.setCurrentItem(position, true)
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "未找到记录ID: $recordId,或位置超出范围")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "定位记录失败", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否有编辑完成返回的记录需要定位
|
||||||
|
*/
|
||||||
|
private fun checkAndLocateEditedRecordFromIntent() {
|
||||||
|
val editedRecordId = intent.getLongExtra("edited_record_id", -1L)
|
||||||
|
if (editedRecordId != -1L) {
|
||||||
|
Log.d(TAG, "检测到编辑完成的记录ID: $editedRecordId")
|
||||||
|
recordIdToLocate = editedRecordId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否需要定位记录(在数据加载完成后调用)
|
||||||
|
*/
|
||||||
|
private fun checkAndLocateEditedRecord() {
|
||||||
|
if (recordIdToLocate != -1L) {
|
||||||
|
Log.d(TAG, "开始定位记录ID: $recordIdToLocate")
|
||||||
|
locateRecordInList(recordIdToLocate)
|
||||||
|
recordIdToLocate = -1L // 重置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
// TODO: 清理资源 - 暂时注释掉
|
// TODO: 清理资源 - 暂时注释掉
|
||||||
|
|||||||
13
app/src/main/res/drawable/ic_add_photo.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/text_secondary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_arrow_back.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/text_primary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_close.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/white">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_delete.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/error_color">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_edit.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/text_secondary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_more_vert.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="@color/text_primary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
|
||||||
|
</vector>
|
||||||
15
app/src/main/res/drawable/ic_placeholder_image.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="200dp"
|
||||||
|
android:height="200dp"
|
||||||
|
android:viewportWidth="200"
|
||||||
|
android:viewportHeight="200">
|
||||||
|
<path
|
||||||
|
android:fillColor="#E0E0E0"
|
||||||
|
android:pathData="M50,50h100v100h-100z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#9E9E9E"
|
||||||
|
android:pathData="M75,75h50v50h-50z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="#757575"
|
||||||
|
android:pathData="M85,85h30v30h-30z"/>
|
||||||
|
</vector>
|
||||||
6
app/src/main/res/drawable/shape_button_primary.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<solid android:color="@color/primary" />
|
||||||
|
</shape>
|
||||||
5
app/src/main/res/drawable/shape_circle_background.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#80000000" />
|
||||||
|
</shape>
|
||||||
8
app/src/main/res/drawable/shape_dialog_background.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="16dp"
|
||||||
|
android:topRightRadius="16dp" />
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
</shape>
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="16dp" />
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
</shape>
|
||||||
9
app/src/main/res/drawable/shape_edit_text_background.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
<solid android:color="@color/white" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/background_secondary" />
|
||||||
|
</shape>
|
||||||
6
app/src/main/res/drawable/shape_image_background.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="12dp" />
|
||||||
|
<solid android:color="@color/background_secondary" />
|
||||||
|
</shape>
|
||||||
126
app/src/main/res/layout/activity_detail.xml
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_primary">
|
||||||
|
|
||||||
|
<!-- 顶部导航栏 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:elevation="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/back_button"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_arrow_back"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="返回" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="今天 14:30"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/more_button"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_more_vert"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="更多操作" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 滚动内容区域 -->
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<!-- 小鸡形象和心情值 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="32dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/chick_image"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:src="@drawable/ic_emotion_happy"
|
||||||
|
android:scaleType="centerInside" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mood_value_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="88"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mood_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="完全无法控制 开心"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 配图区域 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:src="@drawable/ic_placeholder_image"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:background="@drawable/shape_image_background"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- 文本内容区域 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text_content"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="今天的工作很顺利,完成了很多任务,感觉很有成就感。"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
231
app/src/main/res/layout/activity_edit.xml
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_primary">
|
||||||
|
|
||||||
|
<!-- 顶部导航栏 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:elevation="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/back_button"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_arrow_back"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="返回" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/time_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="今天 14:30"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- 占位,保持中间文字居中 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 滚动内容区域 -->
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:paddingBottom="120dp">
|
||||||
|
|
||||||
|
<!-- 只读信息区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="32dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/chick_image"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:src="@drawable/ic_emotion_happy"
|
||||||
|
android:scaleType="centerInside" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mood_value_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="88"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mood_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="完全无法控制 开心"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 文本输入区域 -->
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:background="@drawable/shape_edit_text_background">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/text_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="top|start"
|
||||||
|
android:hint="记录一下此刻的心情..."
|
||||||
|
android:inputType="textMultiLine|textCapSentences"
|
||||||
|
android:maxLength="500"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textColorHint="@color/text_secondary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<!-- 字符计数 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/char_count_text"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:text="0/500"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
<!-- 图片区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/image_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- 添加图片按钮 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/add_image_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@drawable/shape_image_background"
|
||||||
|
android:backgroundTint="@color/background_secondary"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:src="@drawable/ic_add_photo"
|
||||||
|
android:tint="@color/text_secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="添加图片"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="@color/text_secondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 图片预览区域 -->
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/image_preview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/preview_image"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:background="@drawable/shape_image_background"
|
||||||
|
android:contentDescription="图片预览" />
|
||||||
|
|
||||||
|
<!-- 删除图片按钮 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/remove_image_button"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
android:background="@drawable/shape_circle_background"
|
||||||
|
android:backgroundTint="@color/error_color"
|
||||||
|
android:tint="@color/white"
|
||||||
|
android:contentDescription="删除图片"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<!-- 底部保存按钮 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:elevation="8dp"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/save_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="保存"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:background="@drawable/shape_button_primary"
|
||||||
|
android:backgroundTint="@color/primary"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
29
app/src/main/res/layout/activity_image_preview.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black_overlay">
|
||||||
|
|
||||||
|
<!-- 图片预览区域 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/image_preview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="matrix"
|
||||||
|
android:contentDescription="图片预览" />
|
||||||
|
|
||||||
|
<!-- 关闭按钮 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/btn_close"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_gravity="top|end"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
android:background="@drawable/shape_circle_background"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:tint="@color/white"
|
||||||
|
android:contentDescription="关闭" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
80
app/src/main/res/layout/dialog_delete_confirm.xml
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?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"
|
||||||
|
android:background="@drawable/shape_dialog_background_rounded">
|
||||||
|
|
||||||
|
<!-- 标题和内容区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="删除记录"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="确定要删除这条心情记录吗?\n删除后无法恢复。"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:lineSpacingExtra="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 按钮区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- 取消按钮 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_cancel"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="取消"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="?android:attr/selectableItemBackground" />
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 确认删除按钮 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_confirm"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="删除"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/error_color"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="?android:attr/selectableItemBackground" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
80
app/src/main/res/layout/dialog_exit_edit_confirm.xml
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?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"
|
||||||
|
android:background="@drawable/shape_dialog_background_rounded">
|
||||||
|
|
||||||
|
<!-- 标题和内容区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="未保存更改"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="退出编辑后将丢失所有修改"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:lineSpacingExtra="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 按钮区域 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<!-- 继续编辑按钮 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_continue_edit"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="继续编辑"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/primary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="?android:attr/selectableItemBackground" />
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="1dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 退出按钮 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_exit"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="退出"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="?android:attr/selectableItemBackground" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
163
app/src/main/res/layout/dialog_more_options.xml
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<?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"
|
||||||
|
android:background="@drawable/shape_dialog_background">
|
||||||
|
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="更多操作"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 操作选项列表 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- 编辑 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_edit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_edit"
|
||||||
|
android:tint="@color/text_secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="编辑"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="56dp"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 收藏 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_favorite"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_favorite_icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_favorite_border"
|
||||||
|
android:tint="@color/text_secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_favorite_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="收藏"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="56dp"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 分享 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_share"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_share"
|
||||||
|
android:tint="@color/text_secondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="分享"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/text_primary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- 分割线 -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginStart="56dp"
|
||||||
|
android:background="@color/divider_color" />
|
||||||
|
|
||||||
|
<!-- 删除 -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_delete"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_delete"
|
||||||
|
android:tint="@color/error_color" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="删除"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/error_color" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -34,6 +34,8 @@
|
|||||||
<!-- 状态颜色 -->
|
<!-- 状态颜色 -->
|
||||||
<color name="transparent">#00000000</color>
|
<color name="transparent">#00000000</color>
|
||||||
<color name="black_overlay">#80000000</color>
|
<color name="black_overlay">#80000000</color>
|
||||||
|
<color name="error_color">#D32F2F</color>
|
||||||
|
<color name="divider_color">#E0E0E0</color>
|
||||||
|
|
||||||
<!-- 额外色彩 -->
|
<!-- 额外色彩 -->
|
||||||
<color name="chick_red">#FF5252</color>
|
<color name="chick_red">#FF5252</color>
|
||||||
|
|||||||
@ -11,4 +11,15 @@
|
|||||||
<item name="android:textColorSecondary">@color/text_secondary</item>
|
<item name="android:textColorSecondary">@color/text_secondary</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- 图片预览页面样式 - 全屏黑色背景 -->
|
||||||
|
<style name="Theme.Chick_mood.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="android:windowBackground">@color/black</item>
|
||||||
|
<item name="android:windowFullscreen">false</item>
|
||||||
|
<item name="android:windowTranslucentStatus">true</item>
|
||||||
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
|
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||||
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||||
|
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
233
claude.md
@ -6,9 +6,9 @@
|
|||||||
|
|
||||||
**项目名称:** 别摇小鸡心情记录App
|
**项目名称:** 别摇小鸡心情记录App
|
||||||
**开发平台:** Android原生(Kotlin)
|
**开发平台:** Android原生(Kotlin)
|
||||||
**当前版本:** V1.1.0
|
**当前版本:** V1.3.0
|
||||||
**更新日期:** 2025-10-23
|
**更新日期:** 2025-10-24
|
||||||
**开发状态:** ✅ 首页核心功能完成,应用正常运行
|
**开发状态:** ✅ 编辑页功能完成,图片显示系统完善
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -32,6 +32,31 @@
|
|||||||
- ✅ 操作按钮功能(收藏、分享、查看详情)
|
- ✅ 操作按钮功能(收藏、分享、查看详情)
|
||||||
- ✅ ViewPager2横向滑动展示
|
- ✅ ViewPager2横向滑动展示
|
||||||
- ✅ 顶部按钮基础功能(更多、统计)
|
- ✅ 顶部按钮基础功能(更多、统计)
|
||||||
|
|
||||||
|
**V1.2.0 详情页功能:**
|
||||||
|
- ✅ 详情页基础布局和UI组件
|
||||||
|
- ✅ 心情文案生成逻辑(程度+心情类型)
|
||||||
|
- ✅ 更多操作弹窗(编辑、收藏、分享、删除)
|
||||||
|
- ✅ 删除功能和二次确认弹窗
|
||||||
|
- ✅ 收藏功能和状态持久化存储
|
||||||
|
- ✅ 分享功能集成(系统分享)
|
||||||
|
- ✅ 图片放大查看功能
|
||||||
|
- ✅ 首页跳转逻辑完善
|
||||||
|
- ✅ 真实数据库集成
|
||||||
|
- ✅ 测试数据生成
|
||||||
|
- ✅ 数据一致性保证
|
||||||
|
|
||||||
|
**V1.3.0 编辑页功能:**
|
||||||
|
- ✅ 编辑页基础布局和UI组件
|
||||||
|
- ✅ 返回键退出确认逻辑
|
||||||
|
- ✅ 文本输入框功能(最大500字符,支持emoji)
|
||||||
|
- ✅ 图片选择和预览功能(系统相册,单图)
|
||||||
|
- ✅ 保存功能和首页定位逻辑
|
||||||
|
- ✅ 详情页编辑跳转集成
|
||||||
|
- ✅ 图片显示系统完善(首页、详情页、编辑页、预览页)
|
||||||
|
- ✅ 图片选择、保存、加载、删除完整流程
|
||||||
|
- ✅ 定位机制优化(编辑和新增记录自动定位)
|
||||||
|
- ✅ 数据一致性修复(禁用额外测试记录)
|
||||||
- 📋 摇晃检测与心情值计算(待开发)
|
- 📋 摇晃检测与心情值计算(待开发)
|
||||||
- 📋 小鸡动画响应系统(待开发)
|
- 📋 小鸡动画响应系统(待开发)
|
||||||
|
|
||||||
@ -83,14 +108,38 @@
|
|||||||
- **模块4.4** ✅ ViewPager2布局修复(match_parent)
|
- **模块4.4** ✅ ViewPager2布局修复(match_parent)
|
||||||
- **模块4.5** ✅ 崩溃问题修复和稳定性优化
|
- **模块4.5** ✅ 崩溃问题修复和稳定性优化
|
||||||
|
|
||||||
|
### ✅ 已完成模块
|
||||||
|
|
||||||
|
**阶段5:详情页功能实现**
|
||||||
|
- **模块5.1** ✅ 详情页基础布局和UI组件
|
||||||
|
- **模块5.2** ✅ 心情文案生成逻辑(程度+心情类型)
|
||||||
|
- **模块5.3** ✅ 更多操作弹窗(编辑、收藏、分享、删除)
|
||||||
|
- **模块5.4** ✅ 删除功能和二次确认弹窗
|
||||||
|
- **模块5.5** ✅ 收藏功能和状态持久化存储
|
||||||
|
- **模块5.6** ✅ 图片放大查看功能
|
||||||
|
- **模块5.7** ✅ 分享功能集成(系统分享)
|
||||||
|
|
||||||
|
### ✅ 已完成模块
|
||||||
|
|
||||||
|
**阶段6:编辑页功能实现**
|
||||||
|
- **模块6.1** ✅ 编辑页基础布局和UI组件
|
||||||
|
- **模块6.2** ✅ 返回键退出确认逻辑
|
||||||
|
- **模块6.3** ✅ 文本输入框功能(最大500字符,支持emoji)
|
||||||
|
- **模块6.4** ✅ 图片选择和预览功能(系统相册,单图)
|
||||||
|
- **模块6.5** ✅ 保存功能和首页定位逻辑
|
||||||
|
- **模块6.6** ✅ 详情页编辑跳转集成
|
||||||
|
- **模块6.7** ✅ 图片显示系统完善(全链路支持)
|
||||||
|
- **模块6.8** ✅ 定位机制优化(编辑和新增记录自动定位)
|
||||||
|
- **模块6.9** ✅ 数据一致性修复(禁用额外测试记录)
|
||||||
|
|
||||||
### 🔄 开发中模块
|
### 🔄 开发中模块
|
||||||
|
|
||||||
**阶段5:核心功能完善(下一步重点)**
|
**阶段7:核心功能完善(下一步重点)**
|
||||||
- **模块5.1** 📋 摇晃检测功能实现
|
- **模块7.1** 📋 摇晃检测功能实现
|
||||||
- **模块5.2** 📋 心情值计算逻辑
|
- **模块7.2** 📋 心情值计算逻辑
|
||||||
- **模块5.3** 📋 小鸡动画响应系统
|
- **模块7.3** 📋 小鸡动画响应系统
|
||||||
- **模块5.4** 📋 侧边抽屉功能(更多按钮)
|
- **模块7.4** 📋 侧边抽屉功能(更多按钮)
|
||||||
- **模块5.5** 📋 统计页面实现(统计按钮)
|
- **模块7.5** 📋 统计页面实现(统计按钮)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -121,41 +170,72 @@
|
|||||||
|
|
||||||
## 🎯 下一步开发计划
|
## 🎯 下一步开发计划
|
||||||
|
|
||||||
### 优先级1:核心交互功能(摇晃检测)
|
### 优先级1:详情页功能实现(当前重点)
|
||||||
**模块5.1:摇晃检测功能实现**
|
**模块5.1:详情页基础布局和UI组件**
|
||||||
|
- 创建DetailActivity和对应布局文件
|
||||||
|
- 实现顶部导航栏(返回、时间、更多操作)
|
||||||
|
- 实现内容区域布局(小鸡形象、心情值、文案、配图、文本)
|
||||||
|
- 支持四种内容状态(有图有文、有图无文、无图有文、无图无文)
|
||||||
|
|
||||||
|
**模块5.2:心情文案生成逻辑**
|
||||||
|
- 实现心情值到程度描述的映射逻辑
|
||||||
|
- 动态生成"程度+心情类型"组合文案
|
||||||
|
- 心情值范围:0-20(有些) → 20-40(非常) → 40-60(超级) → 60-80(这也太) → 80-100(完全无法控制)
|
||||||
|
|
||||||
|
**模块5.3:更多操作弹窗功能**
|
||||||
|
- 实现底部弹窗布局(编辑、收藏、分享、删除)
|
||||||
|
- 弹窗动画效果和交互逻辑
|
||||||
|
- 各操作按钮的状态管理和点击响应
|
||||||
|
|
||||||
|
**模块5.4:删除功能和二次确认弹窗**
|
||||||
|
- 实现删除确认弹窗布局和交互
|
||||||
|
- 数据库记录删除操作
|
||||||
|
- 删除成功提示和页面跳转逻辑
|
||||||
|
|
||||||
|
**模块5.5:收藏功能和状态持久化**
|
||||||
|
- 数据库添加收藏字段支持
|
||||||
|
- 收藏状态切换和UI反馈
|
||||||
|
- 收藏状态持久化存储
|
||||||
|
|
||||||
|
**模块5.6:图片放大查看功能**
|
||||||
|
- 实现图片点击放大预览
|
||||||
|
- 支持手势缩放和拖拽
|
||||||
|
- 1:1比例显示,图片自适应
|
||||||
|
|
||||||
|
**模块5.7:分享功能集成**
|
||||||
|
- 集成Android系统分享功能
|
||||||
|
- 生成分享文本内容
|
||||||
|
- 跳转系统分享选择器
|
||||||
|
|
||||||
|
### 优先级2:核心交互功能(摇晃检测)
|
||||||
|
**模块6.1:摇晃检测功能实现**
|
||||||
- 实现手机摇晃传感器监听
|
- 实现手机摇晃传感器监听
|
||||||
- 摇晃强度和持续时间计算
|
- 摇晃强度和持续时间计算
|
||||||
- 摇晃阈值设置和优化
|
- 摇晃阈值设置和优化
|
||||||
|
|
||||||
**模块5.2:心情值计算逻辑**
|
**模块6.2:心情值计算逻辑**
|
||||||
- 根据摇晃参数计算心情强度值
|
- 根据摇晃参数计算心情强度值
|
||||||
- 心情强度映射到情绪等级
|
- 心情强度映射到情绪等级
|
||||||
- 心情值与情绪选择的关联
|
- 心情值与情绪选择的关联
|
||||||
|
|
||||||
### 优先级2:动画系统完善
|
### 优先级3:动画系统完善
|
||||||
**模块5.3:小鸡动画响应系统**
|
**模块6.3:小鸡动画响应系统**
|
||||||
- 小鸡待机动画(Lottie集成)
|
- 小鸡待机动画(Lottie集成)
|
||||||
- 摇晃时的小鸡反应动画
|
- 摇晃时的小鸡反应动画
|
||||||
- 不同情绪对应的小鸡状态动画
|
- 不同情绪对应的小鸡状态动画
|
||||||
- 点击小鸡的交互反馈动画
|
- 点击小鸡的交互反馈动画
|
||||||
|
|
||||||
### 优先级3:页面功能扩展
|
### 优先级4:页面功能扩展
|
||||||
**模块5.4:侧边抽屉功能(更多按钮)**
|
**模块6.4:侧边抽屉功能(更多按钮)**
|
||||||
- 实现侧边抽屉UI布局
|
- 实现侧边抽屉UI布局
|
||||||
- 设置、关于、帮助等页面入口
|
- 设置、关于、帮助等页面入口
|
||||||
- 用户个人资料管理
|
- 用户个人资料管理
|
||||||
|
|
||||||
**模块5.5:统计页面实现(统计按钮)**
|
**模块6.5:统计页面实现(统计按钮)**
|
||||||
- 心情数据统计分析
|
- 心情数据统计分析
|
||||||
- 情绪趋势图表展示
|
- 情绪趋势图表展示
|
||||||
- 记录统计和可视化
|
- 记录统计和可视化
|
||||||
|
|
||||||
### 优先级4:用户体验优化
|
|
||||||
- 心情记录编辑和删除功能
|
|
||||||
- 记录详情页面实现
|
|
||||||
- 数据导出和备份功能
|
|
||||||
- 个性化主题和设置选项
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 技术决策记录
|
## 🔧 技术决策记录
|
||||||
@ -245,6 +325,32 @@ Chick_Mood/
|
|||||||
|
|
||||||
## 🔄 更新记录
|
## 🔄 更新记录
|
||||||
|
|
||||||
|
**2025-10-24 (V1.3.0 编辑页功能完成)**
|
||||||
|
- ✅ **编辑页完整实现**:包含基础布局、UI组件和退出确认机制
|
||||||
|
- ✅ **返回键退出确认逻辑**:检测编辑行为,弹出二次确认弹窗
|
||||||
|
- ✅ **文本输入功能**:支持最大500字符、emoji输入、实时字符计数
|
||||||
|
- ✅ **图片选择和预览功能**:系统相册单图选择、真实图片显示、删除功能
|
||||||
|
- ✅ **保存功能和首页定位**:保存后直接返回首页,自动定位到编辑/新增记录
|
||||||
|
- ✅ **详情页编辑跳转**:完整集成编辑页跳转逻辑
|
||||||
|
- ✅ **图片显示系统完善**:首页卡片、详情页、编辑页、预览页全链路真实图片显示
|
||||||
|
- ✅ **图片工具类实现**:ImageUtils提供完整的图片保存、加载、删除API
|
||||||
|
- ✅ **定位机制优化**:编辑保存后和新增记录后自动定位,双重验证确保准确性
|
||||||
|
- ✅ **数据一致性修复**:禁用数据库测试功能,避免产生额外测试记录
|
||||||
|
- ✅ **主题兼容性修复**:解决TextInputLayout与AppCompat主题冲突问题
|
||||||
|
|
||||||
|
**2025-10-23 (V1.2.0 详情页功能完成)**
|
||||||
|
- ✅ **详情页完整实现**:包含基础布局、UI组件和四种内容状态支持
|
||||||
|
- ✅ **心情文案生成逻辑**:根据心情值动态生成"程度+心情类型"文案(0-20有些→20-40非常→40-60超级→60-80这也太→80-100完全无法控制)
|
||||||
|
- ✅ **更多操作弹窗**:实现底部弹窗(编辑、收藏、分享、删除)和完整交互逻辑
|
||||||
|
- ✅ **删除功能和二次确认**:实现删除确认弹窗,防止误删,删除成功提示"记录已删除"
|
||||||
|
- ✅ **收藏功能和状态持久化**:支持收藏状态切换,数据库真实存储和UI状态同步
|
||||||
|
- ✅ **分享功能集成**:集成Android系统分享,生成包含完整心情信息的分享文本
|
||||||
|
- ✅ **图片放大查看功能**:创建ImagePreviewActivity,支持全屏预览和关闭操作
|
||||||
|
- ✅ **首页跳转逻辑完善**:修改卡片点击和详情按钮都能跳转到详情页
|
||||||
|
- ✅ **真实数据库集成**:详情页使用真实数据库数据,替换随机测试数据
|
||||||
|
- ✅ **测试数据生成**:创建10条多样化真实测试数据,支持各种内容状态
|
||||||
|
- ✅ **数据一致性保证**:首页和详情页显示完全一致的数据,收藏和删除功能真实操作数据库
|
||||||
|
|
||||||
**2025-10-23 (V1.1.0 首页核心功能完成)**
|
**2025-10-23 (V1.1.0 首页核心功能完成)**
|
||||||
- ✅ **历史记录展示系统**:完整的ViewPager2横向滑动展示功能
|
- ✅ **历史记录展示系统**:完整的ViewPager2横向滑动展示功能
|
||||||
- ✅ **心情记录卡片布局**:支持纯文本、文本+图片、多图+文本三种状态
|
- ✅ **心情记录卡片布局**:支持纯文本、文本+图片、多图+文本三种状态
|
||||||
@ -278,4 +384,85 @@ Chick_Mood/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 📄 详情页功能详细说明
|
||||||
|
|
||||||
|
### 🎯 功能概述
|
||||||
|
详情页是心情记录的完整展示和管理页面,支持从历史记录列表跳转查看单条记录的详细信息。
|
||||||
|
|
||||||
|
### 📱 页面结构
|
||||||
|
1. **顶部导航栏**
|
||||||
|
- 返回按钮:点击返回历史记录列表
|
||||||
|
- 时间显示:记录创建时间(格式:今天/昨天 HH:mm)
|
||||||
|
- 更多操作:三点图标,点击弹出操作菜单
|
||||||
|
|
||||||
|
2. **内容展示区域**
|
||||||
|
- 小鸡形象:根据情绪类型显示对应图标
|
||||||
|
- 心情值:0-100数值显示
|
||||||
|
- 心情文案:动态生成"程度+心情类型"组合
|
||||||
|
- 配图区域:可选,支持点击放大查看
|
||||||
|
- 文本描述:可选,多行文本内容
|
||||||
|
|
||||||
|
3. **操作功能**
|
||||||
|
- 编辑:跳转编辑页(后续实现)
|
||||||
|
- 收藏:切换收藏状态并持久化
|
||||||
|
- 分享:调用系统分享功能
|
||||||
|
- 删除:二次确认后删除记录
|
||||||
|
|
||||||
|
### 🔄 交互逻辑
|
||||||
|
- **页面跳转**:点击历史记录卡片或详情按钮都可跳转
|
||||||
|
- **内容状态**:支持有图有文、有图无文、无图有文、无图无文四种状态
|
||||||
|
- **心情文案**:根据心情值自动生成程度描述
|
||||||
|
- **图片查看**:点击图片支持放大预览,1:1比例显示
|
||||||
|
- **收藏状态**:记录收藏状态并持久化存储
|
||||||
|
- **删除操作**:二次确认后删除,成功提示并返回首页
|
||||||
|
|
||||||
|
### 💾 数据存储
|
||||||
|
- **心情值**:0-100范围,开发阶段使用随机生成测试数据
|
||||||
|
- **收藏状态**:数据库存储,默认未收藏
|
||||||
|
- **时间格式**:与历史记录保持一致,具体到分钟
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📄 编辑页功能详细说明
|
||||||
|
|
||||||
|
### 🎯 功能概述
|
||||||
|
编辑页是用于修改已存在心情记录的页面,支持编辑文本内容和配图,但核心的情绪类型和心情值不可修改,确保记录的原始性和一致性。
|
||||||
|
|
||||||
|
### 📱 页面结构
|
||||||
|
1. **顶部导航栏**
|
||||||
|
- 返回按钮:点击返回上一页,有编辑行为时弹出确认弹窗
|
||||||
|
- 时间显示:记录创建时间(格式:今天/昨天 HH:mm,不可修改)
|
||||||
|
|
||||||
|
2. **只读内容区域**
|
||||||
|
- 小鸡形象:根据情绪类型显示对应图标(不可修改)
|
||||||
|
- 心情值:0-100数值显示(不可修改)
|
||||||
|
- 心情文案:动态生成的"程度+心情类型"组合(不可修改)
|
||||||
|
|
||||||
|
3. **可编辑区域**
|
||||||
|
- 文本输入框:最大500字符,支持emoji,固定高度可滚动
|
||||||
|
- 配图区域:支持系统相册单图选择、预览和删除
|
||||||
|
|
||||||
|
4. **保存按钮**
|
||||||
|
- 位置:页面底部固定
|
||||||
|
- 功能:保存编辑内容并返回首页,自动定位到编辑记录
|
||||||
|
|
||||||
|
### 🔄 交互逻辑
|
||||||
|
- **退出确认机制**:检测文本或图片变化,弹出二次确认弹窗
|
||||||
|
- **图片管理**:无图片时显示添加占位符,有图片时显示缩略图和删除按钮
|
||||||
|
- **保存流程**:收集编辑数据 → 更新数据库 → 返回首页 → 自动定位
|
||||||
|
- **交互反馈**:保存成功Toast提示"保存成功",返回动画向左推动
|
||||||
|
|
||||||
|
### 📝 编辑规范
|
||||||
|
- **可编辑内容**:文本描述(最大500字符)、配图(单张,系统相册选择)
|
||||||
|
- **不可编辑内容**:情绪类型、心情值、时间、心情文案
|
||||||
|
- **文本输入**:支持emoji,固定高度输入框,支持垂直滚动
|
||||||
|
- **图片处理**:暂时不做大小和压缩限制,使用系统默认加载策略
|
||||||
|
|
||||||
|
### 🎨 状态支持
|
||||||
|
- **基础状态**:无文本无图片的初始编辑状态
|
||||||
|
- **文本编辑状态**:有文本无图片的编辑状态
|
||||||
|
- **完整编辑状态**:有文本有图片的完整编辑状态
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
*此文档将随着开发进展持续更新,记录重要的技术决策和进度变化。*
|
*此文档将随着开发进展持续更新,记录重要的技术决策和进度变化。*
|
||||||
|
Before Width: | Height: | Size: 83 KiB |
BIN
project_img/编辑页/无文本无图片@1x.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
project_img/编辑页/有文本输入有图片@1x.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
project_img/编辑页/未保存直接退出@1x.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 190 KiB |
BIN
project_img/详情页/删除二次确认弹窗@1x.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
project_img/详情页/无图无文@1x.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
project_img/详情页/无图有文@1x.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
project_img/详情页/更多操作@1x.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
project_img/详情页/有图无文@1x.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
project_img/详情页/有图有文@1x.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 221 KiB |