diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d3e64ee..4aa9601 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -19,7 +19,12 @@ "Bash(git init:*)", "Bash(rd:*)", "Bash(git add:*)", - "Bash(git commit:*)" + "Bash(git commit:*)", + "Bash(mkdir:*)", + "Bash(magick:*)", + "Bash(convert:*)", + "Bash(python:*)", + "Bash(rm:*)" ], "deny": [], "ask": [] diff --git a/app/src/main/java/com/chick_mood/ui/adapter/ChickImageAdapter.kt b/app/src/main/java/com/chick_mood/ui/adapter/ChickImageAdapter.kt new file mode 100644 index 0000000..7d54e29 --- /dev/null +++ b/app/src/main/java/com/chick_mood/ui/adapter/ChickImageAdapter.kt @@ -0,0 +1,56 @@ +package com.chick_mood.ui.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.chick_mood.data.model.Emotion +import com.daodaoshi.chick_mood.databinding.ItemChickImageBinding + +/** + * 小鸡形象适配器 + * 用于展示不同情绪对应的小鸡图标 + */ +class ChickImageAdapter : RecyclerView.Adapter() { + + private var emotions: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChickImageViewHolder { + val binding = ItemChickImageBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + return ChickImageViewHolder(binding) + } + + override fun onBindViewHolder(holder: ChickImageViewHolder, position: Int) { + holder.bind(emotions[position]) + } + + override fun getItemCount(): Int = emotions.size + + /** + * 更新数据 + */ + fun updateData(newEmotions: List) { + emotions = newEmotions + notifyDataSetChanged() + } + + inner class ChickImageViewHolder( + private val binding: ItemChickImageBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(emotion: Emotion) { + // 根据情绪类型设置对应的小鸡图标 + val chickDrawable = when (emotion) { + Emotion.HAPPY -> com.daodaoshi.chick_mood.R.drawable.ic_placeholder_chick_happy + Emotion.SAD -> com.daodaoshi.chick_mood.R.drawable.ic_placeholder_chick_sad + Emotion.ANGRY -> com.daodaoshi.chick_mood.R.drawable.ic_placeholder_chick_angry + Emotion.WORRIED -> com.daodaoshi.chick_mood.R.drawable.ic_placeholder_chick_worried + Emotion.LONELY -> com.daodaoshi.chick_mood.R.drawable.ic_placeholder_chick_lonely + Emotion.SCARED -> com.daodaoshi.chick_mood.R.drawable.ic_placeholder_chick_scared + } + + binding.ivChick.setImageResource(chickDrawable) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chick_mood/ui/adapter/MoodCardAdapter.kt b/app/src/main/java/com/chick_mood/ui/adapter/MoodCardAdapter.kt new file mode 100644 index 0000000..0ed3254 --- /dev/null +++ b/app/src/main/java/com/chick_mood/ui/adapter/MoodCardAdapter.kt @@ -0,0 +1,133 @@ +package com.chick_mood.ui.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import androidx.recyclerview.widget.RecyclerView +import com.chick_mood.data.model.MoodRecord +import com.chick_mood.utils.MoodDescriptionGenerator +import com.daodaoshi.chick_mood.databinding.ItemMoodCardBinding + +/** + * 心情卡片适配器 + * 用于展示心情记录的详细信息卡片 + */ +class MoodCardAdapter( + private val onCardClick: (MoodRecord) -> Unit, + private val onDetailClick: (MoodRecord) -> Unit, + private val onFavoriteClick: (MoodRecord) -> Unit, + private val onShareClick: (MoodRecord) -> Unit +) : RecyclerView.Adapter() { + + private var moodRecords: List = emptyList() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoodCardViewHolder { + val binding = ItemMoodCardBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + return MoodCardViewHolder(binding) + } + + override fun onBindViewHolder(holder: MoodCardViewHolder, position: Int) { + holder.bind(moodRecords[position]) + } + + override fun getItemCount(): Int = moodRecords.size + + /** + * 更新数据 + */ + fun updateData(newRecords: List) { + moodRecords = newRecords + notifyDataSetChanged() + } + + /** + * 获取当前数据 + */ + fun getData(): List = moodRecords + + inner class MoodCardViewHolder( + private val binding: ItemMoodCardBinding + ) : RecyclerView.ViewHolder(binding.root) { + + fun bind(record: MoodRecord) { + // 设置心情文案 + val moodDescription = MoodDescriptionGenerator.generateMoodDescription( + record.moodIntensity, record.emotion + ) + binding.tvMoodDescription.text = moodDescription + + // 设置收藏状态 + updateFavoriteIcon(record.isFavorite) + + // 处理文本和图片内容的显示逻辑 + val hasText = !record.textContent.isNullOrEmpty() + val hasImage = !record.imagePath.isNullOrEmpty() + + if (hasText && hasImage) { + // 有图有文:图片在左,文本在右 + binding.ivImage.visibility = View.VISIBLE + binding.tvTextContent.visibility = View.VISIBLE + binding.llMainContent.orientation = LinearLayout.HORIZONTAL + binding.ivImage.layoutParams = LinearLayout.LayoutParams(80, 80).apply { + marginEnd = 12 + } + binding.tvTextContent.text = record.textContent + // TODO: 加载真实图片 + binding.ivImage.setImageResource(com.daodaoshi.chick_mood.R.drawable.placeholder_background) + } else if (hasImage && !hasText) { + // 有图无文:图片居中显示 + binding.ivImage.visibility = View.VISIBLE + binding.tvTextContent.visibility = View.GONE + binding.llMainContent.orientation = LinearLayout.VERTICAL + binding.llMainContent.gravity = android.view.Gravity.CENTER + binding.ivImage.layoutParams = LinearLayout.LayoutParams(120, 120).apply { + marginEnd = 0 + } + // TODO: 加载真实图片 + binding.ivImage.setImageResource(com.daodaoshi.chick_mood.R.drawable.placeholder_background) + } else if (hasText && !hasImage) { + // 无图有文:文本占满整个内容区域 + binding.ivImage.visibility = View.GONE + binding.tvTextContent.visibility = View.VISIBLE + binding.llMainContent.orientation = LinearLayout.VERTICAL + binding.tvTextContent.text = record.textContent + } else { + // 无图无文:隐藏内容区域 + binding.ivImage.visibility = View.GONE + binding.tvTextContent.visibility = View.GONE + } + + // 设置点击事件 + binding.root.setOnClickListener { + onCardClick(record) + } + + binding.btnDetail.setOnClickListener { + onDetailClick(record) + } + + binding.btnFavorite.setOnClickListener { + onFavoriteClick(record) + // 更新收藏状态显示 + updateFavoriteIcon(!record.isFavorite) + } + + binding.btnShare.setOnClickListener { + onShareClick(record) + } + } + + private fun updateFavoriteIcon(isFavorite: Boolean) { + val favoriteIcon = if (isFavorite) { + com.daodaoshi.chick_mood.R.drawable.ic_card_favorite_filled + } else { + com.daodaoshi.chick_mood.R.drawable.ic_card_favorite_border + } + binding.btnFavorite.setImageResource(favoriteIcon) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/chick_mood/utils/MoodDescriptionGenerator.kt b/app/src/main/java/com/chick_mood/utils/MoodDescriptionGenerator.kt new file mode 100644 index 0000000..ad3fad0 --- /dev/null +++ b/app/src/main/java/com/chick_mood/utils/MoodDescriptionGenerator.kt @@ -0,0 +1,54 @@ +package com.chick_mood.utils + +import com.chick_mood.data.model.Emotion + +/** + * 心情文案生成器 + * 根据心情值和情绪类型生成"程度+心情类型"的文案 + */ +object MoodDescriptionGenerator { + + /** + * 生成心情文案 + * @param intensity 心情值 (0-100) + * @param emotion 情绪类型 + * @return 心情文案,格式:程度+情绪类型 + */ + fun generateMoodDescription(intensity: Int, emotion: Emotion): String { + val degree = getDegreeDescription(intensity) + val emotionName = getEmotionName(emotion) + return "$degree $emotionName" + } + + /** + * 根据心情值获取程度描述 + * @param intensity 心情值 (0-100) + * @return 程度描述 + */ + private fun getDegreeDescription(intensity: Int): String { + return when (intensity) { + in 0..20 -> "有些" + in 21..40 -> "非常" + in 41..60 -> "超级" + in 61..80 -> "这也太" + in 81..100 -> "完全无法控制" + else -> "有些" + } + } + + /** + * 获取情绪名称 + * @param emotion 情绪类型 + * @return 情绪中文名称 + */ + private fun getEmotionName(emotion: Emotion): String { + return when (emotion) { + Emotion.HAPPY -> "开心" + Emotion.SAD -> "悲伤" + Emotion.ANGRY -> "生气" + Emotion.WORRIED -> "烦恼" + Emotion.LONELY -> "孤单" + Emotion.SCARED -> "害怕" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt b/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt index a1b2536..05dfe4f 100644 --- a/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt +++ b/app/src/main/java/com/daodaoshi/chick_mood/MainActivitySimple.kt @@ -11,7 +11,8 @@ import com.daodaoshi.chick_mood.databinding.ActivityMainBinding import com.chick_mood.data.database.DatabaseTestHelper import com.chick_mood.data.database.SimpleDatabaseManager import com.chick_mood.ui.emotion.EmotionSelectorDialog -import com.chick_mood.ui.adapter.MoodRecordAdapter +import com.chick_mood.ui.adapter.MoodCardAdapter +import com.chick_mood.ui.adapter.ChickImageAdapter import com.chick_mood.data.model.Emotion import com.chick_mood.data.model.MoodRecord import java.text.SimpleDateFormat @@ -20,6 +21,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import com.chick_mood.utils.MoodDescriptionGenerator /** * 简化的首页Activity - 主要用于展示历史心情记录和创建新记录的入口 @@ -40,7 +42,8 @@ class MainActivitySimple : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private lateinit var timeFormatter: SimpleDateFormat - private lateinit var moodRecordAdapter: MoodRecordAdapter + private lateinit var moodCardAdapter: MoodCardAdapter + private lateinit var chickImageAdapter: ChickImageAdapter private var recordIdToLocate: Long = -1L override fun onCreate(savedInstanceState: Bundle?) { @@ -57,14 +60,14 @@ class MainActivitySimple : AppCompatActivity() { // 初始化UI组件 initViews() - // 初始化ViewPager2适配器 - initMoodRecordAdapter() + // 初始化适配器 + initAdapters() // 数据库测试已禁用,避免创建额外的测试记录 // runDatabaseTest() - // 创建测试数据(如果数据库为空) - createTestDataIfNeeded() + // 测试数据已移除,用户可以通过添加心情功能创建真实数据 + // createTestDataIfNeeded() // 加载历史记录数据 loadMoodRecords() @@ -209,12 +212,56 @@ class MainActivitySimple : AppCompatActivity() { } /** - * 更新时间指示器 + * 更新时间信息 */ - private fun updateTimeIndicator(timestamp: Long) { - // TODO: 实现时间更新功能 - 暂时注释掉 - // val formattedTime = timeFormatter.format(Date(timestamp)) - // binding.tvTimeIndicator.text = formattedTime + private fun updateTimeInfo(position: Int) { + val records = moodCardAdapter.getData() + if (position < records.size) { + val record = records[position] + + // 获取当前日期(用于比较) + val now = Calendar.getInstance() + val recordDate = Calendar.getInstance().apply { + timeInMillis = record.timestamp + } + + // 比较日期 + val weekdayText = when { + isSameDay(now, recordDate) -> "今天" + isYesterday(now, recordDate) -> "昨天" + else -> { + val weekdays = arrayOf("", "星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六") + weekdays[recordDate.get(Calendar.DAY_OF_WEEK)] + } + } + + // 获取时间 + val time = String.format("%02d:%02d", + recordDate.get(Calendar.HOUR_OF_DAY), + recordDate.get(Calendar.MINUTE)) + + binding.tvWeekday.text = weekdayText + binding.tvTime.text = time + } + } + + /** + * 判断是否为同一天 + */ + private fun isSameDay(cal1: Calendar, cal2: Calendar): Boolean { + return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) + } + + /** + * 判断是否为昨天 + */ + private fun isYesterday(now: Calendar, recordDate: Calendar): Boolean { + val yesterday = Calendar.getInstance().apply { + timeInMillis = now.timeInMillis + add(Calendar.DAY_OF_YEAR, -1) + } + return isSameDay(yesterday, recordDate) } /** @@ -222,9 +269,9 @@ class MainActivitySimple : AppCompatActivity() { */ private fun showEmptyStateUI() { binding.llEmptyState.isVisible = true - // TODO: 其他视图状态设置 - 暂时注释掉 - // binding.vpHistoryRecords.isVisible = false - // binding.tvTimeIndicator.isVisible = false + binding.llTimeInfo.isVisible = false + binding.flChickContainer.isVisible = false + binding.vpMoodCards.isVisible = false } /** @@ -232,12 +279,9 @@ class MainActivitySimple : AppCompatActivity() { */ private fun showContentStateUI() { binding.llEmptyState.isVisible = false - // TODO: 其他视图状态设置 - 暂时注释掉 - // binding.vpHistoryRecords.isVisible = true - // binding.tvTimeIndicator.isVisible = true - - // TODO: 设置ViewPager2数据 - // binding.vpHistoryRecords.adapter = null + binding.llTimeInfo.isVisible = true + binding.flChickContainer.isVisible = true + binding.vpMoodCards.isVisible = true } /** @@ -274,15 +318,20 @@ class MainActivitySimple : AppCompatActivity() { } /** - * 初始化心情记录适配器 + * 初始化适配器 */ - private fun initMoodRecordAdapter() { - moodRecordAdapter = MoodRecordAdapter( - onItemClick = { record -> + private fun initAdapters() { + // 初始化心情卡片适配器 + moodCardAdapter = MoodCardAdapter( + onCardClick = { record -> // 点击记录卡片 - 跳转到详情页 Log.d(TAG, "点击了心情记录: ${record.emotion}") showMoodRecordDetail(record) }, + onDetailClick = { record -> + // 查看详情 + showMoodRecordDetail(record) + }, onFavoriteClick = { record -> // 收藏/取消收藏 toggleFavorite(record) @@ -290,24 +339,40 @@ class MainActivitySimple : AppCompatActivity() { onShareClick = { record -> // 分享记录 shareMoodRecord(record) - }, - onDetailClick = { record -> - // 查看详情 - showMoodRecordDetail(record) } ) - binding.vpHistoryRecords.adapter = moodRecordAdapter - binding.vpHistoryRecords.orientation = ViewPager2.ORIENTATION_HORIZONTAL + binding.vpMoodCards.adapter = moodCardAdapter + binding.vpMoodCards.orientation = ViewPager2.ORIENTATION_HORIZONTAL - // 设置页面变化回调,用于更新时间指示器 - binding.vpHistoryRecords.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + // 初始化小鸡形象适配器 + chickImageAdapter = ChickImageAdapter() + binding.vpChickImages.adapter = chickImageAdapter + binding.vpChickImages.orientation = ViewPager2.ORIENTATION_HORIZONTAL + + // 设置页面变化回调,保持两个ViewPager2同步 + binding.vpMoodCards.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { super.onPageSelected(position) - if (position < moodRecordAdapter.itemCount) { - // 当有记录时,更新时间指示器显示当前选中记录的时间 - // updateTimeIndicator(moodRecordAdapter.moodRecords[position].timestamp) + // 同步小鸡形象位置 + if (position < chickImageAdapter.itemCount) { + binding.vpChickImages.setCurrentItem(position, false) } + // 更新时间信息 + updateTimeInfo(position) + } + }) + + // 为小鸡形象ViewPager2也添加页面变化回调,实现双向同步 + binding.vpChickImages.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + super.onPageSelected(position) + // 同步心情卡片位置 + if (position < moodCardAdapter.itemCount) { + binding.vpMoodCards.setCurrentItem(position, false) + } + // 更新时间信息 + updateTimeInfo(position) } }) } @@ -319,14 +384,23 @@ class MainActivitySimple : AppCompatActivity() { CoroutineScope(Dispatchers.IO).launch { try { val dbManager = SimpleDatabaseManager.getInstance(this@MainActivitySimple) - val records = dbManager.getRecentMoodRecords(50) // 获取最近50条记录 + val records = dbManager.getRecentMoodRecords(50) // 获取最近50条记录 withContext(Dispatchers.Main) { if (records.isEmpty()) { showEmptyStateUI() } else { showContentStateUI() - moodRecordAdapter.updateData(records) + + // 更新心情卡片适配器 + moodCardAdapter.updateData(records) + + // 更新小鸡形象适配器 + val emotions = records.map { it.emotion } + chickImageAdapter.updateData(emotions) + + // 更新时间信息(显示第一条记录的时间) + updateTimeInfo(0) // 检查是否需要定位记录 checkAndLocateEditedRecord() @@ -350,16 +424,14 @@ class MainActivitySimple : AppCompatActivity() { private fun toggleFavorite(record: MoodRecord) { CoroutineScope(Dispatchers.IO).launch { try { - // 创建更新后的记录(注意:这里需要修改数据模型支持更新) + val dbManager = SimpleDatabaseManager.getInstance(this@MainActivitySimple) val updatedRecord = record.copy(isFavorite = !record.isFavorite) - - // TODO: 实现数据库更新功能 - // SimpleDatabaseManager.updateMoodRecord(updatedRecord) + dbManager.updateMoodRecord(updatedRecord) withContext(Dispatchers.Main) { - // 重新加载数据 + // 重新加载数据以更新UI状态 loadMoodRecords() - val message = if (updatedRecord.isFavorite) "已收藏" else "已取消收藏" + val message = if (!record.isFavorite) "已收藏" else "已取消收藏" showTemporaryMessage(message) } } catch (e: Exception) { @@ -610,26 +682,26 @@ class MainActivitySimple : AppCompatActivity() { Log.d(TAG, "开始定位记录ID: $recordId") // 确保ViewPager2已经准备好 - binding.vpHistoryRecords.post { + binding.vpMoodCards.post { // 从适配器获取最新数据 - val records = moodRecordAdapter.getData() + val records = moodCardAdapter.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) { + if (position != -1 && position < moodCardAdapter.itemCount) { // 使用setCurrentItem定位,第二个参数为true表示平滑滚动 - binding.vpHistoryRecords.setCurrentItem(position, true) + binding.vpMoodCards.setCurrentItem(position, true) Log.d(TAG, "已定位到位置: $position") // 延迟一点再次确认定位,确保定位成功 - binding.vpHistoryRecords.postDelayed({ - val currentPosition = binding.vpHistoryRecords.currentItem + binding.vpMoodCards.postDelayed({ + val currentPosition = binding.vpMoodCards.currentItem Log.d(TAG, "定位后当前位置: $currentPosition") if (currentPosition != position) { Log.w(TAG, "定位可能不准确,尝试重新定位") - binding.vpHistoryRecords.setCurrentItem(position, true) + binding.vpMoodCards.setCurrentItem(position, true) } }, 300) } else { diff --git a/app/src/main/res/drawable/bg_add_mood_button.xml b/app/src/main/res/drawable/bg_add_mood_button.xml new file mode 100644 index 0000000..3ca8979 --- /dev/null +++ b/app/src/main/res/drawable/bg_add_mood_button.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_mood.png b/app/src/main/res/drawable/ic_add_mood.png new file mode 100644 index 0000000..22333ad Binary files /dev/null and b/app/src/main/res/drawable/ic_add_mood.png differ diff --git a/app/src/main/res/drawable/ic_card_favorite_border.png b/app/src/main/res/drawable/ic_card_favorite_border.png new file mode 100644 index 0000000..38a0c5a Binary files /dev/null and b/app/src/main/res/drawable/ic_card_favorite_border.png differ diff --git a/app/src/main/res/drawable/ic_card_favorite_filled.png b/app/src/main/res/drawable/ic_card_favorite_filled.png new file mode 100644 index 0000000..d05c496 Binary files /dev/null and b/app/src/main/res/drawable/ic_card_favorite_filled.png differ diff --git a/app/src/main/res/drawable/ic_card_share.png b/app/src/main/res/drawable/ic_card_share.png new file mode 100644 index 0000000..3547134 Binary files /dev/null and b/app/src/main/res/drawable/ic_card_share.png differ diff --git a/app/src/main/res/drawable/ic_home_more.png b/app/src/main/res/drawable/ic_home_more.png new file mode 100644 index 0000000..0bba05b Binary files /dev/null and b/app/src/main/res/drawable/ic_home_more.png differ diff --git a/app/src/main/res/drawable/ic_home_statistics.png b/app/src/main/res/drawable/ic_home_statistics.png new file mode 100644 index 0000000..831c570 Binary files /dev/null and b/app/src/main/res/drawable/ic_home_statistics.png differ diff --git a/app/src/main/res/drawable/ic_launcher_chick_background.xml b/app/src/main/res/drawable/ic_launcher_chick_background.xml new file mode 100644 index 0000000..068f2de --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_chick_background.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_chick_foreground.png b/app/src/main/res/drawable/ic_launcher_chick_foreground.png new file mode 100644 index 0000000..3698e23 Binary files /dev/null and b/app/src/main/res/drawable/ic_launcher_chick_foreground.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 728c9d5..1a2de02 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -24,29 +24,19 @@ android:id="@+id/btn_more" android:layout_width="24dp" android:layout_height="24dp" - android:src="@drawable/ic_more" + android:src="@drawable/ic_home_more" android:background="?attr/selectableItemBackgroundBorderless" android:layout_gravity="start" android:layout_marginStart="16dp" android:contentDescription="更多功能" /> - - - + - - + - - - - + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + +