Android 手机便签(一)

项目准备:

1、LiveData与MVVM设计模式
2、Navigation导航的使用
3、RecyclerView
4、Dialog
5、滑动删除
6、按钮动画设计
7、@TypeConverter的使用
8、单例设计模式
9、数据可序列化
10、Room

项目简介:点击App首先进入广告页面,三秒后进入主页,也可以点击跳过直接进入首页。未添加数据时页面会显示无数据状态(下方未展示),点击主页下Add New按钮可进入详情页面添加标签。点击详情页右上方菜单按钮弹出save完成保存,再次从主页点击进入修改时菜单按钮的内容会变为delete和update两种按钮。
详情页面中可选择事件的重要性(也就是后方代码中所提到的优先级),以颜色(红,黄、绿)为标记。选择事务完成时间,添加或选择标签,事件的详细描述。
项目功能简单,却涵盖了丰富的知识点。
项目页面浏览:
主要有三个界面:广告页+标签显示页+添加详细内容页


image.png

整个项目框架


图片来源:https://developer.android.google.cn/training/dependency-injection/manual?hl=zh_cn

本项目只涉及到本地数据库Room的使用,并没有用到Retrofit从网络获取数据。

项目所需添加的gradle

在Project中
  id 'kotlin-kapt'
    id("androidx.navigation.safeargs.kotlin")
    id 'kotlin-parcelize'
 //room
    def room_version = "2.3.0"

    implementation("androidx.room:room-runtime:$room_version")
    annotationProcessor "androidx.room:room-compiler:$room_version"
    implementation("androidx.room:room-ktx:$room_version")
    kapt("androidx.room:room-compiler:$room_version")

    def  lifecycle_version = "2.4.0-alpha02"

    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
    implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")

    //by viewModels
    implementation "androidx.activity:activity-ktx:1.2.0"
    implementation "androidx.fragment:fragment-ktx:1.3.0"

    //navigation
    implementation("androidx.navigation:navigation-fragment-ktx:2.3.5")
    implementation("androidx.navigation:navigation-ui-ktx:2.3.5")
    implementation 'com.nex3z:flow-layout:1.3.3'

android {
    buildFeatures{
        viewBinding true
        dataBinding true
    }
}

在Module中
 dependencies {  
        //classpath
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.3.5")
    }

数据库表的搭建

数据库表

Todo类中

/**
 *@Description
 *@Author PC
 *@QQ 1578684787
 */
@Parcelize   //实现序列化,使支持传递Custom Parcelize的数据
@Entity(tableName = "todo_table")
data class Todo(
    @PrimaryKey(autoGenerate = true)
    var id:Int,
    var title:String,
    var description: String, //内容
    var priority: Priority, //事务的重要性
    @Embedded
    var date: Date,//管理日期
    @Embedded
    var tag: Tag //管理标签
):Parcelable

/**
 * 标识事务的重要性
 */
enum class Priority{
    HIGH,MIDDLE,LOW
}

/**
 * 管理日期
 */
@Parcelize
data class Date(
    var year:Int,
    var month:Int,
    var day:Int
):Parcelable

/**
 * 管理标签
 */
@Parcelize
data class Tag(
    var text:String,
    var bgColor:String
):Parcelable

@Embedded的作用:将该注解的表格内容镶嵌到@Embedded所在的表中,但是这个表不能被单独创建字段。
@Parcelize为实现序列化,传递相应内容,后面将作详细介绍

Tag类中

@Entity(tableName = "tag_table")
data class TagData (
    @PrimaryKey(autoGenerate = true)
    val id:Int,
    val title:String,
    val bgColor:String
    )

搭建接口

/**
 *@Description
 *@Author PC
 *@QQ 1578684787
 */
