有關RecyclerView的類似SwipeRefreshLayout的上拉加載更多,我寫了一篇博客http://blog.csdn.net/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的萬能設配器是CSDN鴻洋博客中的http://blog.csdn.net/lmj623565791/article/details/51118836
下載地址:CSDN