那如同这个题目,这里面涉及的东西其实还是比较多的,RecycleView SwipeRefreshLayout,下拉刷新(这个就是SwipeRefreshLayout的),加载更多。
SwipeRefreshLayout
这个是Google自己封装的一个下拉刷新的控件,里面使用了5.0开始的嵌套滑动机制,有兴趣的朋友可以去看看源码!使用起来其实就涉及到以下方法:
setOnRefreshListener()
下拉刷新的相关回调。
setRefresh()
通知是否开始刷新或者刷新完成。(坑1)
setColorSchemeColors()
loading的时候progressbar
的颜色,支持多个。
SwipeRefreshLayout的坑
进入页面调用setRefresh(true)
,根本不显示刷新的小圆圈?!
简单的说,这个就是在onCreate()
方法执行的时候,view还没有绘制出来,这个时候你设置刷新不刷新其实都一样的,解决方法,post一下!
mRecyclerView.post(new Runnable() {
@Override
public void run() {
mRefreshLayout.setRefreshing(refresh);
}
});
RecycleView
RecycleView
其实出现都有一定的年头了,前几天公司来面试的居然说他还没有用过。。这个也是醉醉的!
RecycleView
是ListView
的强力升级!加入了holder便于管理和复用相同的类型。
就我目前掌握的情况,RecycleView对于ListView有了以下的不同:
1、加入了LayoutManager
用用管理各种类型的布局,而且通过不同的布局可以实现横向、竖向、瀑布式的等各种复杂的布局。
2、加入Holder
来管理相关布局和复用,对于每一种Type的View你都要创建一个对应的Holder
来管理它!
3、取消了header和bottom布局。
4、没有现成的itemClick
回调。
5、引入了丰富的动画效果。(坑4)
6、添加了丰富的数据刷新的方法,可以局部刷新了!(坑3)
7、可自定义相关分割线。
8、支持swipe删除和drag排序。(ItemTouchHelper
帮助类)
9、默认是不显示scrollBar
的(坑2)
10、可以设置不同类型holder占据不同的空间(ItemColumnSpan GridLayoutManager)
上面这些不是所有的都讲,其实本文主要涉及的就是相关adapter
,里面对应不同的holder
,及相关的封装。然后说说踩了哪些坑。
基本思路
- 1、明确什么时候开始加载更多?
下拉刷新就调用SwipeRefreshLayout
相关就好了,那么加载更多呢?这个就要自己去写相关的布局了。然后第一个问题,什么时候加载更多??因为RecycleView
有各种布局,所以判断最后一个也是要区分不同的adapter的!
- 2、加载更多有多少种情况?
大致有三种,正在加载更多;加载更多错误;没有更多数据了;
具体实现
1、监听滑动,满足条件开始加载更多。
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (null != scrollListener) {
scrollListener.onScrolled(SwipeRefreshRecycleView.this, dx, dy);
}
if (null == manager) {
throw new RuntimeException("you should call setLayoutManager() first!!");
}
if (null == adapter) {
throw new RuntimeException("you should call setAdapter() first!!");
}
if (manager instanceof LinearLayoutManager) {
int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) manager).findLastCompletelyVisibleItemPosition();
if (adapter.getItemCount() > 1 && lastCompletelyVisibleItemPosition >= adapter.getItemCount() - 1 && adapter.isHasMore()) {
adapter.isLoadingMore();
if (null != listener) {
listener.onLoadMore();
}
}
int position = ((LinearLayoutManager) manager).findFirstVisibleItemPosition();
if (lastTitlePos == position) {
return;
}
lastTitlePos = position;
}
if (manager instanceof StaggeredGridLayoutManager) {
int[] itemPositions = new int[2];
((StaggeredGridLayoutManager) manager).findLastVisibleItemPositions(itemPositions);
int lastVisibleItemPosition = (itemPositions[1] != 0) ? ++itemPositions[1] : ++itemPositions[0];
if (lastVisibleItemPosition >= adapter.getItemCount() && adapter.isHasMore()) {
adapter.isLoadingMore();
if (null != listener) {
listener.onLoadMore();
}
}
}
}
2、定义自己的加载更多的ViewHolder。
3.定义相关的方法实时更新ViewHolder的三种状态。
public class NewBottomViewHolder extends RecyclerView.ViewHolder{
@Bind(R.id.footer_container)
public LinearLayout contaier;
@Bind(R.id.progressbar)
ProgressBar pb;
@Bind(R.id.content)
TextView content;
@Nullable
private final SwipeRefreshRecycleView.OnRefreshLoadMoreListener mListener;
public NewBottomViewHolder(View itemView, SwipeRefreshRecycleView.OnRefreshLoadMoreListener listener) {
super(itemView);
ButterKnife.bind(this,itemView);
mListener = listener;
}
public void bindDateView(int state) {
switch (state) {
case AdapterLoader.STATE_LASTED:
contaier.setVisibility(View.VISIBLE);
contaier.setOnClickListener(null);
pb.setVisibility(View.GONE);
content.setText("--- 没有更多了 ---");
break;
case AdapterLoader.STATE_LOADING:
contaier.setVisibility(View.VISIBLE);
content.setText("加载更多!!");
contaier.setOnClickListener(null);
pb.setVisibility(View.VISIBLE);
break;
case AdapterLoader.STATE_ERROR:
contaier.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
content.setText("--- 加载更多失败点击重试 ---");
contaier.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onLoadMore();
}
content.setText("加载更多!!");
pb.setVisibility(View.VISIBLE);
}
});
break;
}
}
}
4.定义相关扩展方法便于用户自己定义底部布局及相关状态处理。
这里就必须详细讲讲Adapter里面的相关方法了!
getItemCount(),在RecycleView知道它一共有多少数量的Item需要展示,返回0之后不会执行剩余的方法!
onCreateViewHolder(ViewGroup parent, int viewType),某种Type的Holder第一次创建的时候会调用该方法,当然没有复用的时候也会去创建,一旦复用了,改方法不会再执行了!
onBindViewHolder(RecyclerView.ViewHolder holder, int position),每一次更新对应itemView的时候都会调用该方法,所以在该方法中要实时的刷新数据!(因为存在复用,所以刷新的时候一定要彻底!!!)
以上三个方法是必须实现的,因为在父类adapter里是抽象滴!
还有一个方法也比较重要:
getItemViewType(int position),这个方法是返回对应pos的类型的,如果你只有一个类型,不需要重写该方法,默认返回的是0。
是不是这么说起来比ListView还要爽一点儿?不用去判断什么convertView==null
!
@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_BOTTOM:
if (loadMore != null) {
RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);
if (holder == null) {
throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");
}
return holder;
} else {
return new BottomViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_footer, parent, false));
}
default:
return onViewHolderCreate(parent, viewType);
}
}
@Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == TYPE_BOTTOM) {
loadState = loadState == STATE_ERROR ? STATE_ERROR : isHasMore() ? STATE_LOADING : STATE_LASTED;
if (loadMore != null) {
try {
onBottomViewHolderBind(holder, loadState);
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
((BottomViewHolder) holder).bindDateView(loadState);
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
onViewHolderBind(holder, position);
}
}
这里在RefreshRecycleAdapter<T>
中实现了刚刚说的三个方法,并且相关的已经加final修饰了!所以之后你只需要实现如下方法来完成你自己的item填充就好了:
RecyclerView.ViewHolder onViewHolderCreate(ViewGroup parent, int viewType);
void onViewHolderBind(RecyclerView.ViewHolder holder, int position);
对于加载更多的几种状态的更改,提供如下的相关方法!
boolean isHasMore();
void isLoadingMore();
void loadMoreError();
对于创建自己制定的加载更多的布局,提供如下方法扩展!
void setLoadMoreView(View view);
RecyclerView.ViewHolder onBottomViewHolderCreate(View loadMore);
void onBottomViewHolderBind(RecyclerView.ViewHolder holder, int loadState);
还没有说的那就是数据源相关的方法。提供了set和append两种方式!
void setList(List<T> data);
void appendList(List<T> data);
@Override
public final void appendList(List<T> data) {
int positionStart = list.size();
list.addAll(data);
int itemCount = list.size() - positionStart;
if (positionStart == 0) {
notifyDataSetChanged();
} else {
notifyItemRangeInserted(positionStart + 1, itemCount);
}
}
还是那话,这些方法都是RefreshRecycleAdapter<T>
里面写好的,我们写自己的adapter时更本不用去care!只需要去调用setList()
或者appendList()
就好了!!
说到这里不得不提提RecycleView
刷新数据的相关方法和坑!
在notifyDataSetChanged()
的基础上, RecycleView
增加了一系列的方法用于增删改。所以不要再任性的一味使用notifyDataSetChanged(),这样也不专业了!
notifyItemInserted();
notifyItemRangeInserted();
notifyItemChanged();
notifyItemRangeChanged();
notifyItemRemoved();
notifyItemRangeRemoved();
在使用的过程中,发现调用notifyItemChanged()
之后不会去执行onBindViewHolder()
,(坑3 坑4)这个就导致刷新没有触发了!最后搜到的结果是因为mRecyclerView.setItemAnimator(new DefaultItemAnimator())引起的,解决方案是复写相关方法
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {
return true;
}
小结
首先通过addList()
或者appendList()
的方法设置相关数据源。
滑动过程中需要加载更多时回调相关方法,并在adapter中通知相关状态刷新。
adapter.isLoadingMore();
if (null != listener) {
listener.onLoadMore();
}
加载错误的时候调用相关的方法通知状态改变。
adapter.loadMoreError();
创建BottomHolder
的时候判断有没有设置自定义的view,如果有,那么就去走子类的onBottomViewHolderCreate()
方法创建自定义的Bottomholder
,然后实时更新相关数据!
@Override
public final void setLoadMoreView(@NonNull View view) {
loadMore = view;
}
if (loadMore != null) {
RecyclerView.ViewHolder holder = onBottomViewHolderCreate(loadMore);
if (holder == null) {
throw new RuntimeException("You must impl onBottomViewHolderCreate() and return your own holder ");
}
return holder;
}
最后在onBottomViewHolderBind(RecyclerView.ViewHolder holder, int state)
的方法中执行bindDateView(state)
实时刷新相关的状态。
public void bindDateView(int state) {
switch (state) {
case AdapterLoader.STATE_LASTED:
contaier.setVisibility(View.VISIBLE);
contaier.setOnClickListener(null);
pb.setVisibility(View.GONE);
content.setText("--- 没有更多了 ---");
break;
case AdapterLoader.STATE_LOADING:
contaier.setVisibility(View.VISIBLE);
content.setText("加载更多!!");
contaier.setOnClickListener(null);
pb.setVisibility(View.VISIBLE);
break;
case AdapterLoader.STATE_ERROR:
contaier.setVisibility(View.VISIBLE);
pb.setVisibility(View.GONE);
content.setText("--- 加载更多失败点击重试 ---");
contaier.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onLoadMore();
}
content.setText("加载更多!!");
pb.setVisibility(View.VISIBLE);
}
});
break;
}
}
PS 最后还有默认是不显示scrollBar
的问题,这个问题,似乎必须在xml里面配置,不能代码直接new RecycleView
。然后可以使用相关代码控制ScrollBar
是否显示!
mRecyclerView.setVerticalScrollBarEnabled(true)
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
另外对于特性8、10这里就不详细介绍了,滑动删除和拖拽排序在TouchHelperCallback
中有相关支持!方法如下:
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if (callBack != null) {
callBack.onItemMove(viewHolder.getAdapterPosition(),
target.getAdapterPosition());
}
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if (callBack != null) {
callBack.onItemDismiss(viewHolder.getAdapterPosition());
}
}
详细的可以参照相关Demo-FangShiActivity
gradle快速集成
compile 'com.lovejjfg.powerrecycle:powerrecycle:1.0.0'
相关下载
---- Edit By Joe ----