Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

时间:2023-03-09 21:51:50
Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

1.效果预览

1.1.这个首页就是一个Fragment碎片,本文讲述的就是这个碎片的搭建方式。

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  下拉会有一个旋转的刷新圈,上拉会刷新数据。

1.2.整体结构

  首先底层的是BaseFragment

  然后RefreshRecyclerFragment继承了BaseFragment

  然后SimpleRefreshRecycleFragment继承了RefreshRecyclerFragment

  所以应用类的话只要继承SimpleRefreshRecycleFragment就可以了。

2.最基础的BaseFragment

2.1.首先看一下有哪些成员变量

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  ViewHolder用作View管理

  Config用作配置状态信息

  Diycode是本项目的一个在线服务器

  DataCache是一个缓冲器

2.2.onCreate函数,完成初始化

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  Config是获取单例。

  Diycode也是获取单例。 

  DataCache是新建一个类。

2.3.定义获取布局id的抽象函数

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  继承者中就要实现这个函数了。

2.4.获得本类中的ViewHolder

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

2.5.实现onCreateView

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  新建的ViewHolder其实已经用了一个getLayoutId()方法,这里调用的是本类的抽象函数。

  然后返回的是一个View。

2.6.实现抽象函数initViews

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

2.7.复写onActivityCreated

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  实现了间接调用抽象函数。

2.8.提示函数toast

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

2.9.来一张Fragment的生命周期

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  所以这里的执行顺序是:

  onCreate==>onCreateView==>onActivityCreate

  onCreate作用:初始化配置和Diycode单例和数据缓存器新建。

  onCreateView作用:得到ViewHolder。

  onActivityCreate作用:实现一个抽象函数initViews,用来给基类来实现。

3.具有下拉刷新和上拉加载的Fragment

3.1.继承方式

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  名称:RefreshRecyclerFragment<T,Event extends BaseEvent<List<T>>>

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  一个类中有两个参数,像这样

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  所以本类构造函数也需要两个参数,一个是T,一个是继承BaseEvent<List<T>>的类

3.2.请求状态

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.3.当前状态

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.4.分页加载

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.5.视图

   Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  SwipeRefreshLayout==>旋转的加载圈

  RecyclerView==>ListView类型的,列表

3.6.状态

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.7.适配器

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  HeaderFooterAdapter:带有头部和底部的适配器

  FooterProvider:底部的内容提供器

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.8.实现BaseFragment的getLayoutId方法

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.9.fragment_refresh_recycler.xml

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  实际上是这个东西

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

3.10.初始化视图  

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  在BaseFragment中执行最后面的一个函数。

  如果第一次添加到Footer,就不执行loadMore()。

  loadMore执行了什么呢?

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  如果不能加载更多,或者当前状态为没有更多数据了。

  确定了请求的数据。

  将请求类型封装成mPostTypes

  然后页码+1

  然后状态确定为正在加载

  最后设置内容提供其的FooterLoading,就是底部的loading的用户提示。

  

  回到initViews中

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  设置Normal的效果==>--end--

  然后适配器注册Footer,Footer时一个空的Bean类

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  mFooterProvider刚才上面new的一个,写了一个needLoadMore方法,调用了本类中的loadMore方法。

  

  再次回到initViews中继续

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  refresh_layout就是那个android系统提供的加载圈SwipeRefreshLayout

  setProgressViewOffset来设置下拉刷新的高度

  setColorSchemeColors设置加载圈的颜色

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  RecyclerView是一个列表。

  setHasFixedSize的作用就是确保尺寸是通过用户输入从而确保RecyclerView的尺寸是一个常数。

  setAdapter将列表和适配器关联,适配器已经注册了Footer

  setLayoutManager这个对于RecyclerView将会提升很大作用

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的FragmentDiycode开源项目 搭建可以具有下拉刷新和上拉加载的FragmentDiycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  可以达到很多效果,这里就是RecyclerView强大的地方。

   上面有一个自定义的函数setAdapterRegister

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这是一个抽象函数,在initViews中执行了。在基类中也要具体的实现代码。

  这里需要传进去三个参数,一个是上下文,一个是recyclerView,一个是adapter,

  具体的实现方法,会调用adapter注册数据类型的方法,这里只是声明这个方法,耦合性降低了。

  再次回到initViews中

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  如果下拉了,将会执行刷新的方法。

  refersh()方法

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这个函数和在新建的FooterProvider中的needLoadMore中的loadMore方法很类似

  区别在于pageIndex这里是固定为0了

  然后这个的状态为刷新,常数定义为3

  然后没有mFooterProvider设置的setFooterLoading了,因为不涉及到。

  再次回到initViews中

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这个函数是initViews中最后一个函数

  这个函数是一个抽象函数

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  具体作用是从缓存加载数据。在子类中将实现这个方法。

