You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin日历应用开发:笔记照片存储与动态视图创建问题

解决方案拆解

一、纯代码创建笔记展示View

直接继承LinearLayout(或其他ViewGroup),在代码中动态构建布局和控件,无需依赖XML:

class NoteDisplayView(context: Context) : LinearLayout(context) {
    private val noteContent: TextView
    private val noteImage: ImageView

    init {
        // 基础布局配置
        orientation = VERTICAL
        setPadding(16.dpToPx(), 16.dpToPx(), 16.dpToPx(), 16.dpToPx())
        background = ContextCompat.getDrawable(context, R.drawable.note_bg)

        // 创建笔记文本控件
        noteContent = TextView(context).apply {
            layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT).apply {
                bottomMargin = 8.dpToPx()
            }
            textSize = 16f
            setTextColor(Color.BLACK)
        }

        // 创建图片展示控件(默认隐藏)
        noteImage = ImageView(context).apply {
            layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, 200.dpToPx())
            scaleType = ImageView.ScaleType.CENTER_CROP
            visibility = View.GONE
        }

        // 将控件添加到当前View
        addView(noteContent)
        addView(noteImage)
    }

    // 对外暴露设置笔记内容的方法
    fun setNoteContent(content: String) {
        noteContent.text = content
    }

    // 对外暴露设置图片的方法
    fun setNoteImage(bitmap: Bitmap) {
        noteImage.setImageBitmap(bitmap)
        noteImage.visibility = View.VISIBLE
    }

    // 扩展函数:dp转px
    private fun Int.dpToPx(): Int {
        return (this * context.resources.displayMetrics.density).toInt()
    }
}

使用示例:在Activity/Fragment中点击日历日期后,实例化该View并添加到布局(或弹窗):

// 点击日历日期后调用
val noteView = NoteDisplayView(this)
noteView.setNoteContent("今天的笔记内容")
// 如果有图片,设置图片
val bitmap = BitmapFactory.decodeFile("/storage/emulated/0/.../photo.jpg")
noteView.setNoteImage(bitmap)
// 将noteView添加到指定布局
rootLayout.addView(noteView)

二、笔记、照片、事件的存储方案

1. 结构化存储(推荐Room数据库)

适合多日期多内容的场景,结构化存储所有数据:

步骤1:添加Room依赖(Module级build.gradle)

dependencies {
    implementation "androidx.room:room-runtime:2.6.1"
    kapt "androidx.room:room-compiler:2.6.1"
    implementation "androidx.room:room-ktx:2.6.1"
}

步骤2:创建实体类(对应数据库表)

@Entity(tableName = "calendar_notes")
data class CalendarNote(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val date: Long, // 用时间戳存储选中的日历日期
    val noteContent: String, // 笔记文本
    val imagePath: String?, // 图片本地存储路径(不存Bitmap)
    val eventTitle: String?, // 事件标题
    val eventTime: Long? // 事件时间戳
)

步骤3:创建Dao接口(数据库操作方法)

@Dao
interface CalendarNoteDao {
    @Insert
    suspend fun insertNote(note: CalendarNote)

    @Update
    suspend fun updateNote(note: CalendarNote)

    @Query("SELECT * FROM calendar_notes WHERE date = :targetDate")
    suspend fun getNoteByDate(targetDate: Long): CalendarNote?

    @Delete
    suspend fun deleteNote(note: CalendarNote)
}

步骤4:创建数据库单例类

@Database(entities = [CalendarNote::class], version = 1)
abstract class CalendarDatabase : RoomDatabase() {
    abstract fun calendarNoteDao(): CalendarNoteDao

    companion object {
        @Volatile
        private var INSTANCE: CalendarDatabase? = null

        fun getDatabase(context: Context): CalendarDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    CalendarDatabase::class.java,
                    "calendar_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}

步骤5:数据库操作示例(协程中执行)

// 查询指定日期的笔记
lifecycleScope.launch {
    val selectedDate = calendarView.date // 获取CalendarView选中的日期时间戳
    val existingNote = CalendarDatabase.getDatabase(this@MainActivity)
        .calendarNoteDao()
        .getNoteByDate(selectedDate)
    
    if (existingNote != null) {
        // 展示已有笔记
        val noteView = NoteDisplayView(this@MainActivity)
        noteView.setNoteContent(existingNote.noteContent)
        existingNote.imagePath?.let { path ->
            val bitmap = BitmapFactory.decodeFile(path)
            noteView.setNoteImage(bitmap)
        }
        rootLayout.addView(noteView)
    } else {
        // 插入新笔记
        val newNote = CalendarNote(
            date = selectedDate,
            noteContent = "用户输入的笔记内容",
            imagePath = "/data/data/com.yourapp/files/note_image.jpg", // 图片存储路径
            eventTitle = "同学聚会",
            eventTime = System.currentTimeMillis()
        )
        CalendarDatabase.getDatabase(this@MainActivity)
            .calendarNoteDao()
            .insertNote(newNote)
    }
}

2. 照片存储处理

从相册选图后,不要直接存储Bitmap,而是将图片保存到应用私有目录,再把路径存入数据库:

// 处理相册返回的Uri,保存图片并返回路径
fun saveImageFromUri(uri: Uri, context: Context): String? {
    return try {
        val inputStream = context.contentResolver.openInputStream(uri)
        val bitmap = BitmapFactory.decodeStream(inputStream)
        // 保存到应用私有目录
        val file = File(context.filesDir, "note_img_${System.currentTimeMillis()}.jpg")
        val outputStream = FileOutputStream(file)
        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
        inputStream?.close()
        outputStream.close()
        file.absolutePath
    } catch (e: IOException) {
        e.printStackTrace()
        null
    }
}

三、CalendarView交互逻辑

设置日期点击监听器,触发笔记的创建/展示流程:

calendarView.setOnDateChangeListener { _, year, month, dayOfMonth ->
    // 将年月日转为时间戳(与CalendarView的date格式一致)
    val calendar = Calendar.getInstance().apply {
        set(year, month, dayOfMonth)
        set(Calendar.HOUR_OF_DAY, 0)
        set(Calendar.MINUTE, 0)
        set(Calendar.SECOND, 0)
        set(Calendar.MILLISECOND, 0)
    }
    val selectedDate = calendar.timeInMillis
    // 这里可以弹出输入Dialog让用户填写笔记,再执行数据库操作
    showNoteInputDialog(selectedDate)
}

内容的提问来源于stack exchange,提问作者Nandan Praveen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:27:39