JetPack

您所在的位置:网站首页 viewpager2刷新adapter JetPack

JetPack

2023-03-08 08:53| 来源: 网络整理| 查看: 265

前面我们使用过Paging,最新版本Paging3和以前对比,有所改动 Paging2->Paging3三个模块改为: 1.DataSource->PagingSource : 数据从该模块中获取,数据可以来源于网络、本地数据库等 2.PagedList->Pager : 负责具体获取数据的逻辑,何时获取、加载下一页、预加载等 3.PagedListAdapter->PagingDataAdapter : RecyclerView的adapter需要继承它,内部做了一系列处理 一、paging3上手

效果:

1.首先配置gradle

使用kapt插件

plugins { id 'kotlin-kapt' }

DataBinding支持

defaultConfig { dataBinding{ enabled = true } }

添加依赖

implementation 'androidx.legacy:legacy-support-v4:1.0.0' //依赖协程核心库 ,提供Android UI调度器 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1" //依赖当前平台所对应的平台库 (必须) implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation "com.squareup.retrofit2:converter-gson:2.9.0" implementation 'com.squareup.picasso:picasso:2.71828' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-rc01' implementation 'androidx.paging:paging-runtime-ktx:3.0.0-beta03' implementation "androidx.activity:activity-ktx:1.1.0" implementation "androidx.fragment:fragment-ktx:1.2.5" 2.根据服务器返回数据创建实体类

服务器数据:

{ "has_more":true, "subjects":[ { "id":35076714, "title":"扎克·施奈德版正义联盟", "cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2634360594.webp", "rate":"8.9" }, { "id":26935283, "title":"侍神令", "cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2629260713.webp", "rate":"5.8" }, { "id":35145068, "title":"双层肉排", "cover":"https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2633977758.webp", "rate":"6.7" }, { "id":33433405, "title":"大地", "cover":"https://img9.doubanio.com/view/photo/s_ratio_poster/public/p2628845704.webp", "rate":"6.6" }, { "id":35167535, "title":"租来的朋友", "cover":"https://img2.doubanio.com/view/photo/s_ratio_poster/public/p2616903233.webp", "rate":"6.1" } ] } package com.aruba.paging3application.entity /** * Created by aruba on 2021/9/22. */ data class Movie( val id: Int, val title: String, val cover: String, val rate: String ) package com.aruba.paging3application.entity import com.google.gson.annotations.SerializedName /** * Created by aruba on 2021/9/22. */ data class Movies( @SerializedName("subjects") val movies: List, @SerializedName("has_more") val hasMore: Boolean ) 3.定义网络相关

Api:

package com.aruba.flowapplyapplication.net import com.aruba.paging3application.entity.Movies import retrofit2.http.GET import retrofit2.http.Query /** * Created by aruba on 2021/9/21. */ interface Api { @GET("pkds.do") suspend fun getMovies( @Query("page") page: Int, @Query("pagesize") pagesize: Int ): Movies }

retrofit工具类:

package com.aruba.flowapplyapplication.net import okhttp3.OkHttpClient import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory /** * Created by aruba on 2021/9/21. */ object RetrofitClient { private val instance: Retrofit by lazy { Retrofit.Builder() .baseUrl("http://192.168.17.114:8080/pagingserver_war/") .client(OkHttpClient.Builder().build()) .addConverterFactory(GsonConverterFactory.create()) .build() } fun getApi(clazz: Class): T { return instance.create(clazz) as T } } 4.定义RecyclerView的Adapter相关

布局文件:

BingingAdapter,自定义标签image:

package com.aruba.paging3application.bindingAdapter import android.widget.ImageView import androidx.databinding.BindingAdapter import com.aruba.paging3application.R import com.squareup.picasso.Picasso /** * Created by aruba on 2021/9/22. */ @BindingAdapter("image") fun setImage(imageView: ImageView, imageUrl: String) { Picasso.get().load(imageUrl).placeholder(R.drawable.ic_launcher_foreground) .error(R.drawable.ic_launcher_foreground) .into(imageView); }

