Android 如何实现搜索功能:本地搜索?数据模型如何设计?数据如何展示和保存?

目录

  1. 效果图
  2. 为什么需要搜索功能
  3. 如何设计搜索本地的功能,如何维护呢?
  4. 总结

一、效果图

在这里插入图片描述在这里插入图片描述

二、为什么需要搜索功能

找一个选项,需要花非常多的时间,并且每次都需要指导客户在哪里,现在只要让他们搜索一下就可以。这也是模仿手机里面的设置功能来进行开发的。这些选项我是存储在本地的。参数太多,暂时还没考虑做到后台,当然,即使后面做到后台,也只是替换数据而已。

在这里插入图片描述

三、如何设计搜索本地的功能,如何维护呢?

我们可以看到效果图:

  1. 有开关类的、有输入类的。
  2. 有分类:系统设置、串口设置、功能开启等等。
  3. 有默认值,如:123456,默认是关的等等。

so,我们需要设计数据模型。

3.1 设计数据模型

大家可以思考一下为什么数据模型要这样设计?有默认值,有key,有nameId

/*** nameId:显示名称,这里存储的是rid,方便后续国际化* nameS:名称* category:分类* drfault:默认值* type:类型 1 多选项值 2 开关 3 输入类* mmkvName:保存key*/
enum class OtherEnum(var nameId:Int,var nameS:String,var category:String,var drfault:Any,var type:Int,var mmkvName: String) {MDB(R.string.base_two_code,"通讯协议","串口设置","MDB", 2,MMKVName.MDB),CHANGE(R.string.base_nayax,"找零功能","找零设置",false, 1,MMKVName.CHANGE),CONTACT_NUMBER(R.string.leak_canary_test_class_name,"联系方式","系统设置","123456", 2,MMKVName.CONTACT_NUMBER),
}

3.2 Repo类

这里只是增加了一个要展示的数据,后面如果把数据放在了后端,也就只是替换这一部分就可以。

