SwipeRefreshLayout的下拉刷新、上拉加载
前言
- 这次在小项目中用到了下拉刷新、上拉加载,这次就记录一下
- 博主小白一枚,正在努力进阶,如有错误,欢迎指正!
上拉加载
- 以下操作是在 Adapter 中,实现 Adapter 就不多说了,如果还不熟悉,建议先熟悉再做这个
1. 明确子项 Item 的类型,定义几个 int 类型的常量作为 Item 类型
- 作用:作为不同类型的 Item 的标识符,通过判断来加载对应的布局
- 使用在两个地方
- 你的 Adapter 中的
onCreateViewHolder()
方法中
- 重写的
getItemType()
方法中
private static final int TYPE_BANNER = 0; //第一个Banner布局
private static final int TYPE_FUNCTION = 1; //第二个功能表布局
private static final int TYPE_SELLERS = 2; //第三个热销榜布局
private static final int TYPE_FRUIT = 3; //第四个水果Item布局
private static final int TYPE_FOOTER = 4; //第五个底部加载布局
2. 重写 getItemViewType() 方法
// 根据你想要的放置的 item 位置来返回不同的类型
@Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_BANNER;
}else if (position == 1) {
return TYPE_FUNCTION;
}else if (position == 2) {
return TYPE_SELLERS;
}else if (position + 1 == getItemCount()) {
return TYPE_FOOTER;
}else {
return TYPE_FRUIT;
}
}
3. 根据 Item 的类型,来定义对应的 ViewHolder
- 如果只是要展示出来就不需要进行 实例的获取了
- 如果后续操作需要实例,那么就在 ViewHolder 的构造方法中获取
// 后续操作需要实例的
class BannerViewHolder extends RecyclerView.ViewHolder {
ViewPager viewPager;
LinearLayout linearLayout;
public BannerViewHolder(@NonNull View itemView) {
super(itemView);
viewPager = itemView.findViewById(R.id.home_view_pager);
linearLayout = itemView.findViewById(R.id.home_ll_indicator);
}
}
//后续操作不需要实例的
class FunctionViewHolder extends RecyclerView.ViewHolder {
public FunctionViewHolder(@NonNull View itemView) {
super(itemView);
}
}
4. 在 onCreateViewHolder() 方法中进行判断 item 类型,来加载不同的布局
- 注意,上拉加载
- 方法中的第二个参数 i 就是
getItemViewType()
方法中返回的
// 根据不同的类型,加载对应的 View,并返回对应的 ViewHolder
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
if (i == TYPE_BANNER) {
View view = LayoutInflater.from(context).inflate(R.layout.fragment_home_banner,
viewGroup,false);
return new BannerViewHolder(view);
}else if (i == TYPE_FUNCTION) {
View view = LayoutInflater.from(context).inflate(R.layout.fragment_home_function
viewGroup,false);
return new FunctionViewHolder(view);
}
···
}else if (i == TYPE_FOOTER) {
View view = LayoutInflater.from(context).inflate(R.layout.fragment_home_footer,
viewGroup,false);
return new FooterViewHolder(view);
}
//当 else if 语句的条件都不满足的时候,该方法就无返回值了,所以这里加一个返回值
//不过应该是不会用到的
return null;
}
5. 定义滑动变量、滑动状态常量,并提供相应的 set 方法
//滑动状态常量
public final int STATE_LOADING = 0;
public final int STATE_FINISH = 1;
//滑动状态变量
private int state = STATE_LOADING;
//相应的改变滑动状态的 set方法
public void setLoadState(int state) {
this.state = state;
}
6. 在 onBindViewHolder() 方法中根据不同的 ViewHolder 来对相应的布局实例进行操作
- 这里注意一点,我们在使用 RecyclerView 的时候,对于布局的具体加载,也就是对布局中的实例进行的一系列操作,应该放在
onBindViewHolder()
方法中
其实对于上拉加载这个功能来说,这一步就是上拉加载 UI 的具体展示
- 在下面的代码在匹配到 FooterViewHolder 中进行了判断当前的状态,这样给用户 UI 呈现
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
// 此处省略部分代码,下面的是重点
···
}else if (viewHolder instanceof FooterViewHolder) {
//根据当前的滑动状态,对应的是实现 进度条的显示、隐藏等
FooterViewHolder footerViewHolder = (FooterViewHolder)viewHolder;
switch (state) {
case STATE_LOADING:
footerViewHolder.progressBar.setVisibility(View.VISIBLE);
footerViewHolder.textView.setText("正在加载...");
break;
case STATE_FINISH:
footerViewHolder.progressBar.setVisibility(View.GONE);
footerViewHolder.textView.setText("我也是有底线的哦~");
default:
break;
}
}
}
- 好啦,Adapter 的工作已经作完啦,接下来就是活动、碎片的工作吧!
7. 关于加载的数据
- 我认为,上拉加载最主要的就是 分页思想
- 加载加载大量的数据时,不需要一次全部加载完毕,而是首先加载用户可见的部分(及手机屏幕)
- 而在用户进行上拉的时候,在加载后面的数据
- 分页思想就需要我们将数据处理好了
需要进行处理的内容
- 控制数据的加载
- 我想的就是没加载一次就请求一部分数据,然后放入一个集合中,就可以进行加载了,当全部的数据都请求完毕的时候,就不再请求,而是要通知 UI 进行更新提示数据一加载完毕了
//下面使用的是本地的数据,也控制了只能加载两次,超过两次将不再进行加载
private void getData() {
if (loadCount <= 2){
for (int i = 0; i < 2; i++) {
urlList.add(String.valueOf(i));
}
Log.d("TAG","getData执行");
//加载次数加一
loadCount++;
}
}
8. 最后一步,对 RecyclerView 的滑动监听进行处理
- RecyclerView 的
addOnScrollListener()
方法用于给 RecyclerView 添加一个滑动监听器,也可以实现 RecyclerView.OnScrollListener
这个接口来添加监听
- 添加监听会重写
onScrollStateChanged()
、onScrolled()
这两个方法,前者在滑动状态改变的时候会被调用,后者在滑动完成后会被调用
-
onScrollStateChanged()
方法的滑动状态有三个状态:
- RecyclerView.SCROLL_STATE_IDLE:屏幕停止滚动
- RecyclerView.SCROLL_STATE_DRAGGING:屏幕在滚动
- RecyclerView.SCROLL_STATE_SETTLING:屏幕自动滚动,手指放开了
- 判断是否是当前子项的最后一个,即是否需要进行加载数据
- 总 item 数量 - 1 == 最后一个 Item 的位置
- 总 item 数量是通过 getItemCount()得到的
- 上面的判断成立就开始请求数据,请求完毕后,调用
notifyDataSetChanged()
方法通知 Adapter 更新数据
- 如果多次请求之后已经请求完毕了,这时,就调用我们在 Adapter 中定义的改变滑动状态的方法了,设置已经加载完毕的状态,这个时候,因为我们在
onBindViewHolder()
中进行了判断的,所以底部的布局就会改变啦
- 注意下面使用了 Hanlder 发送了一个延迟消息,目的就是让底部布局的加载状态能够保留一段时间,这样 UI 效果会好一些
//使用的是本地数据
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
//下面的manger是RecyclerView的主布局线性布局的实例,通过new出来的,需要传入几个参数
int lastItemPosition = manager.findLastCompletelyVisibleItemPosition();
int itemCount = manager.getItemCount();
if (itemCount - 1 == lastItemPosition) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.d("TAG","onSrcolled()方法执行");
getData();
if (loadCount <= 2) {
adapter.setLoadState(adapter.STATE_LOADING);
}else {
adapter.setLoadState(adapter.STATE_FINISH);
}
adapter.notifyDataSetChanged();
}
},1000);
}
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView,dx,dy);
}
});
下拉刷新
- 这个比较简单,使用的是 谷歌官方的 SwipeRefreshLayout,是design库下的
- 注意一点:
.setRefreshing();
方法并不会调用 onRefresh()
方法
- 关于自动刷新,可以使用 Hanlder 发送一条空的延迟消息,然后进行操作,注意上面所说的
//这是项目中的代码,不要这么死板,看懂就行啦
private void initSwipeRefresh() {
refreshLayout = viewHome.findViewById(R.id.home_swipe_refresh);
//设置 加载的那个圈圈的颜色,最多四种,这个颜色是依次加载的
refreshLayout.setColorSchemeResources(android.R.color.holo_blue_light,
android.R.color.holo_red_light, android.R.color.holo_orange_light,
android.R.color.holo_green_light);
//设置刷新监听
refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
//这个方法就是下拉刷新的时候会触发的,清楚原有数据,重新获取数据,通知Adapter更新数据
urlList.clear();
loadCount = 0;
getData();
adapter.notifyDataSetChanged();
refreshLayout.setRefreshing(false);
}
});
}