- 前言
- ROOM数据库
- Entity
- Dao
- Database
- viewmodel定义 *** 作方法
- page实现数据 *** 作
- 官方Flow Layout
- Flow Layout属性
- FlowRow添加数据
- 状态布局
- 定义状态枚举
- 展示布局
- 记录数据状态
- viewmodel获取和page展示数据
- 源码地址
今天来实现一下搜索页面,使用ROOM数据库保存搜索的历史记录,根据不同加载状态展示不同布局,并使用官方的Flow layout来展示数据等 *** 作。
先来看一下效果图,很丑~将就看一下
Room是一个数据持久化库,它是 Architecture Component的一部分。它让SQLiteDatabase的使用变得简单,大大减少了重复的代码,并且把SQL查询的检查放在了编译时。
Room数据库主要由Dao、Entity、Database三部分组成。
使用Room之前要导入依赖:
//jetpack room implementation 'androidx.room:room-runtime:2.2.6' kapt 'androidx.room:room-compiler:2.2.6'
并在build.gradle的plugins中添加以下代码:
plugins { ... id 'kotlin-kapt' }Entity
在声明数据库实体类的时候需要使用@Entity来标注改实体类,主要属性如下:
- tableName 设置表名
- indices 设置索引
- primaryKeys 设置主键
- foreignKeys 设置外键
- inheritSuperIndices 父类索引是否被当前类继承
创建一个tableName为search_table的Entity,同时设置主键,注意设置主键的时候的初始值要为0:
@Entity(tableName = "search_table") data class SearchModule( @PrimaryKey(autoGenerate = true)//主键自增 val id:Int = 0,//主键初始值为0 @ColumnInfo(name = "time_stamp")//列名 @SerializedName("time_stamp") val time_stamp:String, @ColumnInfo(name = "search_content") @SerializedName("search_content") val search_content:String, @ColumnInfo(name = "is_delete") @SerializedName("is_delete") val is_delete:Boolean = false, @ColumnInfo(name = "create_time") @SerializedName("create_time") val create_time:String )Dao
数据访问对象,全称Data Access Objects,是Room的主要组件,负责定义访问数据库的方法,Room在编译时创建每个DAO实例。DAO抽象地以一种干净的方式去访问数据库,它可以是一个接口也可以是一个抽象类。如果它是一个抽象类,它可以有一个构造函数,它将RoomDatabase作为其唯一参数。
定义一个Dao,并实现增加,查询,删除三个方法;同时该类也要被@Dao进行注解:
@Dao interface Dao { @Insert fun insert(searchModule: SearchModule):Long @Delete fun delete(searchList:ListDatabase):Int @Query("SELECT * FROM SEARCH_TABLE WHERe is_delete ==:isDelete") fun queryDataList(isDelete:Boolean):List }
数据库持有者,并作为与应用持久关联数据的底层连接的主要访问点。在运行时,通databaseBuilder() 或者 inMemoryDatabaseBuilder()获取Database实例。
使用Database要注意以下几个地方:
- 该类必须是 abstract的
- 该类必须继承RoomDatabase
- 该类必须包含至少一个@Entity标注的类
- 该类必须包含一个被@Dao标注的类
//至少包含一个@Entity标注的类 @Database(entities = [SearchModule::class],exportSchema = false,version = 1) abstract class SearchDatabase : RoomDatabase() { //至少包含一个@Dao标注的类 abstract val dao:Dao } @Volatile private var dbInstance: SearchDatabase? = null val Context.db: SearchDatabase get() { if (dbInstance == null){ synchronized(SearchDatabase::class) { if (dbInstance == null) { val ctx = MyApplication.context dbInstance = Room .databaseBuilder(ctx, SearchDatabase::class.java, "search") .build() } } } return dbInstance!! }viewmodel定义 *** 作方法
创建好room相对应的三个类之后,就可以执行数据增删改查 *** 作了;创建一个SearchViewModel来实现数据 *** 作,在viewmodel中 *** 作数据。
在viewmodel中创建插入方法:
fun insetData(context: Context,searchModule: SearchModule):Long{ val db = context.db val insert = db.dao.insert(searchModule) return insert }
在viewmodel中创建查询方法:
fun queryDataList(context: Context,isDelete:Boolean){ var dataList:List? = null val db = context.db dataList = db.dao.queryDataList(isDelete) _dataList.postValue(dataList) }
这里给数据源设置值的时候要注意,因为 *** 作数据库要在子线程中 *** 作,所以不能直接使用_dataList.value的方法设置值,而是要改为使用postValue的方法设置值。
在viewmodel中创建删除方法:
fun clearDatabase(context: Context,dataList:Listpage实现数据 *** 作):Int{ val db = context.db val int = db.dao.delete(dataList) if (int != 0){ _dataList.postValue(null) } return int }
在page页面调用viewmodel的方法,进行输入的新增查询等 *** 作。
在page页面点击搜索的时候往数据库插入数据,在插入数据之前需要先定义插入数据的相关类:
val searchModule = SearchModule( time_stamp = "${System.currentTimeMillis()}", search_content = searchTv.value, is_delete = false, create_time = formatTime())
新开一个子线程进行输入插入:
thread { val insertLong = searchViewModel.insetData(MyApplication.context,searchModule) }
insertLong 返回的是插入成功的条数。
在page页面查询数据:
val searchViewModel:SearchViewModel = viewModel() val dataList by searchViewModel.dataList.observeAsState() thread { searchViewModel.queryDataList(MyApplication.context,false) }官方Flow Layout
前面在绘制分类页面的时候,自定义了一个FlowLayout,当时只是为了尝试一下compose的自定义view;在搜索这里同样要实现内容的Flow 排列,这里使用官方的Flow Layout。
要使用官方的Flow Layout需要导入以下依赖:
implementation "com.google.accompanist:accompanist-flowlayout:$accompanist_pager"Flow Layout属性
使用FlowLayout实现历史记录的展示,如下图:
在使用组件之前还是要先知道组件的属性,才能更好的使用该组件。
@Composable public fun FlowRow( modifier: Modifier = Modifier, mainAxisSize: SizeMode = SizeMode.Wrap, mainAxisAlignment: FlowMainAxisAlignment = FlowMainAxisAlignment.Start, mainAxisSpacing: Dp = 0.dp, crossAxisAlignment: FlowCrossAxisAlignment = FlowCrossAxisAlignment.Start, crossAxisSpacing: Dp = 0.dp, lastLineMainAxisAlignment: FlowMainAxisAlignment = mainAxisAlignment, content: @Composable () -> Unit )
- modifier 修饰
- mainAxisSize 布局在主轴方向上的大小
- mainAxisAlignment 每行的子对象在主轴方向上的对齐。
- mainAxisSpacing 每行子对象之间的主轴间距。
- crossAxisAlignment 每行的子项在横轴方向上的对齐。
- crossAxisSpacing 布局行之间的横轴间距。
- lastLineMainAxisAlignment 替代最后一行的主轴对齐方式。
- content flowlayout的内容,可以理解为放置的子元素
同时还要注意Flow Layout分为Flow Row 和 Flow Column两种,我们这里使用的是Flow Row。
使用示例如下:
FlowRow(modifier = Modifier .fillMaxWidth() .padding(12.dp, 20.dp, 0.dp, 0.dp)) { }FlowRow添加数据
直接在FlowRow的content里面循环添加子元素
dataList?.forEach { Card( modifier = Modifier.padding(0.dp,12.dp,12.dp,0.dp), border = BorderStroke(color = Color.Black, width = Dp.Hairline), shape = RoundedCornerShape(8.dp) ) { Row( modifier = Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 4.dp), verticalAlignment = Alignment.CenterVertically ) { Spacer(Modifier.width(4.dp)) Text(text = it.search_content) } } }状态布局
简单实现一个数据加载状态布局,分别对应数据加载的加载中,加载失败,加载成功,数据为空等状态时显示不同的布局。
定义状态枚举创建四个枚举,分别对应不同状态:
enum class LoadingState{ Loading, Error, Empty, Success }展示布局
根据不同状态显示不同布局,首先要定义四个布局回调:
@Composable fun LoadingStateLayout(modifier: Modifier, loadingState: LoadingState, loading:@Composable () ->Unit,//加载中 error:@Composable () ->Unit,//加载出错 empty:@Composable () ->Unit,//数据为空 success:@Composable () ->Unit){//加载成功 }
根据不同状态显示不同布局:
Box(modifier = modifier){ val state = loadingState when(state){ LoadingState.Loading-> loading() LoadingState.Error -> error() LoadingState.Empty -> empty() LoadingState.Success -> success() } }记录数据状态
在viewmodel中记录请求状态
val searchLoadingState:MutableLiveData= MutableLiveData(LoadingState.Loading)
在page页面同步请求状态:
//搜索内容加载状态loading val searchLoadingState by searchViewModel.searchLoadingState.observeAsState()viewmodel获取和page展示数据
在viewmodel中创建数据集合
//搜索结果 private val _searchList = MutableLiveData() val searchList = _searchList
在viewmodel中创建请求数据的方法:
fun getSearchContent(page:Int,keyWord:String,pageSize:Int){ NetWork.service.searchContent(page,keyWord,pageSize).enqueue(object : Callback>{ override fun onResponse( call: Call >, response: Response >) { response.body()?.let { _searchList.value = it.data } } override fun onFailure(call: Call >, t: Throwable) { searchLoadingState.value = LoadingState.Error } }) }
根据返回的数据来判断是否为空,如果为空则将保存的状态设置为empty,否则为success:
if (it.data?.datas != null){ searchLoadingState.value = LoadingState.Success }else{ searchLoadingState.value = LoadingState.Empty }
在page中获取数据:
val searchList by searchViewModel.searchList.observeAsState() searchViewModel.getSearchContent(0,searchTv.value,3)
在page中使用状态布局展示数据:
Spacer(modifier = Modifier.height(20.dp)) Text(text = "搜索结果",fontSize = 14.sp,color = Color.Black,modifier = Modifier.padding(horizontal = 16.dp)) LoadingStateLayout( modifier = Modifier.fillMaxWidth(), loadingState = searchLoadingState!!, loading = { Column(modifier = Modifier .padding(vertical = 20.dp) .fillMaxWidth() .height(100.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { CircularProgressIndicator() Text(text = "请先搜索",modifier = Modifier.padding(vertical = 10.dp)) } }, error = { Column(modifier = Modifier .fillMaxWidth() .height(100.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { Text(text = "暂无数据") } }, empty = { Column(modifier = Modifier .fillMaxWidth() .height(100.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { Text(text = "暂无数据") } }) { LazyColumn(modifier = Modifier.fillMaxWidth()){ itemsIndexed(searchList!!.datas){ index: Int, item: SearchList -> Column(modifier = Modifier .fillMaxWidth() .height(45.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { Text(text = item.title,fontSize = 14.sp) } Divider( modifier = Modifier .padding(0.dp, 0.dp, 12.dp, 0.dp,), color = Color(229,224,227), thickness = 1.dp, startIndent = 16.dp) } } }源码地址
Gitee地址如下:戳我~
项目很丑,多担待,后面再美化了~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)