1.效果预览

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

  

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

1.2.整体结构

  首先底层的是BaseFragment

  然后RefreshRecyclerFragment继承了BaseFragment

  然后SimpleRefreshRecycleFragment继承了RefreshRecyclerFragment

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

2.最基础的BaseFragment

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

  

  ViewHolder用作View管理

  Config用作配置状态信息

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

  DataCache是一个缓冲器

2.2.onCreate函数,完成初始化

  

  Config是获取单例。

  Diycode也是获取单例。 

  DataCache是新建一个类。

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

  

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

2.4.获得本类中的ViewHolder

  

2.5.实现onCreateView

  

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

  然后返回的是一个View。

2.6.实现抽象函数initViews

  

2.7.复写onActivityCreated

  

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

2.8.提示函数toast

  

2.9.来一张Fragment的生命周期

  

  所以这里的执行顺序是:

  onCreate==>onCreateView==>onActivityCreate

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

  onCreateView作用:得到ViewHolder。

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

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

3.1.继承方式

  

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

  

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

  

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

3.2.请求状态

  

3.3.当前状态

  

3.4.分页加载

  

3.5.视图

  

  SwipeRefreshLayout==>旋转的加载圈

  RecyclerView==>ListView类型的,列表

3.6.状态

  

3.7.适配器

  

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

  FooterProvider:底部的内容提供器

  

3.8.实现BaseFragment的getLayoutId方法

  

3.9.fragment_refresh_recycler.xml

  

  实际上是这个东西

  

3.10.初始化视图  

  

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

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

  loadMore执行了什么呢?

  

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

  确定了请求的数据。

  将请求类型封装成mPostTypes

  然后页码+1

  然后状态确定为正在加载

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

  

  回到initViews中

  

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

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

  

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

  

  再次回到initViews中继续

  

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

  setProgressViewOffset来设置下拉刷新的高度

  setColorSchemeColors设置加载圈的颜色

  

  RecyclerView是一个列表。

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

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

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

  

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

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

  

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

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

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

  再次回到initViews中

  

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

  refersh()方法

  

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

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

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

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

  再次回到initViews中

  

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

  这个函数是一个抽象函数

  

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

3.11.请求结果的回调

  

  得到请求数据的UUID

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

  

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

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

  

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

  具体交给子类去实现。

  

  如果请求的数据时刷新

  

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

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

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

  

  传入两个产生event和adapter

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

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

  

  将状态设置为正常。

  然后判断请求类型

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

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

  这里面执行了一个 request函数

  

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

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

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

  

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

3.12.设置是否可以刷新

  

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

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

  

  仅仅设置成员变量即可。

3.14.快速回到顶部

  

  调用了RecyclerView的方法。

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

4.1.首先看一下继承方式

  这个Fragment是最高级的抽象类了。

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

  

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

  用法:如果一个话题类

  

  继承方法:

  TopicListFragment extends

            SimpleRefreshRecycleFragment<Topic,GetTopicsListEvent>

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

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

  

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

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

4.3.第二次刷新

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

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

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

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

  

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

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

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

4.4.第二次加载更多

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

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

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

  

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

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

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

4.5.第二次发生异常

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

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

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

  

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

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

  

 

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

  

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

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

  

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.首先看一下怎么定义的

  

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

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

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

  

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

  数据通过bundle传递。

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

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

  

  优先从缓存中获取数据

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

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

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

  答案是Config。

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

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

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

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

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

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

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

  

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

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

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

  

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

5.10.第三次onRefresh刷新

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

  第二次SimpleRefreshRecyclerFragment==>数据处理

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

  

5.11.第三次onLoadMore加载

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

  第二次SimpleRefreshRecyclerFragment==>数据处理

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

  

5.12.将这个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有一些不熟,之后再详细看看。

