有话要说:

这次主要讲述主页面下拉刷新和上拉加载功能的实现。

主要是使用了SwipeRefreshLayout的布局方式,并在此基础上通过RecyclerView的特性增加了上拉加载的功能。

成果:

实现方式:

页面布局:

 <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/swipeRefreshView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:background="#f0f0f0"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>

通过SwipeRefreshLayout来实现下拉刷新功能。

下拉刷新:

       SwipeRefreshLayout swipeRefreshLayout;
     // 下拉刷新控件
swipeRefreshLayout = getView().findViewById(R.id.swipeRefreshView);
// 设置下拉控件背景色
swipeRefreshLayout.setProgressBackgroundColorSchemeColor(Color.WHITE);
// 设置下来控件主色
swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent);
// 设置下拉刷新事件
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String url = baseUrl + (++currentPage);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
try {
Response response = client.newCall(request).execute();
String json = response.body().string();
if (json != null) {
Gson gson = new Gson();
List<NewsBean> newDatas = gson.fromJson(json, new TypeToken<List<NewsBean>>(){}.getType());
if (newsBeans != null && newsBeans.size() > 0) {
newsBeans.addAll(0, newDatas);
}
}
Message message = new Message();
message.what = UPDATE_NEWS;
handler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
});

通过setProgressBackgroundColorSchemeColor来设置下拉控件的背景色,也就是圈圈的主体颜色。

通过setColorSchemeResources来设置下拉控件中间的线条颜色。

通过setOnRefreshListener来定义下拉刷新事件。

然后通过currentPage来实现下拉刷新之后获取的数据是下一页的数据,再添加到集合开头。

 private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case QUERY_NEWS:
recyclerView.getAdapter().notifyDataSetChanged();
break;
case UPDATE_NEWS:
recyclerView.getAdapter().notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
break;
case LOAD_MORE:
((NewsAdapter)recyclerView.getAdapter()).changeStatus(NewsAdapter.UNLOADING);
currentState = NewsAdapter.UNLOADING;
break;
}
}
};

刷新完成之后,通过notifyDataSetChanged告诉RecyclerView数据改变了,进而更改页面显示。通过setRefreshing来控制下拉刷新控件的显示。

由此,完成了下拉刷新的实现。

上拉加载:

由于SwipeRefreshLayout并不提供上拉加载的功能,于是准备利用RecyclerView灵活的特性来实现上拉加载功能。

 package com.example.lanxingren.imitating9gag.adapter;

 import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView; import com.example.lanxingren.imitating9gag.R;
import com.example.lanxingren.imitating9gag.bean.NewsBean;
import com.example.lanxingren.imitating9gag.util.GlideApp; import java.util.List; public class NewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<NewsBean> myNewsList;
private Context myContext; // 是否加载
public static final int LOADING = 1;
public static final int UNLOADING = 2; private int mStatus = UNLOADING;// 当前加载状态 // item的viewType
private final int ITEM = 1;
private final int FOOTER = 2; static class NewsHolder extends RecyclerView.ViewHolder {
CardView cardView;
TextView titleView;
ImageView imageView;
TextView pointView;
ImageView likeImageView;
ImageView unlikeImageView; private NewsHolder (View view) {
super(view);
cardView = (CardView) view;
titleView = view.findViewById(R.id.item_title);
imageView = view.findViewById(R.id.item_image);
pointView = view.findViewById(R.id.item_point);
likeImageView = view.findViewById(R.id.item_like);
unlikeImageView = view.findViewById(R.id.item_unlike);
}
} static class FooterHolder extends RecyclerView.ViewHolder {
CardView cardView;
private FooterHolder (View view) {
super(view);
cardView = view.findViewById(R.id.cardView_footer);
}
} public NewsAdapter (List<NewsBean> newsList) {
this.myNewsList = newsList;
} @Override
public int getItemCount() {
int count = 0;
if (myNewsList != null) {
count = myNewsList.size() + 1;
}
return count;
} @NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (myContext == null) {
myContext = parent.getContext();
} if (viewType == ITEM) {
View view = LayoutInflater.from(myContext).inflate(R.layout.item_news, parent, false);
return new NewsHolder(view);
} else {
View view = LayoutInflater.from(myContext).inflate(R.layout.item_footer, parent, false);
return new FooterHolder(view);
}
} @Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof NewsHolder) {
final NewsHolder newsHolder = (NewsHolder)holder;
final NewsBean newsBean = myNewsList.get(position); // 设置标题
String title = "9GAG#" + newsBean.getId();
if (newsBean.getTitle() != null && newsBean.getTitle().length() > 0) {
title = newsBean.getTitle();
}
newsHolder.titleView.setText(title); // 屏幕宽度
int screenWidth = myContext.getResources()
.getDisplayMetrics()
.widthPixels;
// 屏幕高度
int screenHeight = myContext.getResources()
.getDisplayMetrics()
.heightPixels; // 设置图片,不知道为什么override这样设置就可以让图片正正好显示,有时间研究一下?
if (newsBean.getUrls() != null && newsBean.getUrls().size() > 0) {
GlideApp.with(myContext)
.load(newsBean.getUrls().get(0))
.override(screenWidth, screenHeight)
.into(newsHolder.imageView);
} // 设置点赞数
String point = Integer.toString(newsBean.getLike() - newsBean.getUnlike());
newsHolder.pointView.setText(point); // 设置点赞图标显示
int accentColor = myContext.getResources().getColor(R.color.colorAccent);
int defaultColor = myContext.getResources().getColor(R.color.defaultColor);
switch (newsBean.getIsLiked()) {
case -1:
newsHolder.likeImageView.setColorFilter(defaultColor);
newsHolder.unlikeImageView.setColorFilter(accentColor);
break;
case 0:
newsHolder.likeImageView.setColorFilter(defaultColor);
newsHolder.unlikeImageView.setColorFilter(defaultColor);
break;
case 1:
newsHolder.likeImageView.setColorFilter(accentColor);
newsHolder.unlikeImageView.setColorFilter(defaultColor);
break;
} // 初始化监听事件
newsHolder.likeImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
newsBean.setIsLiked(1);
notifyDataSetChanged();
}
});
newsHolder.unlikeImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
newsBean.setIsLiked(-1);
notifyDataSetChanged();
}
}); } else if (holder instanceof FooterHolder) {
switch (mStatus) {
case UNLOADING:
((FooterHolder) holder).cardView.setVisibility(View.GONE);
break;
case LOADING:
((FooterHolder) holder).cardView.setVisibility(View.VISIBLE);
break;
}
}
} @Override
public int getItemViewType(int position) {
if (position + 1 == getItemCount()) {
return FOOTER;
} else {
return ITEM;
}
} public void changeStatus(int status) {
this.mStatus = status;
notifyDataSetChanged();
}
}