Adapter,继承于PagingDataAdapter,和paging2一样需要DiffUtil.ItemCallback:

package com.aruba.paging3application.adapter import android.view.LayoutInflater import android.view.ViewGroup import androidx.paging.DifferCallback import androidx.paging.PagingDataAdapter import androidx.recyclerview.widget.DiffUtil import com.aruba.paging3application.databinding.ItemBinding import com.aruba.paging3application.entity.Movie /** * Created by aruba on 2021/9/22. */ class MoviePagingAdapter : PagingDataAdapter( object : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean { return oldItem == newItem } } ) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder { val binding = ItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) return BindingViewHolder(binding) } override fun onBindViewHolder(holder: BindingViewHolder, position: Int) { (holder.binding as ItemBinding).movie = getItem(position) } } 5.定义PagingSource

继承PagingSource,实现load函数,返回值为LoadResult,可以使用LoadResult.Page实例化,入参为继承时定义的第二个泛型,和上一页和下一页的两个Key,Key对应的第一个泛型

package com.aruba.paging3application.paging import android.util.Log import androidx.paging.PagingSource import androidx.paging.PagingState import com.aruba.flowapplyapplication.net.Api import com.aruba.flowapplyapplication.net.RetrofitClient import com.aruba.paging3application.entity.Movie import com.aruba.paging3application.entity.Movies /** * Pagind获取数据层 * Created by aruba on 2021/9/22. */ class MoviePagingSource : PagingSource() { companion object { const val pageSize = 10 } //该办法只在初始加载成功且加载页面的列表不为空的情况下被调用。 override fun getRefreshKey(state: PagingState): Int? { return null } override suspend fun load(params: LoadParams): LoadResult { val currentPage = params.key ?: 1 val movies = RetrofitClient.getApi(Api::class.java).getMovies(currentPage, pageSize) //上一页的key val prevKey: Int? = (currentPage - 1).run { if (this == 0) {//说明当前是第一页数据 null } else { this } } //下一页的key val nextKey: Int? = when { params.loadSize > pageSize -> {//第一次加载的会多一些 // public val initialLoadSize: Int = pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER, // 默认PagingConfig为pager分配初始获取数据的大小为pageSize * DEFAULT_INITIAL_PAGE_MULTIPLIER // 所以Pager配置时,如果initialLoadSize不指定,那么第一次加载数据并不是我们定义的pageSize val count = params.loadSize / pageSize count + 1 } movies.hasMore -> { currentPage + 1 } else -> { null } } return LoadResult.Page( data = movies.movies, prevKey = prevKey, nextKey = nextKey ) } } 6.定义Pager

Pager可以返回一个Flow,使用一个ViewModel获取Pager的Flow,下流就可以收集了

package com.aruba.paging3application.viewmodel import androidx.lifecycle.ViewModel import androidx.paging.Pager import androidx.paging.PagingConfig import com.aruba.paging3application.paging.MoviePagingSource /** * Created by aruba on 2021/9/22. */ class MovieViewModel : ViewModel() { //绑定page的flow,返回是一个flow对象 fun bindPage() = Pager(config = PagingConfig( initialLoadSize = MoviePagingSource.pageSize,//初次加载的数据量大小 pageSize = MoviePagingSource.pageSize, enablePlaceholders = false ), pagingSourceFactory = { MoviePagingSource() } ).flow } 7.Activity中实例化ViewModel、配置RecyclerView等 package com.aruba.paging3application import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.aruba.paging3application.adapter.MoviePagingAdapter import com.aruba.paging3application.databinding.ActivityMainBinding import com.aruba.paging3application.viewmodel.MovieViewModel import kotlinx.coroutines.flow.collect class MainActivity : AppCompatActivity() { private val movieViewModel by viewModels() private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) val adapter = MoviePagingAdapter() binding.recyclerView.adapter = adapter binding.recyclerView.layoutManager = LinearLayoutManager(this) lifecycleScope.launchWhenCreated { movieViewModel.bindPage().collect { adapter.submitData(it) } } } } 二、加载更多

效果:

LoadStateAdapter

PagingDataAdapter支持设置一个LoadStateAdapter,来显示加载更多 定义Adapter继承于LoadStateAdapter:

package com.aruba.paging3application.adapter import android.view.LayoutInflater import android.view.ViewGroup import androidx.paging.LoadState import androidx.paging.LoadStateAdapter import com.aruba.paging3application.databinding.MovieLoadmoreBinding /** * Created by aruba on 2021/9/22. */ class LoadMoreAdapter : LoadStateAdapter() { override fun onBindViewHolder(holder: BindingViewHolder, loadState: LoadState) { } override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): BindingViewHolder { val binding = MovieLoadmoreBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return BindingViewHolder(binding) } }

布局为:

PagingDataAdapter设置下就可以了

val adapter = MoviePagingAdapter() binding.recyclerView.adapter = adapter.withLoadStateFooter(LoadMoreAdapter()) 三、下拉刷新

效果:

在布局中为RecyclerView套一层 SwipeRefreshLayout后,在Activity中设置 刷新监听 binding.apply { recyclerView.adapter = adapter.withLoadStateFooter(LoadMoreAdapter()) recyclerView.layoutManager = LinearLayoutManager(this@MainActivity) //开始刷新 refreshLayout.setOnRefreshListener { adapter.refresh() } }

对Adapter的状态进行监听,来更新SwipeRefreshLayout的刷新状态

//监听adapter状态 adapter.loadStateFlow.collect { //根据刷新状态来通知swiprefreshLayout是否刷新完毕 binding.refreshLayout.isRefreshing = it.refresh is LoadState.Loading }

完整代码:

package com.aruba.paging3application import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import androidx.paging.LoadState import androidx.recyclerview.widget.LinearLayoutManager import com.aruba.paging3application.adapter.LoadMoreAdapter import com.aruba.paging3application.adapter.MoviePagingAdapter import com.aruba.paging3application.databinding.ActivityMainBinding import com.aruba.paging3application.viewmodel.MovieViewModel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { private val movieViewModel by viewModels() private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) val adapter = MoviePagingAdapter() binding.apply { recyclerView.adapter = adapter.withLoadStateFooter(LoadMoreAdapter()) recyclerView.layoutManager = LinearLayoutManager(this@MainActivity) //开始刷新 refreshLayout.setOnRefreshListener { adapter.refresh() } } lifecycleScope.launchWhenCreated { launch { movieViewModel.bindPage().collect { adapter.submitData(it) } } launch { //监听adapter状态 adapter.loadStateFlow.collect { //根据刷新状态来通知swiprefreshLayout是否刷新完毕 binding.refreshLayout.isRefreshing = it.refresh is LoadState.Loading } } } } } 四、瞬态数据缓存

目前我们写的代码是不带瞬态数据缓存的:

想要实现瞬态数据缓存,则要 将Flow变为ViewModel的属性,并为它设置作用域 package com.aruba.paging3application.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.cachedIn import com.aruba.paging3application.paging.MoviePagingSource /** * Created by aruba on 2021/9/22. */ class MovieViewModel : ViewModel() { private val pagerFlow by lazy { Pager(config = PagingConfig( initialLoadSize = MoviePagingSource.pageSize * 2,//初次加载的数据量大小 pageSize = MoviePagingSource.pageSize, enablePlaceholders = false ), pagingSourceFactory = { MoviePagingSource() } ).flow.cachedIn(viewModelScope) } //绑定page的flow,返回是一个flow对象 fun bindPage() = pagerFlow }

效果:

Demo地址:https://gitee.com/aruba/paging3-application.git


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3