首先看下原版知乎日报首页效果
我们需实现的效果有:
- 1.下拉刷新数据
- 2.滑动到底部加载下一页数据
- 3.随着列表页的滑动,头部的标题也发生变化.
好,有了需求,我们一步一步实现.重点是第三个效果.
1.首先,确定页面的布局
从上往下:toolBar
,下拉刷新控件swipeRefresh
,还是数据填充的列表页recycleView
.
笔者因为个人项目规划的原因,把toolbar放在了Mainactivity中.
所以布局的代码如下:
activity_main
fragment_zhihu_home
2.实现下拉刷新效果
这个最简单,布局分析的时候就已经说到了采用swipeRefresh
下拉加载控件.swipeRefresh
包裹recycleView
,实现OnRefreshListener()
方法:
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
presenter.getData(true);
}
});
3.实现滑动到底部加载下一页
其实也就是需要判断recycleview什么时候滑动到了底部.
在recycleview的滑动监听中去监听是否滑动到底部
recyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isSlideToBottom(recyclerView)) {
presenter.loadNext();
}
}
});
判断是否滑动到底部
public static boolean isSlideToBottom(RecyclerView recyclerView) {
if (recyclerView == null) return false;
if (recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
>= recyclerView.computeVerticalScrollRange())
return true;
return false;
}
computeVerticalScrollExtent()
表示显示区域的高度
computeVerticalScrollOffset()
表示已经向下滚动的距离,为0时表示已处于顶部。
computeVerticalScrollRange()
表示整体的高度,包括未显示的区域
判断recycleview是否滑动到底部的其他方法
4.随着列表页的滑动,头部的标题也发生变化.
本文的重点
说到这,可以分析下知乎日报的数据接口
这里用到了两个api:
1.最新消息api:
- URL: https://news-at.zhihu.com/api/4/news/latest
- 响应实例:
date: "20140523",
stories: [
{
title: "中国古代家具发展到今天有两个高峰,一个两宋一个明末(多图)",
ga_prefix: "052321",
images: [
"http://p1.zhimg.com/45/b9/45b9f057fc1957ed2c946814342c0f02.jpg"
],
type: 0,
id: 3930445
},
...
],
top_stories: [
{
title: "商场和很多人家里,竹制家具越来越多(多图)",
image: "http://p2.zhimg.com/9a/15/9a1570bb9e5fa53ae9fb9269a56ee019.jpg",
ga_prefix: "052315",
type: 0,
id: 3930883
},
...
]
}
- 分析:
date : 日期
stories : 当日新闻
title : 新闻标题
images : 图像地址(官方 API 使用数组形式。目前暂未有使用多张图片的情形出现,曾见无 images 属性的情况,请在使用中注意 )
ga_prefix : 供 Google Analytics 使用
type : 作用未知
id : url 与 share_url 中最后的数字(应为内容的 id)
multipic : 消息是否包含多张图片(仅出现在包含多图的新闻中)
top_stories : 界面顶部 ViewPager 滚动显示的显示内容(子项格式同上)(请注意区分此处的 image 属性与 stories 中的 images 属性)
2.过往消息
- URL: https://news-at.zhihu.com/api/4/news/before/20131119
若果需要查询 11 月 18 日的消息,before 后的数字应为 20131119
知乎日报的生日为 2013 年 5 月 19 日,若 before 后数字小于 20130520 ,只会接收到空消息
输入的今日之后的日期仍然获得今日内容,但是格式不同于最新消息的 JSON 格式
- 响应实例:
{
date: "20131118",
stories: [
{
title: "深夜食堂 · 我的张曼妮",
ga_prefix: "111822",
images: [
"http://p4.zhimg.com/7b/c8/7bc8ef5947b069513c51e4b9521b5c82.jpg"
],
type: 0,
id: 1747159
},
...
]
}
- 格式与最新消息的相同
recycleview的多布局显示:
我们在列表页需要显示的内容
1.top_stories[]:头部的banner
2.date: "20140523":每一页数据的头部日期显示
3.stories[]:新闻列表
public class ZhihuHomeAdapter extends RecyclerView.Adapter {
public static final int HEAD = 1;
private List zhihuBanberBeans;
private List
这里的List
是将日期date
和新闻实体放在一个集合中.getItemViewType
的时候只需判断Object是否是String或者是新闻实体类.
再看如何知道滑动到哪里开始改变头部标题?
思路分析:
记录下时间的item在列表中的哪些位置,当recycleview下滑到这个位置时就改变标题.而一旦上滑显示出了上一天的数据,就变换成上一天的时间.
- 时间的item在recycleview中的哪个位置
我们的分页是就是根据时间一天天来分页的,所以只需要在加载下一天数据的之前,当前recycleview的总item数,就是下一天时间的item.
List integers = new ArrayList<>();
@Override
public void getData(ZhiHuHomeBean zhiHuHomeBean, boolean isrefresh) {
zhihuHomeAdapter.setData(zhiHuHomeBean);
if (isrefresh) {
swipeRefresh.setRefreshing(false);
}
integers.clear();
integers.add(new DateBean(0, getResources().getString(R.string.app_zhihu_home)));
integers.add(new DateBean(1, getResources().getString(R.string.app_zhihu_today)));
}
@Override
public void loadNext(ZhiHuListBean zhiHuListBean) {
integers.add(new DateBean(zhihuHomeAdapter.getItemCount(), zhiHuListBean.getShowData()));
zhihuHomeAdapter.addData(zhiHuListBean);
}
- recycleview上滑和下滑时
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();
if (isSlideToBottom(recyclerView)) {
presenter.loadNext();
}
if (dy > 0) {
//下滑
scrollDown();
} else {
//上滑
scrollUp();
}
}
int nowPosition = -1;
private void scrollUp() {
if (nowPosition > -1&&integers.size()>nowPosition) {
if (firstVisibleItemPosition < integers.get(nowPosition).getPosition()) {
nowPosition--;
changeTitie(integers.get(nowPosition).getTitle());
}
}
}
private void scrollDown() {
if (integers.size()>nowPosition+1) {
if (integers.get(nowPosition+1).getPosition() <= firstVisibleItemPosition) {
nowPosition++;
changeTitie(integers.get(nowPosition).getTitle());
}
}
}
最后效果图:
项目地址