class OtherFragmentRepo @Inject constructor() : BaseRepository() {var arrayList: MutableList<OtherEnum> = Arrays.asList(//用于列表展示OtherEnum.MDB,OtherEnum.CHANGE,OtherEnum.CONTACT_NUMBER,)}

如果有新增的数据,只需要在这里增加就可以了。这样也非常好维护,所以需要数据模型设置好来。

3.3 VM:主要是提供搜索的功能

  1. search方法其实就是遍历所有的集合元素,找到匹配的内容,存放到一个list里面进行展示,通过_readAllDataSuccess进行数据通知界面刷新。因为数据量较小,不到100个左右,所以这里使用的for循环遍历。
class OtherFragmentVM @Inject constructor(private val mRepo: OtherFragmentRepo) : BaseViewModel() {//搜索功能【通信设置、系统设置、功能开启等等】//1. 首先我们需要先添加我们的所有设置。【通过一个bean来存储,一个list来存储所有的】//bean:名称,分类,类型【开关类、输入类、多值类、音量调节】,value//举例:优惠券、功能开启、开关、false//2. 首先把所有的功能项拿到,进行遍历//3. 具体的数据展示:如何展示呢?navigation+fragment??【搜索的时候展示另外一个fragment,而不搜索的时候展示其中一个。】//1.拿到所有的通讯设置var list :ArrayList<OtherEnum> = ArrayList()private var _readAllDataSuccess = MutableLiveData<Int>()//是否读取所有数据成功val readAllDataSuccess: LiveData<Int> get() = _readAllDataSuccess//2.搜索功能fun search(searchText: CharSequence) {list.clear()val arrayList = mRepo.arrayListfor (otherEnum in arrayList) {val stringRes = UiUtil.getStringRes(otherEnum.nameId).uppercase()if (stringRes.contains(searchText.toString().uppercase())) {list.add(otherEnum)}}_readAllDataSuccess.value = list.size}}

3.4 Fragment

  1. 搜索功能:先判断是否输入内容为空,如果为空就提示用户。符合条件就调用search进行模糊匹配搜索。搜索到以后,就将默认显示的navigation进行GONE,将搜索结果进行VISIBLE
class OtherFragment : BaseFragment<BackstageFragmentOtherBinding, OtherFragmentVM>() {private  val TAG = "OtherFragment"override val mViewModel: OtherFragmentVM by viewModels()override fun createVB() = BackstageFragmentOtherBinding.inflate(layoutInflater)private var otherAdapter: OtherAdapter? = nulloverride fun BackstageFragmentOtherBinding.initView() {Log.d(TAG, "OtherFragment initView: ")ivSearch.setOnClickListener {val searchText = etSearch.text.trim()Log.d(TAG, "searchText: "+searchText)if(""==searchText){ToastUtil.switchToastStyleToWarn("请输入内容后搜索")return@setOnClickListener}//进行数据的搜索mViewModel.search(searchText)}with(rvOtherSearch){//设置布局排列方式,默认垂直排列val gridLayoutManager: GridLayoutManager =GridLayoutManager(this@OtherFragment.context, 2, androidx.recyclerview.widget.GridLayoutManager.VERTICAL, false)layoutManager = gridLayoutManagermViewModel.list.add(OtherEnum.CHANGE)otherAdapter = OtherAdapter(mViewModel.list)otherAdapter!!.setItemListener(object : AdapterClickListener {override fun onClickListener(view: View?, position: Int, data: String?) {}})adapter = otherAdapter}}override fun initObserve() {observeLiveData(mViewModel.readAllDataSuccess,::searchResult)}fun searchResult(i: Int) {if (i==0) {ToastUtil.switchToastStyleToWarn("搜索结果为空")mBinding.rvOtherSearch.visibility = View.GONEmBinding.fcvOther.visibility = View.VISIBLEreturn}mBinding.rvOtherSearch.visibility = View.VISIBLEmBinding.fcvOther.visibility = View.GONELog.d(TAG, "searchResult: "+mViewModel.list)otherAdapter?.setData(mViewModel.list)otherAdapter?.notifyDataSetChanged()}override fun initRequestData() {}}

3.5 xml:UI应该如何编写呢?

UI方面:
(1)最简单的:一个输入框;一个搜索按钮;一个recycleview展示搜索结果;一个navigation+FragmentContainerView展示默认的内容,也就是不搜索的时候全部展示。
(2)扩展:1. 搜索历史,热门搜索,输入的时候补全提示。后面可以增加。我们先实现最简单的。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:background="@drawable/no_nav_bg"android:layout_height="match_parent"><net.lucode.hackware.magicindicator.MagicIndicatorandroid:id="@+id/magic_other"android:layout_width="match_parent"android:layout_height="88dp"android:background="@drawable/backstage_shape_product_nav"android:paddingLeft="20dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><EditTextandroid:id="@+id/et_search"android:layout_width="1000dp"android:layout_height="80dp"android:background="@drawable/home_other_rectangle_background"android:paddingLeft="20dp"android:maxLines="1"android:inputType="text"android:layout_marginVertical="10dp"android:hint="@string/backstage_search_hint"android:textColor="#2E80DD"android:textSize="32sp"android:textStyle="bold"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintHorizontal_bias="0.51"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/magic_other" /><ImageViewandroid:id="@+id/iv_search"android:layout_width="90dp"android:layout_height="90dp"android:padding="10dp"android:layout_marginLeft="10dp"android:src="@drawable/search"app:layout_constraintBottom_toBottomOf="@+id/et_search"app:layout_constraintStart_toEndOf="@+id/et_search"app:layout_constraintTop_toTopOf="@+id/et_search" /><androidx.fragment.app.FragmentContainerViewandroid:id="@+id/fcv_other"android:name="androidx.navigation.fragment.NavHostFragment"android:layout_width="match_parent"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/et_search"app:navGraph="@navigation/backstage_other_nav" /><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/rv_other_search"android:layout_width="match_parent"android:layout_height="0dp"android:visibility="gone"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/et_search" /></androidx.constraintlayout.widget.ConstraintLayout>

3.6 Adapter:这里我们需要思考子项不一样应该如何设计,保存也数据逻辑也不一样!

  1. 子项不一样,我们应该如何处理呢?我们可以看到otherEnum里面有一个type属性,就是用于定义不同的布局的,如下:
  2. 那么开关类的保存,输入类的保存有应该如何呢?只需要使用不同的布局进行不同的逻辑进行处理就可以。如下:

class OtherAdapter(var productList: MutableList<OtherEnum>) :RecyclerView.Adapter<RecyclerView.ViewHolder>() {private val TAG = "HomeProductAdapter"var SWITCH_TYPE = 1var INPUT_TYPE = 2fun setItemListener(itemListener: AdapterClickListener?) {this.itemListener = itemListener}private var itemListener: AdapterClickListener? = nullprivate var generalParamData: MutableList<String>? = null//开关类布局inner class MyViewHolder(binding: BackstageItemSystemSettingsBinding) :ViewHolder(binding.root) {private val mBinding = bindingfun bind(otherEnum: OtherEnum) {mBinding.run {Log.d(TAG, "MyViewHolder bind: " + otherEnum)tvName.text = UiUtil.getStringRes(productList[layoutPosition].nameId)//开关类的数据保存rgSwitch.setOnCheckedChangeListener { group, checkedId ->when (checkedId) {R.id.rb_close -> {SpUtils.putBoolean(otherEnum.mmkvName,false)}R.id.rb_open -> {SpUtils.putBoolean(otherEnum.mmkvName,true)}else -> {}}}var flag = SpUtils.getBoolean(otherEnum.mmkvName,otherEnum.drfault as Boolean)if(flag == true){rbOpen.isChecked = true}else{rbClose.isChecked = true}}}}//输入类的布局inner class InputViewHolder(binding: BackstageItemInputBinding) : ViewHolder(binding.root) {private val mBinding = bindingfun bind(otherEnum: OtherEnum) {mBinding.run {Log.d(TAG, "InputViewHolder bind: " +otherEnum.mmkvName+":"+ SpUtils.contains(otherEnum.mmkvName))tvName.text = UiUtil.getStringRes(productList[layoutPosition].nameId)var defaultValue = SpUtils.getString(otherEnum.mmkvName,otherEnum.drfault as String)etValue.setText(defaultValue)//输入类的数据保存btnUpdate.setOnClickListener {val value = etValue.text.trim().toString()if(value==""){ToastUtil.switchToastStyleToWarn("输入为空")return@setOnClickListener}SpUtils.putString(otherEnum.mmkvName,value)ToastUtil.switchToastStyleToSuccess("更新成功:"+otherEnum.mmkvName)}}}}override fun getItemViewType(position: Int): Int {//不同的类型,使用不同的布局return productList[position].type}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {when (viewType) {SWITCH_TYPE -> {return MyViewHolder(BackstageItemSystemSettingsBinding.inflate(LayoutInflater.from(parent.context),parent,false))}INPUT_TYPE -> {Log.d(TAG, "onCreateViewHolder: InputViewHolder:"+viewType)return InputViewHolder(BackstageItemInputBinding.inflate(LayoutInflater.from(parent.context),parent,false))}else -> {}}return MyViewHolder(BackstageItemSystemSettingsBinding.inflate(LayoutInflater.from(parent.context),parent,false))}override fun getItemCount(): Int {return productList.size}override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {if (holder is MyViewHolder) {holder.bind(productList.get(position))} else if (holder is InputViewHolder) {holder.bind(productList.get(position))}itemListener?.onClickListener(holder.itemView, position, null)}fun setData(productList: MutableList<OtherEnum>) {this.productList = productList}
}

四、总结:

其实搜索功能的重点在于数据模型的设计,还有apdater布局的设置。以前都是一个一个控件的增加,数据也可以直接就增加,所以维护很模仿,现在换成了recycleview,所以我们需要思考每一个item,他的数据应该如何展示,默认值是如何,在哪个分类,如何保存数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/880198.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

低代码平台后端搭建-阶段完结

前言 最近又要开始为跳槽做准备了&#xff0c;发现还是写博客学的效率高点&#xff0c;在总结其他技术栈之前准备先把这个专题小完结一波。在这一篇中我又试着添加了一些实际项目中可能会用到的功能点&#xff0c;用来验证这个平台的扩展性&#xff0c;以及总结一些学过的知识。…

数据库数据恢复—Oracle报错“需要更多的恢复来保持一致性”的数据恢复案例

Oracle数据库故障&检测&#xff1a; 打开oracle数据库报错“system01.dbf需要更多的恢复来保持一致性&#xff0c;数据库无法打开”。 数据库没有备份&#xff0c;无法通过备份去恢复数据库。用户方联系北亚企安数据恢复中心并提供Oracle_Home目录中的所有文件&#xff0c;…

大小端字节序 和 内存高低地址顺序

目录 1. 大小端字节序 1.1 什么是大小端字节序&#xff1f; 1.2 为什么有大小端字节序? 1.3 习题&#xff1a;用程序结果判断大端小端 2. 各种易混淆的高低地址顺序 2.1 监视窗口的地址表示【计算机标准展示方式】 2.2 横向地址表示 2.3 一个字节 与 多个字节 的地址…

C语言 | Leetcode C语言题解之第416题分割等和子集

题目&#xff1a; 题解&#xff1a; bool canPartition(int* nums, int numsSize) {if (numsSize < 2) {return false;}int sum 0, maxNum 0;for (int i 0; i < numsSize; i) {sum nums[i];maxNum fmax(maxNum, nums[i]);}if (sum & 1) {return false;}int tar…

《程序猿之设计模式实战 · 适配器模式》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

springboot文件上传+拦截器

springboot文件上传拦截器 文章目录 springboot文件上传拦截器1.静态资源访问静态目录&#xff1a; 2.文件上传文件上传配置文件书写文件上传代码 3.拦截器1.初始化拦截器2.初始化配置文件 1.静态资源访问 静态目录&#xff1a; 这里的static就是spring boot默认存放静态资源的…

ZYNQ FPGA自学笔记~操作PLL

一 时钟缓冲器、管理和路由 垂直时钟中心&#xff08;clock backbone&#xff09;将设备分为相邻的左侧和右侧区域&#xff0c;水平中心线将设备分为顶部和底部两侧。clock backbone中的资源镜像到水平相邻区域的两侧&#xff0c;从而将某些时钟资源扩展到水平相邻区域。BUFG不…

考研数据结构——C语言实现小顶堆

数组初始化&#xff1a; 首先&#xff0c;我们有一个整数数组arr&#xff0c;里面包含了一系列需要排序的数字。数组的长度n是通过对数组arr的总字节大小除以单个元素的字节大小得到的。 小顶堆调整函数&#xff1a; adjustHeapMin函数的作用是将数组中的元素从某个节点向下调整…

[001-02-001].第2节:java开发环境搭建

4.1.书籍推荐&#xff1a; 4.2.人机交互方式 1.图形化界面(Graphical User Interface GUI)这种方式简单直观&#xff0c;使用者易于接受&#xff0c;容易上手操作2.命令行方式(Command Line Interface CLI)&#xff1a;需要有一个控制台&#xff0c;输入特定的指令&#xff0c…

[数据结构]无头单向非循环链表的实现与应用

文章目录 一、引言二、线性表的基本概念1、线性表是什么2、链表与顺序表的区别3、无头单向非循环链表 三、无头单向非循环链表的实现1、结构体定义2、初始化3、销毁4、显示5、增删查改 四、分析无头单向非循环链表1、存储方式2、优点3、缺点 五、总结1、练习题2、源代码 一、引…

Frontiers出版社系列SCISSCI合集

【SciencePub学术】本期&#xff0c;小编根据WOS数据库&#xff0c;整理了一下Frontiers出版社系列的SCI&SSCI合集&#xff0c;以供各位学者投稿参考&#xff01; 来源&#xff1a;WOS数据库 Frontiers系列期刊中&#xff0c;Frontiers in Immunology以其5.7分的影响因子位…

第十四届蓝桥杯嵌入式国赛

一. 前言 本篇博客主要讲述十四届蓝桥杯嵌入式的国赛题目&#xff0c;包括STM32CubeMx的相关配置以及相关功能实现代码以及我在做题过程中所遇到的一些问题和总结收获。如果有兴趣的伙伴还可以去做做其它届的真题&#xff0c;可去 蓝桥云课 上搜索历届真题即可。 二. 题目概述 …

每日一练:二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,n…

合宙LuatOS应用,与时间相关那些事

合宙嵌入式操作系统LuatOS——在蜂窝物联网模组上推出开源二次开发框架&#xff0c;功能齐全性能稳定&#xff0c;可大幅度降低用户的研发成本和研发周期。 在LuatOS中&#xff0c;获取时间函数用得最多的就是os.time()函数了。接下来&#xff0c;我会讲一些与这个函数以及其他…

c++924

2 #include <iostream> #include <cstring>using namespace std;class MyString { private:char *str; // 记录C风格的字符串int size; // 记录字符串的实际长度public:// 定义无参构造MyString() : size(0) {str new char[1];str[0] \0;cou…

中秋节特别游戏:给玉兔投喂月饼

&#x1f5bc;️ 效果展示 &#x1f4dc; 游戏背景 在中秋这个充满诗意的节日里&#xff0c;玉兔因为贪玩被赶下人间。在这个温柔的夜晚&#xff0c;我们希望通过一个小游戏&#xff0c;让玉兔感受到人间的温暖和关怀。&#x1f430;&#x1f319; &#x1f3ae; 游戏设计 人…

5、论文阅读:深水下的图像增强

深水下的图像增强 前言介绍贡献UWCNN介绍网络架构残差Residuals块 Blocks网络层密集串联网络深度减少边界伪影网络损失Loss后处理前言 水下场景中,与波长相关的光吸收和散射会降低图像的可见度,导致对比度低和色偏失真。为了解决这个问题,我们提出了一种基于卷积神经网络的…

伊犁云计算22-1 apache 安装rhel8

1 局域网网络必须通 2 yum 必须搭建成功 3 apache 必须安装 开干 要用su 用户来访问 一看httpd 组件安装完毕 到这里就是测试成功了 如何修改主页的目录 网站目录默认保存在/var/WWW/HTML 我希望改变/home/www 122 127 167 行要改

打造灵活DateTimePicker日期时间选择器组件:轻松实现时间的独立清除功能

element ui中日期和时间选择器&#xff08;DateTimePicker&#xff09;是一个常见且重要的组件。它允许用户轻松地选择日期和时间&#xff0c;极大地提升了用户体验。然而&#xff0c;在某些场景下&#xff0c;用户可能需要更细粒度的控制&#xff0c;例如单独清除已选择的时间…

Swagger 概念和使用以及遇到的问题

前言 接口文档对于前后端开发人员都十分重要。尤其近几年流行前后端分离后接口文档又变 成重中之重。接口文档固然重要,但是由于项目周期等原因后端人员经常出现无法及时更新&#xff0c; 导致前端人员抱怨接口文档和实际情况不一致。 很多人员会抱怨别人写的接口文档不…