当Recycler中需要下拉刷新的时候。就想自己实现一下。
发现LinearLayoutManager时,体验完美。但是,到了GridLayoutManager和StaggeredGridLayoutManager问题就来了:
在网上搜索了解决办法,还真有:
地址:http://blog.csdn.net/qibin0506/article/details/49716795
GridLayoutManager解决方法:
getSpanSize() 这个方法返回这个Item占几个空间,默认情况下占一个。
isFooter(position) 为true时,说明这是一个底部布局,返回gridManager.getSpanCount() 。标识占一整行。
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return isFooter(position) ? gridManager.getSpanCount() : 1;
}
});
}
}
StaggeredGridLayoutManager解决方法:
StaggeredGridLayoutManager.LayoutParams为我们提供了一个setFullSpan方法来设置占领全部空间
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (isStaggeredGridLayout(holder)) {
handleLayoutIfStaggeredGridLayout(holder, holder.getLayoutPosition());
}
}
private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) {
ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
return true;
}
return false;
}
protected void handleLayoutIfStaggeredGridLayout(RecyclerView.ViewHolder holder, int position) {
if (isHeader(position) || isFooter(position)) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
p.setFullSpan(true);
}
}
============================================================================
下面看实现:
很多上拉加载,都封装进了RecyclerView和LayoutManager。
在XML中引用自定义的RecyclerView就可以得到上拉加载功能。
我这个是将上拉加载扩展到了Adapter 和 OnScrollListener。
这样,xml不需要修改,还是使用原生的控件,只需要设置Adapter和OnScrollListener,
就可以实现上拉加载。RecyclerView和LayoutManager都使用系统默认的就ok!!!
自定义一个Adapter:ExAdapter,扩展上拉加载功能:
主要方法如下:
@Override
public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (hasHeader() && viewType == TYPE_HEADER) {
View v = LayoutInflater.from(parent.getContext()).inflate(getHeaderView(), parent, false);
return new HeaderViewHolder(v);
} else if (hasFooter() && viewType == TYPE_FOOTER) {
View v = LayoutInflater.from(parent.getContext()).inflate(getFooterView(), parent, false);
return new FooterViewHolder(v);
} else {
return onCreateHolder(parent, viewType);
}
}
/**
* 子类需要使用onCreateHolder 来替代onCreateViewHolder
*
* @param parent
* @param viewType
* @return
*/
protected abstract RecyclerView.ViewHolder onCreateHolder(ViewGroup parent, int viewType);
@Override
public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == TYPE_HEADER) {
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
View headerView = headerHolder.itemView;
onBindHeaderView(headerView);
} else if (getItemViewType(position) == TYPE_FOOTER) {
FooterViewHolder footerHolder = (FooterViewHolder) holder;
View footerView = footerHolder.itemView;
onBindFooterView(footerView);
} else {
onBindItemView(holder, hasHeader() ? position - 1 : position);
}
}
/**
* 子类需要使用onBindItemView 来替代onBindViewHolder
*/
protected abstract void onBindItemView(RecyclerView.ViewHolder holder, int position);
@Override
public final int getItemViewType(int position) {
int size = getCount();
if (hasHeader()) {
if (position == 0) {
return TYPE_HEADER; // 有头布局,并且位置时0.返回头布局类型
} else {
if (position == size + 1) {
return TYPE_FOOTER; // 当布局位置为 size + 1 说明有头布局,并且 这时的布局为底部布局
} else {
return getViewType(position - 1); // 有头布局时,position自动减一
}
}
} else {
if (position == size) { // 没有头布局时,size为position说明这时为底部布局
return TYPE_FOOTER;
} else {
return getViewType(position);// 没有头布局时,position不变
}
}
}
/**
* 子类需要使用getViewType 来替代getItemViewType
* 默认情况下返回0.
* -1 与 -2 已经被头布局 和 底部布局使用。不要返回-1 和 -2
*
* @param position
* @return
*/
protected int getViewType(int position) {
return 0;
}
@Override
public final int getItemCount() {
if (visibleItemCount != -1 && visibleItemCount < getCount()) {
// 不是初始化的时候 && Item数量要大于可见元素数量
setShowFooterView(true);
} else {
setShowFooterView(false);
}
int count = 0;
count += (hasHeader() ? 1 : 0);
count += (hasFooter() ? 1 : 0);
count += getCount();
return count;
}
/**
* 子类需要使用getCount 来替代getItemCount
*
* @return
*/
protected abstract int getCount();
/**
* 网格布局时,使用这个类来替代Adapter
* Created by wp on 16-3-10.
*/
public abstract class ExAdapterGridLayout extends ExAdapter {
/**
* 定义一个内部类GridSpanSizeLookup继承GridLayoutManager.SpanSizeLookup,
* 调用父类isHeader和isFooter方法判断是否是头或者尾,
* 如果是则返回gridManager.getSpanCount();
* 即一个item占据一行的span数,否则就返回1
*
* @param recyclerView
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
if (manager instanceof GridLayoutManager) {
final GridLayoutManager gridManager = ((GridLayoutManager) manager);
gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return isFooter(position) ? gridManager.getSpanCount() : 1;
}
});
}
}
}
/**
* 瀑布流时使用这个类来代替Adapter类
* Created by wp on 16-3-10.
*/
public abstract class ExAdapterStaggeredGridLayout extends ExAdapter {
/**
* StaggeredGridLayoutManager中没有setSpanSizeLookup方法,
* 庆幸的是StaggeredGridLayoutManager.LayoutParams中有setFullSpan方法可以达到同样的效果。
* 这时候重写的不再是onAttachedToRecyclerView方法而是onViewAttachedToWindow方法
*
* @param holder
*/
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
super.onViewAttachedToWindow(holder);
if (isStaggeredGridLayout(holder)) {
handleLayoutIfStaggeredGridLayout(holder, holder.getLayoutPosition());
}
}
private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) {
ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
return true;
}
return false;
}
protected void handleLayoutIfStaggeredGridLayout(RecyclerView.ViewHolder holder, int position) {
if (isHeader(position) || isFooter(position)) {
StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
p.setFullSpan(true);
}
}
}
继承RecyclerView.OnScrollListener的类:ExScrollListener
主要方法如下:
/**
* 当recyclerView滚动时调用
*
* @param recyclerView
* @param dx
* @param dy
*/
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
layoutManager = recyclerView.getLayoutManager();
if (mLayoutManagerType == null) {
if (layoutManager instanceof LinearLayoutManager) {
mLayoutManagerType = layoutManagerType.LINEAR_LAYOUT;
} else if (layoutManager instanceof GridLayoutManager) {
mLayoutManagerType = layoutManagerType.GRID_LAYOUT;
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
mLayoutManagerType = layoutManagerType.STAGGERED_GRID_LAYOUT;
} else {
throw new RuntimeException("不支持的layoutManager");
}
}
switch (mLayoutManagerType) {
case LINEAR_LAYOUT:
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
break;
case GRID_LAYOUT:
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
break;
case STAGGERED_GRID_LAYOUT:
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
if (lastPositions == null) {
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
}
staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
lastVisibleItemPosition = findMax(lastPositions);
break;
default:
break;
}
if (exAdapter == null && recyclerView.getAdapter() instanceof ExAdapter) {
exAdapter = (ExAdapter) recyclerView.getAdapter();
}
if (exAdapter != null && layoutManager != null)
exAdapter.setVisibleItemCount(layoutManager.getChildCount());
}
/**
* 滚动状态改变时调用
*
* @param recyclerView
* @param newState
*/
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//正在滚动时回调,回调2-3次,手指没抛则回调2次。scrollState = 2的这次不回调
//回调顺序如下
//第1次:scrollState = SCROLL_STATE_TOUCH_SCROLL(1) 正在滚动
//第2次:scrollState = SCROLL_STATE_FLING(2) 手指做了抛的动作(手指离开屏幕前,用力滑了一下)
//第3次:scrollState = SCROLL_STATE_IDLE(0) 停止滚动
//当屏幕停止滚动时为0;当屏幕滚动且用户使用的触碰或手指还在屏幕上时为1;
//由于用户的操作,屏幕产生惯性滑动时为2
if (layoutManager == null)
return;
// 没有处于加载状态
if (newState == RecyclerView.SCROLL_STATE_IDLE//当滚动停止时。判断
&& layoutManager.getChildCount() > 0 // 界面上可见的数量 > 0
&& lastVisibleItemPosition >= layoutManager.getItemCount() - 1 // 到达了最后一个条目
&& !noMoreData // 有更多数据
&& !loading // 没有正在加载数据
) {
loading = true;
tempTotalItemCount = layoutManager.getItemCount(); // 将Item的总数 赋值给previousTotal。用来判断是否请求到数据。
onLoadMore();
// Logger.w("可见的Item数量是:" + layoutManager.getChildCount() + "\n当前的Item数量时:" + layoutManager.getItemCount());
}
}
/**
* 上拉加载完成,调用此方法,
* 会通知ScrollListener上拉加载完成,可以进行下一次的上拉加载。
*/
public void loadMoreComplete() {
loading = false;
if (layoutManager == null)
return;
int count = layoutManager.getItemCount();
if (count == tempTotalItemCount) {
noMoreData = true; // 当加载后的数据 等于 之前数据时,证明什么页没有加载出来
} else {
noMoreData = false;
}
if (exAdapter != null)
exAdapter.setNoMoreData(noMoreData);
}
/**
* 重置没有数据状态。将字段变为true。
* 当没有数据时,就算上拉也不会进行加载。
* 这个方法可以清楚这个标识,让上拉加载重新启用
*/
public void resetLoadMore() {
noMoreData = false;
tempTotalItemCount = -1;
if (exAdapter != null)
exAdapter.setNoMoreData(noMoreData);
}
// ==================================================
在Activity中使用:
private void initRecycler(Activity activity) {
adapter = new ItemAdapter(activity); //ItemAdapter继承了 ExAdapter
adapter.showLoadMore(); // 设置显示“上拉加载”功能
scrollListener = new ExScrollListener() {
@Override
protected void onLoadMore() {
// 上拉加载回调
}
};
// setLayoutManager() 方法要在setAdapter()方法之前调用。不然会出问题
viewHolder.tab1Recycler.setLayoutManager(new LinearLayoutManager(activity));
viewHolder.tab1Recycler.setAdapter(adapter);
viewHolder.tab1Recycler.addOnScrollListener(scrollListener);
}