有关RecyclerView的类似SwipeRefreshLayout的上拉加载更多,我写了一篇博客http://blog.youkuaiyun.com/cj_286/article/details/52767070
但是之前写的那个有一点点的小问题,如果上拉刷新,刷新小图标还没有移动到刷新位置,刷新数据就已经获取到,并且调用了setRefreshing(false),在setRefreshing(false)中会去调用translationTo(int from,int to,final boolean isShow),将刷新图标隐藏,而在上拉刷新的时候,也会去调用translationTo(int from,int to,final boolean isShow),将图标移动到刷新位置,就在移动过程中还没有结束,就调用了停止刷新,这样就会出现问题,所以解决办法就是如果在上拉加载更多的时候,如果刷新图标还没有移动到刷新位置就调用了setRefreshing(false)来停止刷新,这是就要等待让其移动到指定位置之后再停止,所以就需要提供一个借口监听,上拉刷新图标到指定位置回调该接口,在setRefreshing(false)中如果上拉刷新图标还在移动过程中就设置监听,在监听回调中再去停止刷新,这样就解决了以上的bug.translationTo(int from,int to,final boolean isShow)方法修改后的代码如下:
/**
* 执行平移动画
* @param from
* @param to
*/
private void translationTo(int from,int to,final boolean isShow){
//1.调用ofInt(int...values)方法创建ValueAnimator对象
ValueAnimator mAnimator = ValueAnimator.ofInt(from,to);
//2.为目标对象的属性变化设置监听器
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// 3.为目标对象的属性设置计算好的属性值
int animatorValue = (int)animation.getAnimatedValue();
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) mImageView.getLayoutParams();
marginLayoutParams.bottomMargin = animatorValue;
mImageView.setLayoutParams(marginLayoutParams);
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(isShow){
start();
mPrepareAnimation = false;
//设置准备刷新的回调,防止在没有到刷新的位置,就已经调用了setRefreshing(false)来停止刷新,这样就会出现问题
//内部使用
if(mPrepareAnimationListener != null){
mPrepareAnimationListener.finishAnimation();
}
}else{
hideRefreshArrow();
hide();
}
}
});
//4.设置动画的持续时间、是否重复及重复次数等属性
mAnimator.setDuration(100);
//mAnimator.setRepeatCount(3);
mAnimator.setRepeatMode(ValueAnimator.INFINITE);
//5.为ValueAnimator设置目标对象并开始执行动画
mAnimator.setTarget(mImageView);
mAnimator.start();
}
监听接口定义
/**
* 准备刷新动画的监听,就是上拉后,移动到刷新位置的监听
*/
interface PrepareAnimationListener{
void finishAnimation();
}
private PrepareAnimationListener mPrepareAnimationListener;
private void setPrepareAnimationListener(PrepareAnimationListener listener){
mPrepareAnimationListener = listener;
}
setRefreshing(boolean refreshing)代码修改后如下:
/**
* 加载更多或停止加载更多
* @param refreshing
*/
public void setRefreshing(boolean refreshing) {
if(!mIsAllowLoadMore) return;
if(refreshing){
if(mStart) return;
showRefreshArrow();
getValueToTranslation();
mPrepareAnimation = true;
mIsRefreshing = true;//正在刷新
if (mOnPullListener != null) {
mOnPullListener.onLoadMore(this);
}
mIsCanScoll = false;
}else {
//如果准备执行动画(平移到刷新转圈的位置)结束后才能停止
if(!mPrepareAnimation) {
setRefreshStop();
}else{
//上拉加载的View还没有到刷新转圈的地方,就停止了刷新,所以要设置准备刷新的监听,
//等到刷新的地方了,再调用停止刷新
setPrepareAnimationListener(new PrepareAnimationListener() {
@Override
public void finishAnimation() {
setRefreshStop();
}
});
}
}
}
在解决以上的问题后,之前的PullRefreshLayout并不支持StaggeredGridLayoutManager(瀑布流)的上拉加载更多,这次将其加上,要想实现瀑布流的上拉刷新,在瀑布流显示方式的时候,向上滑动时,要不断的去获取,瀑布流中显示在最下面的条目布局的getBottom(),如果在下面的条目布局的getBottom()<= getHeight()时,就说明已经到底了,应该去上拉刷新了,那么如何去获取最下面的条目布局呢,StaggeredGridLayoutManager提供了一个findLastVisibleItemPositions(int[])方法,该方法是获取瀑布流的每一列的最后一个的条目的索引,然后再根据findViewByPosition(int)去获取条目布局,条目布局获取到了就可以得到getBottom(),获取没一列最后一个条目的getBottom(),取其最大的一个与getHeight()相比,如果小于等于就说明到底了,该刷新了。
获取最后一个可见条目的索引
/**
* 获取底部可见项的位置
* 获取最后一个条目的索引
* @param lastPos 如果是瀑布流的话就返回每一列最后一个条目的索引
* @return
*/
private int getLastVisibleItemPosition(int[] lastPos) {
RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager();
int lastVisibleItemPosition = 0;
if (lm instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) lm).findLastVisibleItemPosition();
} else if (lm instanceof LinearLayoutManager) {
lastVisibleItemPosition = ((LinearLayoutManager) lm).findLastVisibleItemPosition();
}else if(lm instanceof StaggeredGridLayoutManager){
StaggeredGridLayoutManager layoutManager = (StaggeredGridLayoutManager)lm;
int columnCount = layoutManager.getColumnCountForAccessibility(null, null);//获取瀑布流的列数
//int columnCount = layoutManager.getSpanCount();//获取瀑布流的列数
int[] positions = new int[columnCount];
layoutManager.findLastVisibleItemPositions(positions);//获取瀑布流的每一列的最下面的条目的索引(并不是最后n个(n为瀑布流的列数)),有的条目可能会很长
lastVisibleItemPosition = getArrayMax(positions);//返回其中最大的一个(它是乱序的,并不是按顺序保存的)
System.arraycopy(positions,0,lastPos,0,positions.length);
//瀑布流的布局方式是哪一列的高度最小,下一个条目就排到哪一列的后面
}
return lastVisibleItemPosition;
}
瀑布流是否已经到底部了
/**
* 判断最后几个有一个到底部了就说明到底部了
* @param layoutManagers
* @param lastPos
* @return
*/
private boolean staggeredCanPullUp(RecyclerView.LayoutManager layoutManagers, int[] lastPos){
if(!(layoutManagers instanceof StaggeredGridLayoutManager)) return false;
int bottom = 0;
for(int i=0;i<lastPos.length ; i++){
/**
* 判断lastItem的底边到recyclerView顶部的距离
* 是否小于recyclerView的高度
* 如果小于或等于 说明滚动到了底部
* 这样有一个弊端,可能有某一列显示不全就加载更多了
*/
// if(layoutManagers.findViewByPosition(lastPos[i]).getBottom() <= getHeight() ){
// /**
// * 已到最后条目的底部了
// */
// return true;
// }
/**
* 这个就不存在以上的弊端了
*/
//取出每一列的最后一个条目的mBottom,并并取出最大的一个与getHeight()比较
if(layoutManagers.findViewByPosition(lastPos[i]).getBottom() > bottom) {
bottom = layoutManagers.findViewByPosition(lastPos[i]).getBottom();
}
}
//return false;
return bottom <= getHeight();
}
好了,暂时我就发现这两点问题,如果还有其他的问题或是不合理的地方,欢迎留言
在该Demo中的RecyclerView的万能设配器是优快云鸿洋博客中的http://blog.youkuaiyun.com/lmj623565791/article/details/51118836
下载地址:优快云