Android compose wanandroid app之搜索页面实现

2024-03-29 15:08

本文主要是介绍Android compose wanandroid app之搜索页面实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

实现搜索页面

  • 前言
    • ROOM数据库
      • Entity
      • Dao
      • DataBase
      • viewmodel定义操作方法
      • page实现数据操作
    • 官方Flow Layout
      • Flow Layout属性
      • FlowRow添加数据
    • 状态布局
      • 定义状态枚举
      • 展示布局
      • 记录数据状态
      • viewmodel获取和page展示数据
    • 源码地址

前言

今天来实现一下搜索页面,使用ROOM数据库保存搜索的历史记录,根据不同加载状态展示不同布局,并使用官方的Flow layout来展示数据等操作。

先来看一下效果图,很丑~将就看一下
在这里插入图片描述

ROOM数据库

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 {@Insertfun insert(searchModule: SearchModule):Long@Deletefun delete(searchList:List<SearchModule>):Int@Query("SELECT * FROM SEARCH_TABLE WHERE is_delete ==:isDelete")fun queryDataList(isDelete:Boolean):List<SearchModule>
}

DataBase

数据库持有者,并作为与应用持久关联数据的底层连接的主要访问点。在运行时,通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? = nullval Context.db: SearchDataBaseget() {if (dbInstance == null){synchronized(SearchDataBase::class) {if (dbInstance == null) {val ctx = MyApplication.contextdbInstance = 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.dbval insert = db.dao.insert(searchModule)return insert
}

在viewmodel中创建查询方法:

fun queryDataList(context: Context,isDelete:Boolean){var dataList:List<SearchModule>? = nullval db = context.dbdataList = db.dao.queryDataList(isDelete)_dataList.postValue(dataList)
}

这里给数据源设置值的时候要注意,因为操作数据库要在子线程中操作,所以不能直接使用_dataList.value的方法设置值,而是要改为使用postValue的方法设置值。

在viewmodel中创建删除方法:

fun clearDataBase(context: Context,dataList:List<SearchModule>):Int{val db = context.dbval int = db.dao.delete(dataList)if (int != 0){_dataList.postValue(null)}return int
}

page实现数据操作

在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 = loadingStatewhen(state){LoadingState.Loading->  loading()LoadingState.Error ->   error()LoadingState.Empty ->   empty()LoadingState.Success -> success()}
}

记录数据状态

在viewmodel中记录请求状态

val searchLoadingState:MutableLiveData<LoadingState> = MutableLiveData(LoadingState.Loading)

在page页面同步请求状态:

//搜索内容加载状态loading
val searchLoadingState by searchViewModel.searchLoadingState.observeAsState()

viewmodel获取和page展示数据

在viewmodel中创建数据集合

//搜索结果
private val _searchList = MutableLiveData<SearchModule2>()
val searchList = _searchList

在viewmodel中创建请求数据的方法:

fun getSearchContent(page:Int,keyWord:String,pageSize:Int){NetWork.service.searchContent(page,keyWord,pageSize).enqueue(object : Callback<BaseResult<SearchModule2>>{override fun onResponse(call: Call<BaseResult<SearchModule2>>,response: Response<BaseResult<SearchModule2>>) {response.body()?.let {_searchList.value = it.data}}override fun onFailure(call: Call<BaseResult<SearchModule2>>, 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地址如下:戳我~
项目很丑,多担待,后面再美化了~

这篇关于Android compose wanandroid app之搜索页面实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/858908

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu1240、hdu1253(三维搜索题)

1、从后往前输入,(x,y,z); 2、从下往上输入,(y , z, x); 3、从左往右输入,(z,x,y); hdu1240代码如下: #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#inc

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影