这里用到了网络。。用的php构建的假数据
核心代码如下
<?php
// ---------------------------引入接口参数类(以用户实际路径为准)---------------------------------
namespace App\Services\test;
use App\Common\Services\BaseService;
use App\Http\Controllers\ExpressController;
use App\Services\express\ExpressService;
use App\Services\utils\IpServices;
class TestService extends BaseService
{
public function index($key): \Illuminate\Http\JsonResponse
{
$data = array();
$dataSub = array();
$dataSub['text'] = $key . "乱七八糟噢噢噢噢的数据";
$dataSub['id'] = 1;
$data[] = $dataSub;
$dataSub['text'] = $key . "你说不用自作自受自己创造伤悲,写歌的人就应该有伤悲";
$dataSub['id'] = 2;
$data[] = $dataSub;
$dataSub['text'] = $key . "去吗,配吗 这褴褛的披风";
$dataSub['id'] = 3;
$data[] = $dataSub;
return $this->apiSuccess("1", $data);
}
}
最后返回json结果如下
{
"code": 20000,
"message": "1",
"data": [{
"text": "123乱七八糟噢噢噢噢的数据",
"id": 0
}, {
"text": "123你说不用自作自受自己创造伤悲,写歌的人就应该有伤悲",
"id": 1
}, {
"text": "123去吗,配吗 这褴褛的披风",
"id": 1
}]
}
好 正题开始咯
package com.example.android_flow_practice.net
import com.example.android_flow_practice.model.Article
import com.example.android_flow_practice.model.NetResponse
import retrofit2.http.GET
import retrofit2.http.Query
interface ArticleApi {
@GET("api/v1/open/test")
suspend fun searchArticles(
@Query("key") key: String
): NetResponse<List<Article>>
}
package com.example.android_flow_practice.net
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.create
object RetrofitClient {
val url = "https://xxx.xxx.com/";
private val instance: Retrofit by lazy {
Retrofit.Builder().client(OkHttpClient.Builder().build()).baseUrl(url)
.addConverterFactory(GsonConverterFactory.create()).build()
}
val articleApi: ArticleApi by lazy {
instance.create(ArticleApi::class.java)
}
}
依然是马赛克的url。。多余的靠自己
准备adapter
package com.example.android_flow_practice.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.example.android_flow_practice.databinding.ItemArticleBinding
import com.example.android_flow_practice.databinding.ItemUserBinding
import com.example.android_flow_practice.db.User
import com.example.android_flow_practice.model.Article
class ArticleAdapter(private val context: Context) : RecyclerView.Adapter<BindingViewHolder>() {
private val data = ArrayList<Article>()
fun setData(data: List<Article>) {
this.data.clear()
this.data.addAll(data);
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
val binding = ItemArticleBinding.inflate(LayoutInflater.from(context), parent, false)
return BindingViewHolder(binding = binding)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: BindingViewHolder, position: Int) {
val item = data[position]
val binding = holder.binding as ItemArticleBinding
binding.text.text = "${item.id}, ${item.text}"
}
}
对应的viewHolder
package com.example.android_flow_practice.adapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
class BindingViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {}
布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingVertical="4dp"
android:textSize="26sp" />
</LinearLayout>
然后adapter是在Fragment当中的
Fragment布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
tools:context=".fragment.UserFragment">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/ed_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Input keyword for search"
android:padding="8.dp"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
布局完事以后代码
package com.example.android_flow_practice.fragment
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.example.android_flow_practice.R
import com.example.android_flow_practice.adapter.ArticleAdapter
import com.example.android_flow_practice.databinding.FragmentArticleBinding
import com.example.android_flow_practice.databinding.FragmentDownloadBinding
import com.example.android_flow_practice.viewmodel.ArticleViewModel
import com.example.android_flow_practice.viewmodel.UserViewModel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.collect
class ArticleFragment : Fragment() {
private val TAG = "ArticleFragment"
private val viewModel: ArticleViewModel by viewModels()
private val mBinding: FragmentArticleBinding by lazy {
FragmentArticleBinding.inflate(layoutInflater)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return mBinding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lifecycleScope.launchWhenCreated {
mBinding.edSearch.textWatcherFlow().collect {
Log.e(TAG, "onActivityCreated: ${it}")
viewModel.searchArticles(it)
}
}
context?.let {
val adapter = ArticleAdapter(it)
mBinding.rv.adapter = adapter
viewModel.articles.observe(viewLifecycleOwner, { artices ->
adapter.setData(artices)
})
}
}
//获取关键字
fun TextView.textWatcherFlow(): Flow<String> = callbackFlow {
val textWatcher = object : TextWatcher {
override fun beforeTextChanged(
s: CharSequence?, start: Int, count: Int, after: Int
) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
trySend(s.toString()).isSuccess
}
}
addTextChangedListener(textWatcher)
awaitClose { removeTextChangedListener(textWatcher) }
}
}
核心代码存放与viewModel当中
package com.example.android_flow_practice.viewmodel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.example.android_flow_practice.model.Article
import com.example.android_flow_practice.net.RetrofitClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import okhttp3.Dispatcher
class ArticleViewModel(app: Application) : AndroidViewModel(app) {
val articles = MutableLiveData<List<Article>>()
fun searchArticles(key: String) {
viewModelScope.launch {
flow {
val list = RetrofitClient.articleApi.searchArticles(key)
emit(list)
}.flowOn(Dispatchers.IO).catch { e -> e.printStackTrace() }.collect {
it.data.let {
articles.setValue(it)
}
}
}
}
}
这里简单拆析下这里
使用flow 监听
afterTextChanged 将文本变动后的数据返回过来。
又监听其关闭方法
awaitClose 对textWather进行解除监听也就是释放
这是其一
其二 将viewModel的搜索列表数据弄到了articles
然后再次对其进行监听 这样结构数据就不是collect{ collect{} }了
那我们试下这种错误的写法看看会怎么样
collect{ collect{} }
viewModel中复制修改
fun searchArticles2(key: String) = flow {
val list = RetrofitClient.articleApi.searchArticles(key)
emit(list)
}.flowOn(Dispatchers.IO).catch { e -> e.printStackTrace() }
fragment也有所改变
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lifecycleScope.launchWhenCreated {
mBinding.edSearch.textWatcherFlow().collect {
Log.e(TAG, "onActivityCreated: ${it}")
viewModel.searchArticles2(it).collect{ dataList->
context?.let {
val adapter = ArticleAdapter(it)
mBinding.rv.adapter = adapter
adapter.setData(dataList.data)
}
}
}
}
这样做依然没有问题
但是并不推荐。
因为collect{ collect{} } 并不是流的设计思想。。
collect{ }
collect{} 这种事
你可以明显的感觉到 使用上述不推荐的写法
collect{ collect{} } 这种写法。速度完全跟不上
collect{ }
collect{}