在适配器中定义了两个布局,一个是普通布局,一个是尾布局(footer)。

下面是footer的具体布局:

 <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="10dp"
android:layout_marginBottom="0dp"
app:cardCornerRadius="0dp"
android:elevation="0dp"
android:id="@+id/cardView_footer">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
/>
</android.support.v7.widget.CardView>

注意点:

  1. 通过mStatus来判断尾布局是否展示
  2. ITEM代表的是普通布局,即段子的布局;FOOTER代表的是尾布局
  3. 由于增加了一个item,故getItemCount得在原先的基础上加上一
  4. 在onCreateViewHolder中通过viewType来创建不同的viewHolder
  5. 实现getItemViewType方法,根据position的值来确定viewType
  6. 在onBindViewHolder中先判断viewHolder的类型,如果是尾布局(footer)的话,再根据mStatus来判断是否展示
  7. changeStatus主要是给外面用的,通过该方法可以控制footer的显示

使用上拉加载:

 private void initLoadMoreListener() {
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(final RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState); // 获取当前可见的item位置
int lastVisiblePosition = 0;
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
lastVisiblePosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
} // 当前加载状态是UNLOADING && 当前可见的item位置是最后一条时
if (currentState == NewsAdapter.UNLOADING
&& lastVisiblePosition + 1 == recyclerView.getAdapter().getItemCount()) {
// 改变footer的可见性
((NewsAdapter)recyclerView.getAdapter()).changeStatus(NewsAdapter.LOADING);
currentState = NewsAdapter.LOADING; new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} String url = baseUrl + (++currentPage);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
try {
Response response = client.newCall(request).execute();
String json = response.body().string();
if (json != null) {
Gson gson = new Gson();
List<NewsBean> newDatas = gson.fromJson(json, new TypeToken<List<NewsBean>>(){}.getType());
if (newsBeans != null && newsBeans.size() > 0) {
newsBeans.addAll(newsBeans.size(), newDatas);
}
}
Message message = new Message();
message.what = LOAD_MORE;
handler.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
});
}

通过给recyclerView加上滚动事件来实现下拉加载功能,具体逻辑如下:

获取页面可见最下面的item位置

→判断当前尾布局的加载状态

→如果尾布局现在的状态是UNLOADING && item位置为最后一个,则开线程加载数据

→将尾布局展示,即开始转圈动画

→请求网络,获取数据,放入数据集

→根据handler的处理,告诉RecyclerView数据改变,然后将footer隐藏

通过以上的过程就可以实现上拉加载的效果。

参考:

SwipeRefreshLayout详解和自定义上拉加载更多SwipeRefreshLayout + RecyclerView 实现 上拉刷新 和 下拉刷新

结束语:

本次学习了Android的下拉刷新以及上拉加载的实现,对RecyclerView有了进一步的了解。

接下来准备实现点赞功能以及GIF的暂停功能。

大家如果有什么疑问或者建议可以通过评论或者邮件的方式联系我,欢迎大家的评论~

