RecyclerView三種顯示方式的下拉刷新和上拉加載更多


有關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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章