• 简简单单搞一个实用的Android端搜索框


    Hello啊老铁们,今天带来一个非常实用的自定义搜索框,包含了搜索框、热门搜索列表、最近搜索列表等常见的功能,有类似的,大家可以直接复用,将会大大节约您的开发时间,有一点,很负责任的告诉大家,实现这个没什么技术含量,就是很简单的自定义组合View,本文除了使用介绍,我也会把具体的实现过程分享给大家。

    今天的内容大概如下:

    1、效果展示

    2、快速使用及属性介绍

    3、具体代码实现

    4、开源地址及总结

    一、效果展示

    效果很常见,就是平常需求中的效果,上面是搜索框,下面是最近和热门搜索列表,为了方便大家在实际需求中使用,配置了很多属性,也进行了上下控件的拆分,也就是上边搜索框和下面的搜索列表的拆分,可以按需进行使用。

    二、快速使用及属性介绍

    快速使用

    目前已经发布至远程Maven,大家可以进行远程依赖使用。

    1、在你的根项目下的build.gradle文件下,引入maven。

    1. allprojects {
    2. repositories {
    3. maven { url "https://gitee.com/AbnerAndroid/almighty/raw/master" }
    4. }
    5. }

    2、在你需要使用的Module中build.gradle文件下,引入依赖。

    1. dependencies {
    2. implementation 'com.vip:search:1.0.0'
    3. }

    具体代码

    1、xml中引入SearchLayout(搜索框)和SearchList(搜索列表),在实际开发中,根据需求可选择使用,二者是互不关联的。

    1. "1.0" encoding="utf-8"?>
    2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. xmlns:app="http://schemas.android.com/apk/res-auto"
    4. xmlns:tools="http://schemas.android.com/tools"
    5. android:layout_width="match_parent"
    6. android:layout_height="match_parent"
    7. android:paddingLeft="10dp"
    8. android:paddingRight="10dp"
    9. tools:context=".MainActivity">
    10. <com.vip.search.SearchLayout
    11. android:id="@+id/search_layout"
    12. android:layout_width="match_parent"
    13. android:layout_height="40dp"
    14. android:layout_marginTop="10dp"
    15. app:layout_constraintLeft_toLeftOf="parent"
    16. app:layout_constraintTop_toTopOf="parent"
    17. app:search_bg="@drawable/shape_stroke_10" />
    18. <com.vip.search.SearchList
    19. android:id="@+id/search_list"
    20. android:layout_width="match_parent"
    21. android:layout_height="wrap_content"
    22. android:layout_marginTop="10dp"
    23. app:is_hot_flex_box_or_grid="true"
    24. app:is_visibility_history_clear="true"
    25. app:layout_constraintTop_toBottomOf="@id/search_layout" />
    26. androidx.constraintlayout.widget.ConstraintLayout>

    2、代码逻辑,以下是测试代码,如用到实际项目,请以实际项目获取控件为主。

    1. class MainActivity : AppCompatActivity() {
    2. override fun onCreate(savedInstanceState: Bundle?) {
    3. super.onCreate(savedInstanceState)
    4. setContentView(R.layout.activity_main)
    5. val searchLayout = findViewById(R.id.search_layout)
    6. val searchList = findViewById(R.id.search_list)
    7. searchLayout.setOnTextSearchListener({
    8. //搜索内容改变
    9. }, {
    10. //软键盘点击了搜索
    11. searchList.doSearchContent(it)
    12. })
    13. //设置用于测试的热门搜索列表
    14. searchList.setHotList(getHotList())
    15. //热门搜索条目点击事件
    16. searchList.setOnHotItemClickListener { s, i ->
    17. Toast.makeText(this, s, Toast.LENGTH_SHORT).show()
    18. }
    19. //历史搜索条目点击事件
    20. searchList.setOnHistoryItemClickListener { s, i ->
    21. Toast.makeText(this, s, Toast.LENGTH_SHORT).show()
    22. }
    23. }
    24. /**
    25. * AUTHOR:AbnerMing
    26. * INTRODUCE:模拟热门搜索列表
    27. */
    28. private val mTestHotList = arrayListOf(
    29. "二流小码农", "三流小可爱", "Android",
    30. "Kotlin", "iOS", "Java", "Python", "Php是世界上最好的语言"
    31. )
    32. private fun getHotList(): ArrayList {
    33. return ArrayList().apply {
    34. mTestHotList.forEachIndexed { index, s ->
    35. val bean = SearchBean()
    36. bean.content = s
    37. bean.isShowLeftIcon = true
    38. val drawable: Drawable? = if (index < 2) {
    39. ContextCompat.getDrawable(this@MainActivity, R.drawable.shape_circle_select)
    40. } else if (index == 2) {
    41. ContextCompat.getDrawable(this@MainActivity, R.drawable.shape_circle_ordinary)
    42. } else {
    43. ContextCompat.getDrawable(this@MainActivity, R.drawable.shape_circle_normal)
    44. }
    45. drawable?.setBounds(0, 0, drawable.minimumWidth, drawable.minimumHeight)
    46. bean.leftIcon = drawable
    47. add(bean)
    48. }
    49. }
    50. }
    51. }

    主要方法介绍

    1、搜索框监听

    拿到searchLayout控件之后,调用setOnTextSearchListener方法即可,第一个方法是搜索内容发生变化会回调,第二个方法是,点击了软键盘的搜索按钮会回调,如果要在最近搜索里展示,直接调用doSearchContent方法即可。

    1. searchLayout.setOnTextSearchListener({
    2. //搜索内容改变
    3. }, {
    4. //软键盘点击了搜索
    5. searchList.doSearchContent(it)
    6. })

    2、搜索列表点击事件

    热门搜索调用setOnHotItemClickListener方法,历史搜索也就是最近搜索调用setOnHistoryItemClickListener方法,都是两个参数,第一个是文本内容,第二个是索引,也就是点的是哪一个。

    1. //热门搜索条目点击事件
    2. searchList.setOnHotItemClickListener { s, i ->
    3. Toast.makeText(this, s, Toast.LENGTH_SHORT).show()
    4. }
    5. //历史搜索条目点击事件
    6. searchList.setOnHistoryItemClickListener { s, i ->
    7. Toast.makeText(this, s, Toast.LENGTH_SHORT).show()
    8. }

    3、改变最近(历史)搜索item背景

    有的老铁说了,默认的背景我不喜欢,能否可以动态设置,必须能!

    默认背景

    设置背景,通过setHistoryItemBg方法。

    searchList.setHistoryItemBg(R.drawable.shape_solid_d43c3c_10)

    效果展示

    4、动态设置热门搜索热度

    可能有很多需求中,需要展示几个热度,有的是按照颜色区分,如下图:

    实现起来很简单,在设置热门列表(setHotList)的时候,针对传递的对象设置leftIcon即可。测试代码如下:

    1. private fun getHotList(): ArrayList {
    2. return ArrayList().apply {
    3. mTestHotList.forEachIndexed { index, s ->
    4. val bean = SearchBean()
    5. bean.content = s
    6. bean.isShowLeftIcon = true
    7. val drawable: Drawable? = if (index < 2) {
    8. ContextCompat.getDrawable(this@MainActivity, R.drawable.shape_circle_select)
    9. } else if (index == 2) {
    10. ContextCompat.getDrawable(this@MainActivity, R.drawable.shape_circle_ordinary)
    11. } else {
    12. ContextCompat.getDrawable(this@MainActivity, R.drawable.shape_circle_normal)
    13. }
    14. drawable?.setBounds(0, 0, drawable.minimumWidth, drawable.minimumHeight)
    15. bean.leftIcon = drawable
    16. add(bean)
    17. }
    18. }
    19. }

    具体的哪个数据展示什么颜色,直接设置即可,想怎么展示就怎么展示。当然了除了展示不同的热度之外,还有一些其他的变量,isShowLeftIcon为是否展示文字左边的icon,textColor为当前文字的颜色,根据不同的颜色,我们也可以实现下面的效果。

    除了常见的方法之外,还提供了很多的属性操作,具体的大家可以看下面,按需使用即可。

    属性介绍

    为了让功能灵活多变,也为了满足更多的需求样式,目前自定义了很多属性,大家可以按自己的需要进行设置,或者直接去GitHub中下载源码更改也可以。

    SearchLayout(搜索框属性)

    属性

    类型

    概述

    search_icon

    reference

    搜索图标,可直接从drawable或者mipmap中设置

    search_icon_width

    dimension

    搜索图标的宽

    search_icon_height

    dimension

    搜索图标的高

    search_icon_left

    dimension

    搜索图标距离左边的距离

    search_icon_delete

    reference

    搜索删除图标,右侧的删除

    search_icon_delete_width

    dimension

    搜索删除图标的宽

    search_icon_delete_height

    dimension

    搜索删除图标的高

    search_icon_delete_right

    dimension

    搜索删除图标距离右边的距离

    search_hint

    string

    搜索框占位字符

    search_hint_color

    color

    搜索框占位字符颜色

    search_color

    color

    搜索框文字颜色

    search_size

    dimension

    搜索框文字大小

    search_text_cursor

    reference

    搜索框光标

    search_bg

    reference

    整个搜索框背景

    SearchList(搜索列表属性)

    属性

    类型

    概述

    is_hot_flex_box_or_grid

    boolean

    热门搜索列表样式,是网格还是流式布局

    is_hot_center

    boolean

    热门搜索列表样式,内容是否居中

    hot_grid_span_count

    integer

    热门搜索列表样式,如果是网格布局,条目列数,默认2

    hot_item_top_margin

    integer

    热门搜索列表 item距离上边的距离

    hot_item_color

    color

    热门搜索列表 item 文字颜色

    hot_item_size

    dimension

    热门搜索列表 item 文字大小

    hot_item_line

    integer

    热门搜索列表 item 文字展示几行

    hot_item_bg

    reference

    热门搜索列表 item 背景

    hot_item_margin_top

    reference

    热门搜索列表 item 距离上边的距离

    hot_padding_left

    dimension

    热门搜索列表 内边距,左

    hot_padding_top

    dimension

    热门搜索列表 内边距,上

    hot_padding_right

    dimension

    热门搜索列表 内边距,右

    hot_padding_bottom

    dimension

    热门搜索列表 内边距,下

    is_history_flex_box_or_grid

    boolean

    历史搜索列表样式,是网格还是流式布局

    history_flex_box_count

    integer

    历史搜索列表,最多展示几个item,默认10

    is_history_center

    boolean

    历史搜索列表样式,内容是否居中

    history_grid_span_count

    integer

    历史搜索列表样式,如果是网格布局,条目列数,默认2

    history_item_top_margin

    integer

    历史搜索列表 item距离上边的距离

    history_item_color

    color

    历史搜索列表 item 文字颜色

    history_item_size

    dimension

    历史搜索列表 item 文字大小

    history_item_margin_top

    dimension

    历史搜索列表 item 距离上边的距离

    is_visibility_history_clear

    boolean

    历史搜索右边是否展示清除小按钮

    history_clear_icon

    reference

    历史搜索右边的清除小按钮

    history_clear_text

    string

    历史搜索右边的清除文字

    history_clear_size

    dimension

    历史搜索右边的清除文字大小

    history_clear_color

    color

    历史搜索右边的清除文字颜色

    history_padding_left

    dimension

    历史搜索列表 内边距,左

    history_padding_top

    dimension

    历史搜索列表 内边距,上

    history_padding_right

    dimension

    历史搜索列表 内边距,右

    history_padding_bottom

    dimension

    历史搜索列表 内边距,下

    三、具体代码实现

    关于这个组合View的实现方式,我是分为了两个View,大家在上边的使用中应该也看到了,一个是搜索框SearchLayout,一个是搜索框下面的搜索列表展示SearchList,开头就阐述了,没啥技术含量,简单的罗列下代码实现吧。

    SearchLayout是一个组合View,中间是一个EditText,左右两边是一个ImageView,也就是搜索图标和删除图标,如下图:

    SearchLayout本身没有啥要说的,无非就是把View组合到了一起,在开发的时候,既然要给别人使用,那么就要拓展出很多的动态属性或者方法出来,这是很重要的,所以,在封装的时候,自定义属性无比的重要,需要精确和认真,这一块没啥好说的,有一点需要注意,也就是EditText绑定软键盘搜索,除了设置属性android:imeOptions="actionSearch",也要设置,android:singleLine="true",方可生效。

    SearchList其实也没啥好说的,也是一个组合View,使用的是上下两个RecyclerView来实现的,至于流失布局,采用的是google提供的flexbox,设置布局管理器即可。

    recyclerView.layoutManager = FlexboxLayoutManager(mContext)

    除了这个之外,可能需要阐述的也就是最近搜索的存储机制了,存储呢,Android中提供了很多的存储方式,比如数据库,SharedPreferences,SD卡,还有DataStore,MMKV等,无论哪一种吧,选择适合的即可,这个开源中,不想引入其他的三方了,直接使用的是SharedPreferences。

    具体的实现方式,把搜索的内容,转成json串,以json串的形式进行存储,这里借助了原生的JSONArray和JSONObject。流程就是,触发搜索内容后,先从SharedPreferences取出之前存储的内容,放到JSONArray中,当前搜索内容如果存在JSONArray中,那边就要执行删除原来的,再把新的内容插入到第一个的位置,如果不存在JSONArray中,直接添加即可,随后再转成字符串存储即可。

    当然了,一般在正常的需求开发中,最近搜索列表肯定不是无限展示的,都有固定的展示个数,比如10个,比如15个,所以,当超过指定的个数,也就是指定的阀门后,就要执行删除的操作。

    1. val searchHistory = getSearchHistory()
    2. if (!TextUtils.isEmpty(it)) {
    3. val jsonArray: JSONArray = if (TextUtils.isEmpty(searchHistory)) {
    4. JSONArray()
    5. } else {
    6. JSONArray(searchHistory)
    7. }
    8. val json = JSONObject()
    9. json.put("content", it)
    10. //如果出现了一样的,删除后,加到第一个
    11. var isEqual = false
    12. var equalPosition = 0
    13. for (i in 0 until jsonArray.length()) {
    14. val item = jsonArray.getJSONObject(i)
    15. val content = item.getString("content")
    16. if (it == content) {
    17. isEqual = true
    18. equalPosition = i
    19. break
    20. }
    21. }
    22. //有一样的
    23. if (isEqual) {
    24. jsonArray.remove(equalPosition)
    25. } else {
    26. //超过了指定的阀门之后,就不在扩充
    27. if (jsonArray.length() >= mHistoryListSize) {
    28. jsonArray.remove(0)
    29. }
    30. }
    31. jsonArray.put(json)
    32. SearchSharedPreUtils.put(mContext!!, "search_history", jsonArray.toString())
    33. }
    34. getSearchHistory()?.let {
    35. eachSearchHistory(it)
    36. }
    37. //两个有一个不为空,展示
    38. if (!TextUtils.isEmpty(it) || !TextUtils.isEmpty(searchHistory)) {
    39. showOrHideHistoryLayout(View.VISIBLE)
    40. }

    当然了,存储的逻辑,有很多的实现的方式,这里并不是最优的,只是提供了一种思路,大家可以按照自己的方式来操作。

    四、开源地址及总结

    开源地址:https://github.com/AbnerMing888/SearchLayout

    搜索列表,无论是热门还是最近的搜索列表,均支持网格和流失布局形式展示,大家看属性相关介绍中即可。这个搜索框本身就是很简单的效果还有代码,大家直接看源码或文中介绍即可,就不多赘述了。

  • 相关阅读:
    【网络编程】TCP Socket编程
    2.Android系统启动
    决策树算法之鸢尾花特征分类可视化详解【机器学习】
    freeCodeCamp响应式网页设计笔记
    代码随想录算法训练营第2天| 977有序数组的平方、209长度最小的子数组。
    【网络编程】- TCP/UDP 套接字编程
    Windows批处理:bat文件学习
    使用阿里云服务器学习Docker
    漫谈AI 时代的信息模型
    Spring/SpringBoot注解用法解析/案例
  • 原文地址:https://blog.csdn.net/ming_147/article/details/127765894