3.11.请求结果的回调

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  得到请求数据的UUID

  如果是加载更多,就执行onLoadMore(event)

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这里判断是否请求得到的数据的长度小于当前请求的长度

  然后执行了一个抽象函数onLoadMore(event,mAdapter)

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  在回调函数中执行,就是数据得到了,下面要实现如何将数据完美展现出去。

  具体交给子类去实现。

  

  如果请求的数据时刷新

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  也是首先判断一下,因为加载更多涉及到mFooterProvider

  所以这里设置setFooterNormal(),就是正常的loading

  然后执行了一个抽象函数onLoadMore

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  传入两个产生event和adapter

  主要解决:如何将数据加入到adapter中。交给子类具体实现。

  如果没有得到数据将会执行onError方法

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  将状态设置为正常。

  然后判断请求类型

  如果为加载更多,设置mFooterProvider设置成==>失败,点击重试。

  设置的点击重试,将原来自增了的pageIndex减回去,然后在执行loadMore

  这里面执行了一个 request函数

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这个函数也是自己定义的抽象函数,具体实现交给子类吧。就是传递一个范围区间。

  如果是刷新失败,就将mRefreshLayout取消转圈,将底部设置为正常。

  最后执行onError(event,postType)方法

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  当加载更多失败和刷新数据失败的时候,用一个toast来提示用户。

3.12.设置是否可以刷新

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  SwipeRefreshLayout有这个setEnabled功能来设置是否可以刷新

3.13.设置是否可以加载更多

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  仅仅设置成员变量即可。

3.14.快速回到顶部

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  调用了RecyclerView的方法。

4.可以给被人用的刷新加载的Fragment

4.1.首先看一下继承方式

  这个Fragment是*的抽象类了。

  它是一个可以给被人用的刷新加载的Fragment。

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这里我想说一些这个Event是自己随意起的名字,就是一个继承BaseEvent<List<T>>的一个Object。

  用法:如果一个话题类

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  继承方法:

  TopicListFragment extends

            SimpleRefreshRecycleFragment<Topic,GetTopicsListEvent>

  GetTopicsListEvent==>extends BaseEvent<List<Topic>>

4.2.getRecyclerViewLayoutManager方法来设置快速返回顶部的功能

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  返回一个自定义类,传入上下文的参数。

  比较复杂,就先不管,理解作用即可。

4.3.第二次刷新

  为什么叫做第二次刷新呢?

  因为在RefreshRecycleFragment已经首先执行了onRefresh==>设置状态

  然后再执行一个抽象函数onRefresh,这里的函数就是实现的这个抽象函数

  第二次刷新主要是处理数据的。

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  刷新的话,要先清空一下数据

  然后将请求得到的数据,填充到适配器,采用addDatas的方式来讲数据加进去

  最后友好地提示一下用户:刷新成功。

4.4.第二次加载更多

  同理,在RefreshRecycleFragement已经首先执行了onLoadMore==>设置状态+是否到底了。

  然后再执行一个抽象函数onLoadMore,这里的函数就是实现的这个抽象函数

  第二次刷新主要是处理数据的。

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  加载跟多的话,调用方法addDatas即可。

  这样就会把新的数据加到adapter中。

  这里就没必要再提示用户了,不然每一页都会有这个烦人的提示了。

4.5.第二次发生异常

  同理,在请求数据失败的时候,在RefreshRecycleFragment会执行onError==>设置状态+点击事件。

  然后再执行一个抽象函数onError,这里的函数就是实现的这个抽象函数。

  第二次处理异常主要是为了提示用户的。

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

