项目准备:
1、LiveData与MVVM设计模式
2、Navigation导航的使用
3、RecyclerView
4、Dialog
5、滑动删除
6、按钮动画设计
7、@TypeConverter的使用
8、单例设计模式
9、数据可序列化
10、Room
项目简介:点击App首先进入广告页面,三秒后进入主页,也可以点击跳过直接进入首页。未添加数据时页面会显示无数据状态(下方未展示),点击主页下Add New按钮可进入详情页面添加标签。点击详情页右上方菜单按钮弹出save完成保存,再次从主页点击进入修改时菜单按钮的内容会变为delete和update两种按钮。
详情页面中可选择事件的重要性(也就是后方代码中所提到的优先级),以颜色(红,黄、绿)为标记。选择事务完成时间,添加或选择标签,事件的详细描述。
项目功能简单,却涵盖了丰富的知识点。
项目页面浏览:
主要有三个界面:广告页+标签显示页+添加详细内容页
整个项目框架
本项目只涉及到本地数据库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)
}
}
未完待续……
完整代码