Diycode开源项目 搭建可以具有下拉刷新和上拉加载的Fragment的更多相关文章

  1. android--------自定义控件ListView实现下拉刷新和上拉加载

    开发项目过程中基本都会用到listView的下拉刷新和上滑加载更多,为了方便重写的ListView来实现下拉刷新,同时添加了上拉自动加载更多的功能. Android下拉刷新可以分为两种情况: 1.获取 ...

  2. 安卓开发笔记——关于开源组件PullToRefresh实现下拉刷新和上拉加载(一分钟搞定,超级简单)

    前言 以前在实现ListView下拉刷新和上拉加载数据的时候都是去继承原生的ListView重写它的一些方法,实现起来非常繁杂,需要我们自己去给ListView定制下拉刷新和上拉加载的布局文件,然后添 ...

  3. Android实现RecyclerView的下拉刷新和上拉载入很多其它

    需求 先上效果图, Material Design风格的下拉刷新和上拉载入很多其它. 源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2 假设对于 ...

  4. 下拉刷新和上拉加载 Swift

    转载自:http://iyiming.me/blog/2015/07/05/custom-refresh-and-loading/ 关于下拉刷新和上拉加载,项目中一直使用MJRefresh(原先还用过 ...

  5. H5下拉刷新和上拉加载实现原理浅析

    前言 在移动端H5网页中,下拉刷新和上拉加载更多数据的交互方式出现频率很高,开源社区也有很多类似的解决方案,如iscroll,pulltorefresh.js库等.下面是对这两种常见交互基本实现原理的 ...

  6. RecyclerView下拉刷新和上拉加载更多实现

    RecyclerView下拉刷新和上拉加载更多实现 转 https://www.jianshu.com/p/4ea7c2d95ecf   在Android开发中,RecyclerView算是使用频率非 ...

  7. Vue移动端项目中下拉刷新和上拉加载

    Vue2.0中引入Mint-UI的下拉刷新和上拉加载.简单粗暴 安装Mint-UI npm i mint-ui -S 引入 打开项目的main.js入口文件,引入并使用.注意,为了方便,这里是全部引入 ...

  8. Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

    RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...

  9. iscroll.js 下拉刷新和上拉加载

    html代码如下 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

随机推荐

  1. wepy-cli 开发小程序如何使用vant组件

    同样使用wepy-cli快速生成的小程序,目前可以使用组件: 直接通过 git 下载 Vant Weapp 源代码,并将dist目录拷贝到自己的项目中 git clone https://github ...

  2. hibernate课程 初探单表映射2-5 session详解(上)

    1 本章目的:获得session的两种方式: openSession 和 getCurrentSession 2 两种session的使用方法 1openSession可以直接写,getCurrent ...

  3. JavaScript 创建对象的七种方式

    转自:xxxgitone.github.io/2017/06/10/JavaScript创建对象的七种方式/ JavaScript创建对象的方式有很多,通过Object构造函数或对象字面量的方式也可以 ...

  4. bt5 r3下metasploit连接postgresql数据库

    一.查看PostgreSQL使用的端口,默认为7337 #: netstat -tnpl |grep postgres 二.查看Msf配置,里面有默认的用户名和密码 默认配置文件:/opt/metas ...

  5. DataGridView控件使用大全

    转自:http://www.cnblogs.com/xiaofengfeng/archive/2011/04/16/2018504.html DataGridView控件 DataGridView是用 ...

  6. draggable与overflow同时存在,无法拖拽出父元素问题解决

    在使用jquery-ui的拖拽功能对列表内的选项拖拽时,发现无法将选项拖拽出列表的范围,一出范围就自动隐藏在列表下,查找到最后的原因是css中的overflow的原因,overflow存在则不能将选项 ...

  7. Android(java)学习笔记154:采用HttpClient提交数据(qq登录案例)

    1.Apache -Httpclient HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的.最新的.功能丰富的支持 HTTP 协议的客户端编程工具包 ...

  8. systemd初始化进程(转)

    Systemd初始化进程 Linux操作系统开机过程首先从BIOS开始→进入"Boot Loader"→加载内核→内核的初始化→启动初始化进程,初始化进程作为系统第一个进程,它需要 ...

  9. mongodb索引 全文索引

    全文索引,也叫文本索引,平时,我们百度的搜索,比如api文档的搜索,这种全局的索引就可以使用全文索引实现 全文索引:对字符串与字符串数组创建全文可搜索对索引 使用情况:比如有一个数据集合,存储了用户的 ...

  10. IPC Gateway 设计

    1. IPC Gateway对外提供的功能: IPC的register/request/reply/notification服务. 2. IPC Gatew的实现原理: 各个具体的服务注册自己的回调函 ...