5.一个案例NewsListFragment继承上面的类

5.1.我们要使用的基类就是最高等级的抽象类SimpleRefreshRecyclerFragment

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

 

5.2.这里的T就是News,News是一个简单的Bean

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  这个Bean类SDK已经定义好了。

5.3.同理这里的Event也是SDK中定义的GetNewsListEvent

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

5.4.NewsListFragment的源代码如下

/*
* Copyright 2017 GcsSloop
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Last modified 2017-04-09 05:15:40
*
* GitHub: https://github.com/GcsSloop
* Website: http://www.gcssloop.com
* Weibo: http://weibo.com/GcsSloop
*/ package com.gcssloop.diycode.fragment; import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View; import com.gcssloop.diycode.fragment.base.SimpleRefreshRecyclerFragment;
import com.gcssloop.diycode.fragment.provider.NewsProvider;
import com.gcssloop.diycode_sdk.api.news.bean.New;
import com.gcssloop.diycode_sdk.api.news.event.GetNewsListEvent;
import com.gcssloop.diycode_sdk.log.Logger;
import com.gcssloop.recyclerview.adapter.multitype.HeaderFooterAdapter; import java.util.List; /**
* 首页 news 列表
*/
public class NewsListFragment extends SimpleRefreshRecyclerFragment<New, GetNewsListEvent> { private boolean isFirstLaunch = true; public static NewsListFragment newInstance() {
Bundle args = new Bundle();
NewsListFragment fragment = new NewsListFragment();
fragment.setArguments(args);
return fragment;
} @Override public void initData(HeaderFooterAdapter adapter) {
// 优先从缓存中获取数据,如果是第一次加载则恢复滚动位置,如果没有缓存则从网络加载
List<Object> news = mDataCache.getNewsListObj();
if (null != news && news.size() > 0) {
Logger.e("news : " + news.size());
pageIndex = mConfig.getNewsListPageIndex();
adapter.addDatas(news);
if (isFirstLaunch) {
int lastPosition = mConfig.getNewsListLastPosition();
mRecyclerView.getLayoutManager().scrollToPosition(lastPosition);
isFirstAddFooter = false;
isFirstLaunch = false;
}
} else {
loadMore();
}
} @Override protected void setAdapterRegister(Context context, RecyclerView recyclerView,
HeaderFooterAdapter adapter) {
adapter.register(New.class, new NewsProvider(getContext()));
} @NonNull @Override protected String request(int offset, int limit) {
return mDiycode.getNewsList(null, offset,limit);
} @Override protected void onRefresh(GetNewsListEvent event, HeaderFooterAdapter adapter) {
super.onRefresh(event, adapter);
mDataCache.saveNewsListObj(adapter.getDatas());
} @Override protected void onLoadMore(GetNewsListEvent event, HeaderFooterAdapter adapter) {
// TODO 排除重复数据
super.onLoadMore(event, adapter);
mDataCache.saveNewsListObj(adapter.getDatas());
} @Override public void onDestroyView() {
super.onDestroyView();
// 存储 PageIndex
mConfig.saveNewsListPageIndex(pageIndex);
// 存储 RecyclerView 滚动位置
View view = mRecyclerView.getLayoutManager().getChildAt(0);
int lastPosition = mRecyclerView.getLayoutManager().getPosition(view);
mConfig.saveNewsListPosition(lastPosition);
}
}

5.5.首先看一下怎么定义的

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  GetNewsListEvent是新闻的集合作为参数的一个BaseEvent类。

  这里设置是否是第一次加载。

5.6.外部如何调用这个碎片

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  外部只要调用了这个函数,即可加载news这个碎片。

  数据通过bundle传递。

5.7.实现RefreshRecyclerFragment中的最后的一个抽象函数initData

  作用:尝试读取缓存数据。

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  优先从缓存中获取数据

  如果是第一次加载则恢复混动位置

  如果没有缓存则从网络加载

  用什么方法记住当前看到了第几页呢?当前的位置呢?

  答案是Config。

  这是一个自定义的类,就是存放临时用来记录历史的。

  如何让recyclerView滑动到原来的位置呢?

  答案是:mRecyclerView.getLayoutManager().scrollToPosition(上次的位置)

  然后修改是否第一次添加Footer,是否第一次加载

  如果缓存中没有数据,就loadMore()

  loadMore()是定义在RefreshRecyclerFragment中的==>重新request数据

