1. 前言
最近项目中需要做模糊搜索的功能,刚开始觉得很难,但仔细想想也没什么,因为具体的模糊搜索部分是后端提供接口,而我们需要做的就是当搜索框内容发生变化时就执行一次搜索功能而已。
2. 效果图
![效果图](https://img-blog.csdnimg.cn/69b574a11b1c4dce9ef742595e36d546.gif#pic_center)
可以看到
- 文本框在输入时实时进行搜索
- 当搜索没有结果时,底部的结果展示直接隐藏掉
- 茅台并不坚挺…
3. 思路
-
搜索框部分使用EditText,通过
addTextChangedListener
方法来实现对其的监听 -
接口部分,这里我使用百度的接口,简单的通过Retrofit2来实现网络请求
-
模糊搜索结果展示部分,我使用Recyclerview来实现对模糊搜索结果的展示
4. 实现
4.1 EditText监听部分
方法解释:
EditText的addTextChangedListener
监听如下,可以看到共有三个方法
-
beforeTextChanged()
在EditText输入前,先调用一次该方法
-
afterTextChanged()
在EditText输入完成后,调用一次该方法
-
onTextChanged()
在EditText输入过程中,不断的回调该方法
etContent.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
效果实现:
我们想实现的效果有如下内容
- 当输入框内有内容时则显示删除按钮,没有内容时则隐藏
- 在输入过程中不断的进行网络请求
- 搜索有结果时则显示Recyclerview,没有数据则隐藏
具体实现:
etContent.addTextChangedListener(object : TextWatcher {
......
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (etContent.text.isNullOrEmpty()) {
//输入框为空,删除按钮隐藏
ivDel.visibility = View.GONE
//当输入框为空时,隐藏recyclerview,目的是隐藏上一次的搜索结果
recy.visibility = View.GONE
} else {
//输入框不为空,删除按钮展示
ivDel.visibility = View.VISIBLE
//输入框发生变化就进行网络请求
mainViewModel.getFuzzySearchList(Const.fuzzySearchUrl,etContent.text.toString())
}
}
})
4.2 网络请求部分
接口部分:
-
模糊搜索
@GET
请求参数:
key:wd
value:要搜索的内容
地址:
https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su
使用以上接口,传入一个参数wd即可,wd为我们输入框的内容
interface ApiService { @GET fun getFuzzySearchList(@Url url:String,@Query("wd") content:String): Call<ResponseBody> }
-
详情接口
Url为:
http://www.baidu.com.cn/s?wd=
+文本框内容
+&cl=3
这里我们简单的通过webview加载该网页即可
逻辑部分:
- 请求完成后若数据为空,则要隐藏Recyclerview面板,反之则显示
- 请求完成后对Recyclerview进行更新
5. 具体实现
MainActivity
class MainActivity : AppCompatActivity() {
//创建ViewModel
private val mainViewModel: MainViewModel by lazy {
ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application)).get(
MainViewModel::class.java
)
}
private val data = mutableListOf<String>()
private val myAdapter = MyAdapter(data)
override fun onCreate(savedInstanceState: Bundle?) {
...
//Recyclerview相关配置
recy.layoutManager = LinearLayoutManager(this)
recy.adapter = myAdapter
//跳转到详情页
myAdapter.setOnMyClickListener(object :MyAdapter.MyClickListener{
override fun onClick(position: Int) {
val intent = Intent(this@MainActivity,NewsShowActivity::class.java)
val url = Const.serchUrl+data[position]+"&cl=3"
intent.putExtra("url",url)
startActivity(intent)
//跳转后清空搜索框,隐藏Recyclerview面板
etContent.setText("")
recy.visibility = View.GONE
}
//模糊搜索结果监听
mainViewModel.getResultLiveData().observe(this, Observer {
if(it.isEmpty()){
//数据为空,隐藏RecyclerView面板
recy.visibility = View.GONE
}else{
recy.visibility = View.VISIBLE
}
//搜索完成后对RecyclerView更新
data.clear()
data.addAll(it)
myAdapter.notifyDataSetChanged()
})
//EditText搜索框进行监听
etContent.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (etContent.text.isNullOrEmpty()) {
ivDel.visibility = View.GONE
//为空时如果不隐藏, 上一次搜索的结果就会在下面显示不会隐藏
recy.visibility = View.GONE
} else {
ivDel.visibility = View.VISIBLE
//进行网络请求
mainViewModel.getFuzzySearchList(Const.fuzzySearchUrl,etContent.text.toString())
}
}
})
}
}
MainViewModel
模糊搜索返回的数据是如下格式,而且是GBK编码,所以我们在请求成功后需要对数据进行下处理
window.baidu.sug({q:"奥运会",p:false,s:["奥运会","奥运会女子200米蝶泳决赛","奥运会奖牌榜","奥运会赛程","奥运会金牌榜2021","奥运会乒乓球","奥运会直播","奥运会多少年举办一次","奥运会在哪看","奥运会裁判"]});
class MainViewModel :ViewModel(){
private val httpClient = OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.build()
private val retrofit: Retrofit =Retrofit.Builder()
.baseUrl("http://a")
.client(httpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
private val api = retrofit.create(ApiService::class.java)
/**
* 模糊搜索
*/
fun getFuzzySearchList(url:String,content:String){
api.getFuzzySearchList(url,content).enqueue(object : retrofit2.Callback<ResponseBody> {
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
errorLiveData.value = t.message
}
override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
if(response?.body() != null){
//因为模糊搜索的结果是GBK编码,所以这里需要处理下
val charset = response.body()!!.contentType()!!.charset()!!
val source = response?.body()?.source()
source?.let {
source.skip(ByteString.decodeHex("efbbbf").size().toLong())
}
val string = response?.body()?.string()
string?.let {
val startIndex = it.indexOf("(")
val endIndex = it.length-2
//截取
val json = it.substring(startIndex+1, endIndex)
val jsonObject = gson.fromJson<ResultBean>(json, ResultBean::class.java)
//对数据进行更新
resultLiveData.value = jsonObject.s
}
}
}
})
}
}
6. 总结
以上就是全部内容,其实也挺简单的,我们这边其实就是对EditText监听,然后对网络请求的结果进行处理就可以了,其余部分的逻辑就是什么时候显示与隐藏的事情。
如果本文对你有帮助,请别忘记三连,如果有不恰当的地方也请提出来,下篇文章见。