仿9GAG制作过程(四)的更多相关文章

  1. 仿9GAG制作过程(一)

    有话要说: 准备开始学习Android应用程序的一个完整的设计过程.准备做一个仿9GAG的APP,前端界面设计+后台数据爬虫+后台接口设计,整个流程体验一遍.今天准备先把前端界面的框架给完成了. 成果 ...

  2. 仿9GAG制作过程(五)

    有话要说: 在做完了数据展示功能之后,就想着完善整个APP.发现现在后台非常的混乱,有好多点都不具备,比方说:图片应该有略缩图和原图,段子.评论.点赞应该联动起来,段子应该有创建时间等. 于是就重新设 ...

  3. 仿9GAG制作过程(三)

    有话要说: 这次准备讲述后台服务器的搭建以及前台访问到数据的过程. 成果: 准备: 安装了eclipse 安装了Tomcat7 安装了数据库管理工具:Navicat 搭建服务器: 用eclipse直接 ...

  4. 仿9GAG制作过程(二)

    有话要说: 这次准备讲述用python爬虫以及将爬来的数据存到MySQL数据库的过程,爬的是煎蛋网的无聊图. 成果: 准备: 下载了python3.7并配置好了环境变量 下载了PyCharm作为开发p ...

  5. [PCB制作] 1、记录一个简单的电路板的制作过程——四线二项步进电机驱动模块(L6219)

    前言 现在,很多人手上都有一两个电子设备,但是却很少有人清楚其中比较关键的部分(PCB电路板)是如何制作出来的.我虽然懂点硬件,但是之前设计的简单系统都是自己在万能板上用导线自己焊接的(如下图左),复 ...

  6. Android实训案例(九)——答题系统的思绪,自己设计一个题库的体验,一个思路清晰的答题软件制作过程

    Android实训案例(九)--答题系统的思绪,自己设计一个题库的体验,一个思路清晰的答题软件制作过程 项目也是偷师的,决心研究一下数据库.所以写的还是很详细的,各位看官,耐着性子看完,实现结果不重要 ...

  7. BabyLinux制作过程详解

    转:http://www.360doc.com/content/05/0915/14/1429_12641.shtml BabyLinux制作过程详解 作者:GuCuiwen email:win2li ...

  8. Android 仿PhotoShop调色板应用(四) 不同区域颜色选择的颜色生成响应

    版权声明:本文为博主原创文章,未经博主允许不得转载.  Android 仿PhotoShop调色板应用(四) 不同区域颜色选择的颜色生成响应  上一篇讲过了主体界面的绘制,这里讲解调色板应用中的另外一 ...

  9. rpt水晶报表制作过程

    原文:rpt水晶报表制作过程 最近公司安排一个以前的项目,里面需要用到水晶报表,由于原来做这个项目的同事离职,所在公司的同事报表做成了rdlc类型的,而这类报表在加载的时候很难动态的从数据库加载数据, ...

随机推荐

  1. Java面试大纲-java面试该做哪些准备,java开发达到这样的水平可以涨工资

    Java培训结束,面临的就是毕业找工作.在找工作时,就要针对性地做充分的面试准备.准备不充分的面试,完全是浪费时间,更是对自己的不负责. 上海尚学堂Java培训整理出Java面试大纲,其中大部分都是面 ...

  2. [Swift]LeetCode273. 整数转换英文表示 | Integer to English Words

    Convert a non-negative integer to its english words representation. Given input is guaranteed to be ...

  3. [Swift]LeetCode739. 每日温度 | Daily Temperatures

    Given a list of daily temperatures T, return a list such that, for each day in the input, tells you ...

  4. [Swift]LeetCode977. 有序数组的平方 | Squares of a Sorted Array

    Given an array of integers A sorted in non-decreasing order, return an array of the squares of each ...

  5. Redis 设计与实现 (九)--Lua

    EVAL script numkeys key [key ...] arg [arg ...] script:     你的lua脚本 numkeys:  key的个数 key:           ...

  6. 并发编程(十六)——java7 深入并发包 ConcurrentHashMap 源码解析

    以前写过介绍HashMap的文章,文中提到过HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容 ...

  7. SpringBoot入门教程(十六)@Autowired、@Inject、@Resource

    @Resource,@Autowired,@Inject 这3种都是用来注入bean的,它们属于不同的程序中.详情参见下表: v区别 ANNOTATION PACKAGE SOURCE 作用域 实现方 ...

  8. StackExchange.Redis .net core Timeout performing 超时问题

    最近在做的一个项目,用的.net core 2.1,然后缓存用的Redis,缓存相关封装是同事写的,用的驱动是StackExchange.Redis version 2.0.571 ,一直听说这个驱动 ...

  9. 【Java基础】【26网络编程】

    26.01_网络编程(网络编程概述)(了解) A:计算机网络 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下, ...

  10. 关于HTML相关知识随记

    HTML是构成网页文档的主要语言,它由HTML标签和字符信息组成.HTML标签可以标识文字.图形.动画.声音.表格.超链接等网页对象,字符信息用以传达网页内容,如标题.段落文本.图像等. HTML4文 ...