网站建设与网页设计心得体会,单机多wordpress,phpmysql网站开发全程实例,快速seo排名优化文章目录 前言参考链接依赖库及版本Demo效果接口及数据展示各项模块Retrofit2Bean,对应上面的接口返回.Service API部分 Paging3PagingSource以及 RxPagingSourcePagingDataAdapter 适配器ViewModelPublicInfoPage /Activity 最后 前言 继续安卓学习之旅,本章的主要目标是: 1.完… 文章目录 前言参考链接依赖库及版本Demo效果接口及数据展示各项模块Retrofit2Bean,对应上面的接口返回.Service API部分 Paging3PagingSource以及 RxPagingSourcePagingDataAdapter 适配器ViewModelPublicInfoPage /Activity 最后 前言 继续安卓学习之旅,本章的主要目标是: 1.完成一个无限上拉加载的列表(Paging3 RecyclerView) 2.加载的是网络数据, 要采用主流的 Retrofitokhttp方式 3.在了解了RxJava之后,也希望用上Rxjava 4.用到ViewModel来配合,以及一些jetpack的东西都用上 (为什么不用Paging2? 这里主要是看说3比2还要方便些,所以就偷懒没去用Paging2)
参考链接 这些是在学习和搜索中看到的比较好的文章,不过他们要么是kotlin 要么是RxJava2,都不是能直接套上去就用的,但是从文章里面总结归纳,也是有借鉴效果的. SmartRefreshLayout-github 这个后期再结合Paging3,完成一个有酷炫下拉及淘宝二楼效果的的demoJava实现)使用官方Paging3分页库实现RecyclerView加载更多(loadmore)的功能 这个较为简洁,没那么多原理的描述,方便更实战的理解借鉴Android paging3 使用和踩坑经验分享 这个虽然是kotlin,不过里面一些名词的解释不错, 适合快速扫盲Jetpack新成员Paging3从吐槽到真香
依赖库及版本 为什么要说这个, 因为在实际百度各方面资料的时候,没仔细区分好版本,导致在练习过程中走了不少弯路,踩了坑.为避免这个情况,这里列出本Demo中的各个依赖库及版本 Retrofix2 // 引入 retrofix 网络框架(自带okhttp)// github :https://github.com/square/retrofit// 视频教学// https://www.bilibili.com/video/BV1vV411W75V?p4vd_source3dc64571e08f84008d5c43796c009480implementation com.squareup.retrofit2:retrofit:2.9.0implementation com.squareup.retrofit2:converter-gson:2.9.0implementation com.squareup.retrofit2:adapter-rxjava3:2.9.0Rxjava3 // 支持RxJava/RxAndroidimplementation io.reactivex.rxjava3:rxandroid:3.0.2implementation io.reactivex.rxjava3:rxjava:3.1.5Paging3 // 引入 paging 3// 注意, 由于上面我们用的是 retrofit2 rxjava3// 所以,在使用 paging3的时候, 要选 支持rxjava3的 paging-rxjava3// 切记版本对应好def paging_version 3.1.1implementation androidx.paging:paging-runtime:$paging_version
// // optional - RxJava2 support
// implementation androidx.paging:paging-rxjava2:$paging_version// optional - RxJava3 supportimplementation androidx.paging:paging-rxjava3:$paging_version这里稍微提一下, 如果用的是RxJava3, 就使用RxJava3 support的可选项, 不然不匹配,但同时也造成另一个问题, 这里插入说一下哈 就是 包括目前官网(点击进入)那边的, 关于对RxPagingSource的示例里面, 也应该用的还是RxJava2,如果你和我一样用RxJava3,那大概率在做map的时候,会报错说, 类型转换失败, 不能用 this::toLoadResult 这个稍后再说…
Demo效果 一个简单的demo 接口及数据展示 各项模块
Retrofit2
Bean,对应上面的接口返回.
Response_public_info_bean
package retrofit.bean;import java.util.List;/*** author: tiannan* time: 2023/4/12.* email: tianNanYiHao163.com* descripetion: 此处添加描述*/
public class Response_public_info_bean {private String msg;private String code;private Datas data;public String getMsg() {return msg;}public void setMsg(String msg) {this.msg msg;}public String getCode() {return code;}public void setCode(String code) {this.code code;}public Datas getData() {return data;}public void setData(Datas data) {this.data data;}public class Datas {private int pageSize;private ListCell list;public int getPageSize() {return pageSize;}public void setPageSize(int pageSize) {this.pageSize pageSize;}public ListCell getList() {return list;}public void setList(ListCell list) {this.list list;}Overridepublic String toString() {return Datas{ list list };}public class Cell {private String productName;private String productTypeName;private String riskRateName;private int id;private int pageNum; // 增加两个下标 page页下标private int indexNum;// 增加两个下标 newsInfo(cell)页下标Overridepublic String toString() {return News{ productName productName \ , productTypeName productTypeName \ , riskRateName riskRateName \ , id id , pageNum pageNum , indexNum indexNum };}public String getProductName() {return productName;}public void setProductName(String productName) {this.productName productName;}public String getProductTypeName() {return productTypeName;}public void setProductTypeName(String productTypeName) {this.productTypeName productTypeName;}public String getRiskRateName() {return riskRateName;}public void setRiskRateName(String riskRateName) {this.riskRateName riskRateName;}public int getId() {return id;}public void setId(int id) {this.id id;}public int getPageNum() {return pageNum;}public void setPageNum(int pageNum) {this.pageNum pageNum;}public int getIndexNum() {return indexNum;}public void setIndexNum(int indexNum) {this.indexNum indexNum;}}}
}
Service API部分 这里不展开说太多 Retrofit2部分的东西,这里只贴一下和本章有关的部分代码 /*** 请求公募数据列表* param map* return*/GET(App_Url.admin_getPublicProductInfoPageList)SingleResponse_public_info_bean admin_getPublicProductInfoPageList(QueryMap HashMapString,String map);以上部分完成后 ,能够通过RxJava3 Retrofit2 配合完成一次网络请求, 基本就算完成Demo一半功能了
Paging3
PagingSource以及 RxPagingSource 这里一开始我用错了RxPagingSource的导入版本, 用成了rxjava2的,踩了些坑 当时导入了paging.rxjava2这个版本, 主要原因还是在配置依赖的时候, 把paging-rxjava2:3.1.1版本也同步了 版本问题注意好 回到 RxPagingSource 按照网上的文章的示例, 先处理 loadSingle()函数的实现,这里有坑就是上面说的, RxJava3 Paging3的情况下, 在 return网络请求的时候,会报错 我这里没有在详细探究是否由于RxJava2的原因导致不能用 ::这种双冒号的写法 这里仅仅贴一下RxJava2下的map 和 RxJava3下的map的源码区别 RxJava2版本: RxJava3版本: 确实有一点区别, 这个先放一放, 等后期有空再看怎么处理…
先直接看怎么去写这个 this::toLoadResult 首先,既然通过Retrofit2,我们已经定义了网络请求的返回值 那么我们在RxPagingSource的 loadSingle()中, 会去调用网络请求,得到一个 SingleResponse_public_info_bean 我们再看官网例子的这部分代码 注意看返回值其实是LoadResultInteger, User ,或者说,在本文章 我们要的返回值其实是 LoadResultInteger, Response_public_info_bean.Datas.Cell 所以.对于map操作符,在Rxjava3的下, 我们是可以自己去提供一个FunctionT,R,这里面T就是我们上面的Response_public_info_bean R就是Response_public_info_bean.Datas.Cell
所以代码就是 (这里要注意下prevkey, 和 nextKey的入参 , 要做好逻辑判断, 一开始我参考别人的代码, 在prevKey填的是null, 在nextKey填入的是nextPageKey1,结果导致加载页码瞬间冲到了几百页, 其实总page数量才不过十几页)
为了更加清晰明了的展示页码和条数下标, 我又添加了一个map操作符, 是给cell这个Bean数据再添加一下当前所属的页码 和 当前的下标 Function的入参依然是 Response_public_info_bean 返回也还是 Response_public_info_bean, 相当于我们就对Response_public_info_bean数据做了个数据加工
所以,基于RxJava3的map操作符这边就可以这样返回
PagingDataAdapter 适配器
public class PublicInfoAdapter extends PagingDataAdapterResponse_public_info_bean.Datas.Cell, PublicInfoAdapter.Holder {public PublicInfoAdapter(NotNull DiffUtil.ItemCallbackResponse_public_info_bean.Datas.Cell diffCallback) {super(diffCallback);}NonNullNotNullOverridepublic Holder onCreateViewHolder(NonNull NotNull ViewGroup parent, int viewType) {PublicInfoCellBinding publicInfoCellBinding PublicInfoCellBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);return new Holder(publicInfoCellBinding);}Overridepublic void onBindViewHolder(NonNull NotNull Holder holder, int position) {Response_public_info_bean.Datas.Cell cell getItem(position);Log.d(dfaddfsa, onBindViewHolder: cell.getPageNum() . cell.getIndexNum() --- cell.getProductName());holder.publicInfoCellBinding.textTitle.setText(cell.getProductName());holder.publicInfoCellBinding.underflag.setText(第cell.getPageNum() 页.第 cell.getIndexNum()条);}public class Holder extends RecyclerView.ViewHolder {/*** 给每个 Holder 实例 一个 viewBinding*/private PublicInfoCellBinding publicInfoCellBinding;public Holder(NonNull NotNull View itemView) {super(itemView);}public Holder(NonNull PublicInfoCellBinding publicInfoCellBinding){super(publicInfoCellBinding.getRoot());this.publicInfoCellBinding publicInfoCellBinding;}}
}
这里没太多可以说的,网上基本讲明白了, 我仅仅分享我遇到的一个问题 页面列表在加载完成后, 出现了一屏数据的重复渲染, 而且随着页面的滚动,该组数据的最后一条一直在渲染不同内容(但随着日志打印,数据都是正常输出的) 最后通过UI观察, 感觉最后一条数据随滚动而渲染,有点想是for循环没拦住的那种意思 就猜想,是不是 PagingDataAdapter 里面没处理好, 后来果然发现, PublicInfoCellBinding publicInfoCellBinding;一不小心写成了全局的,而不是给每个Holder一个PublicInfoCellBinding publicInfoCellBinding;, 最后修复下即可 这里还是由于对PagingDataAdapter的不够熟悉, 刚写着玩意儿,才出现的低级错误
ViewModel
vm部分,网上也大同小异 ,写demo过程中未出现过多的坎儿
public class PublicInfoViewModel extends ViewModel {// paging3 page对象PagerInteger, Response_public_info_bean.Datas.Cell pager;// paging3 数据源对象PublicInfoSource publicInfoSource;// rxjava3 的 obserable 可观察对象FlowablePagingDataResponse_public_info_bean.Datas.Cell pagingDataFlowable;public PublicInfoViewModel(Context context) {CoroutineScope viewModelScope ViewModelKt.getViewModelScope(this);publicInfoSource new PublicInfoSource();// Maximum size must be at least pageSize 2*prefetchDist, pageSize20, prefetchDist20, maxSize20/*** pageSize 每页多少个条目* prefetchDistance 预加载下一页的距离滑动到倒数第几个条目就加载下一页无缝加载可选默认值是pageSize* enablePlaceholders 是否启用条目占位当条目总数量确定的时候列表一次性展示所有条目* 但是没有数据在adapter的onBindViewHolder里面绑定数据时候是空数据判断是空数据展示对应的占位item可选默认开启* initialLoadSize 第一页加载条目数量 可选默认值是 3*pageSize 有时候需要第一页多点数据可用* maxSize : 定义列表最大数量可选默认值是Int.MAX_VALUE* jumpThreshold : 暂时还不知道用法从文档注释上看是滚动大距离导致加载失效的阈值可选默认值是Int.MIN_VALUE 表示禁用此功能**/PagingConfig pagingConfig new PagingConfig(20,1,false,20*3);pager new PagerInteger, Response_public_info_bean.Datas.Cell(pagingConfig, () - publicInfoSource);pagingDataFlowable PagingRx.getFlowable(pager);PagingRx.cachedIn(pagingDataFlowable, viewModelScope);}public FlowablePagingDataResponse_public_info_bean.Datas.Cell getPagingDataFlowable() {return pagingDataFlowable;}
}
PublicInfoPage /Activity
这里要注意的是, setLayoutManager要设置 否则啥也不展示 public class PublicInfoPage extends AppCompatActivity {ActivityNewsPageBinding newsPageBinding;PublicInfoViewModel newsViewModel;PublicInfoAdapter newsAdapter;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);newsPageBinding ActivityNewsPageBinding.inflate(getLayoutInflater());newsAdapter new PublicInfoAdapter(new DiffUtil.ItemCallbackResponse_public_info_bean.Datas.Cell() {Overridepublic boolean areItemsTheSame(NonNull NotNull Response_public_info_bean.Datas.Cell oldItem, NonNull NotNull Response_public_info_bean.Datas.Cell newItem) {return oldItem.getId() newItem.getId();}Overridepublic boolean areContentsTheSame(NonNull NotNull Response_public_info_bean.Datas.Cell oldItem, NonNull NotNull Response_public_info_bean.Datas.Cell newItem) {return oldItem.getProductName().equals(newItem.getProductName()) oldItem.getProductTypeName().equals(newItem.getProductTypeName());}});newsPageBinding.recicleView.setAdapter(newsAdapter);newsPageBinding.recicleView.setLayoutManager(new LinearLayoutManager(this));setContentView(newsPageBinding.getRoot());}Overrideprotected void onResume() {super.onResume();newsViewModel new PublicInfoViewModel(this);newsViewModel.getPagingDataFlowable().subscribe(new ConsumerPagingDataResponse_public_info_bean.Datas.Cell() {Overridepublic void accept(PagingDataResponse_public_info_bean.Datas.Cell newsPagingData) throws Throwable {newsAdapter.submitData(getLifecycle(), newsPagingData);}});}
}最后
以上就是这样了. SmartRefreshLayout 也可以结合Paging3 这个有空也看一下, 安卓的玩法确实和iOS不一样, 也和RN不一样, 但互相又看得到对方的影子