5.8.实现在RefreshRecycleFragment中定义的setAdapterRegister方法

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  将一个Bean类的参数+一个BaseViewProvider类型的参数 传进去即可。

  第一个参数,是SDK中定义的Bean,第二个参数是内容提供器(自定义)。以后再研究。

5.9.实现在RefreshRecycleFragment中定义的request方法

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

     传入两个范围参数,进行真实数据的请求。

5.10.第三次onRefresh刷新

  第一次RefreshRecyclerFragment==>状态的配置

  第二次SimpleRefreshRecyclerFragment==>数据处理

  第三次自定义某个应用类==>添加缓存

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

5.11.第三次onLoadMore加载

  第一次RefreshRecyclerFragment==>状态的配置

  第二次SimpleRefreshRecyclerFragment==>数据处理

  第三次自定义某个应用类==>添加缓存

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

5.12.将这个Fragment关闭时要执行的函数

  Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment

  每次退出APP,退出这个页面的时候,记录一下上次滑动的位置

  将这个位置存储到Config中,这是自定义的一个存储数据的类。

6.总结一下

6.1.这篇博客讲述了Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment,其实不只有这个项目要用到

  这种可以上拉刷新,下拉加载更多的Fragment,之前我做大学喵APP的时候也是用Fragment实现上拉刷新,

  下拉加载更多,我只会用一些第三方库来实现,而且这个第三方库局限性特别大,而且不好用,今后可以用这种

  方法,效果也好,虽然复杂点,但是原理搞懂了,什么都不怕了。

6.2.如何搭建这种Fragment呢?首先由一个BaseFragment,定义了最基本的三个方法,onCreate,onCreateView,

  onActivityCreated,主要的作用就是一些配置+缓存的初始化,除此之外两个抽象函数,必须在子类中实现,

  一个是getLayoutId获取布局id,就是这个碎片的布局,另外一个是initViews初始化视图,在onActivityCreated

  中执行了,可以说这个是最后一个在BaseFragment中执行的函数了。然后是一个用户提示的toast了。

6.3.最复杂的莫过于这个RefreshRecycleFragment,挺长的。具体实现了SwipeRefreshLayout+RecyclerView

  的初始化,将一个自定义的HeaderFooterAdapter(带有头部和尾部的一个适配器)+尾部的内容提供器发生

  必要的联系。然后定义刷新执行什么请求,加载执行什么请求,怎么修改状态,定义数据的回调,回调中

  刷新成功,加载成功,或者请求失败该怎么处理,部分定义了抽象函数,所以要在子类中实现。

6.4.然后再次继承者为SimpleRefreshRecyclerFragment,作用也非常明显,就是处理真实数据的,将调用适配器中

  的addDatas方法,将event中的数据放在适配器中。然后如果请求失败,也会toast友好提示用户。

6.5.真实的调用过程,首先是实现newInstance,基本都要实现的吧。为了方便调用这个碎片。然后就是最关键的

  初始化数据,作用相当明显,就是从缓存中获得数据,如果没有才去请求。然后记录一下位置,用Config来

  记录每个碎片的页码和位置。然后实现了一些抽象函数,setAdapterRegister函数是注入数据类型,里面

  有很大一坨东西,不过先不研究。然后是request这里进行细节的请求。然后第三次刷新和第三次加载来放入

  缓存,然后一个onDestroyView来处理关闭碎片后记录存储位置,放入Config。

6.6.总之,一个实现上拉刷新,下拉加载更多的碎片要实现这些东西,这里讲3个抽象类放在一起,然后Fragment

  只要修改一下本Fragment就行了,实现必要的刷新和加载更多的方法,耦合性比较低,所以可以直接用的。

  唯一的问题就是这里用到了EventBus,这个和SDK定义的一些Bean还扯上了关系,这里有一些耦合,另外还有

  HeadFooterAdapter和FooterProvider有一些不熟,之后再详细看看。