@Dao
interface TodoDao {
    //插入todo
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertTodoData(todo: Todo)
    //获取todo数据
    @Query("select * from todo_table")
    fun getTodoDatas():LiveData>
    //删除todo
    @Delete
    suspend fun deleteTodoData(todo: Todo)
    //删除所有todo
    @Query("delete from todo_table")
    suspend fun deleteAllTodoDatas()
    //更新数据
    @Update(onConflict = OnConflictStrategy.REPLACE)
    suspend fun updateTodoData(todo: Todo)

    /**
     * 标签操作
     */
    //插入标签
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertTag(tagData: TagData)

    //获取所有标签
    @Query("select * from tag_table")
    fun getAllTags():LiveData
    //删除标签
    @Delete
    fun deleteTag(tagData: TagData)

}
/**
 *@Description
 *@Author PC
 *@QQ 1578684787
 */
@Dao
interface TagDao {
    //插入Tag
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertTag(tag: TagData)

    //删除tag
    @Delete
    suspend fun deleteTag(tag: TagData)
    //读取所有todo
    @Query("select * from tag_table")
    fun getAllTag():LiveData>
}

创建TodoConverter解析器

因为我们所创建的Priority(事务的优先级)系统无法识别,所以创建解析器将Priority转换为字符串,在使用时再转换回我们自定义的Priority。
主要是@TypeConverter的使用
priority是我自己定义的枚举类型的数据,枚举中有个特殊的属性name将枚举转化为String类型
valueOf为枚举中特有的方法,将枚举值对应的name传递给它,就会转化为对应的枚举类型。

class TodoConverter {

    //将priority对象存入数据库时调用
    @TypeConverter
    fun priorityToString(priority: Priority):String{
        return priority.name
    }

    //从数据库中取出该数据时转化为Priority类型
    @TypeConverter
    fun stringToPriority(str:String):Priority{
        return Priority.valueOf(str)
    }
}

创建数据库类

使用单例的方式创建数据库类,保证数据库的唯一性,数据的一致性

@TypeConverters(TodoConverter::class)
@Database(
    entities = [Todo::class, TagData::class],
    version = 1,
    exportSchema = false
)
abstract class TodoDatabase:RoomDatabase() {
    abstract fun getTodoDao(): TodoDao
    abstract fun getTagDao(): TagDao

    //创建单例对象
    companion object {
        @Volatile
        private var INSTANCE: TodoDatabase? = null
        fun getDatabase(context: Context):TodoDatabase{
            if (INSTANCE != null){
                return INSTANCE!!
            }
            //创建对象
            synchronized(this){
                if (INSTANCE==null){
                    INSTANCE = Room.databaseBuilder(context,TodoDatabase::class.java,"todo.db"
                    ).build()
                }
            }
            return INSTANCE!!
        }
    }
}

创建TodoRepository

Repository作为伪数据仓库,外部在访问数据库的数据时通过该对象访问数据

class TodoRepository(context: Context) {
    private val todoDao = TodoDatabase.getDatabase(context).getTodoDao()
    private val tagDao = TodoDatabase.getDatabase(context).getTagDao()

    /**
     * TagDao
     */
    //插入Tag
    suspend fun insertTag(tag: TagData){
        tagDao.insertTag(tag)
    }

    //删除tag
    suspend fun deleteTagData(tag: TagData){
        tagDao.deleteTag(tag)
    }
    //删除所有todo
    fun getAllTags():LiveData>{
        return tagDao.getAllTag()
    }

    /**
     * todoDao
     */
    //插入todo
    suspend fun insertTodoData(todo: Todo){
        todoDao.insertTodoData(todo)
    }
    //获取todo数据
    fun getTodoDatas():LiveData>{
        return todoDao.getTodoDatas()
    }
    //删除todo
    suspend fun deleteTodoData(todo: Todo){
        todoDao.deleteTodoData(todo)
    }
    //删除所有todo
    suspend fun deleteAllTodoDatas(){
        todoDao.deleteAllTodoDatas()
    }
    //更新数据
    suspend fun updateTodoData(todo: Todo){
        todoDao.updateTodoData(todo)
    }

}

未完待续……
完整代码

你可能感兴趣的:(Android 手机便签(一))