RecyclerView刷新閃爍問題

RecyclerView相比傳統的ListView無疑是一個更高級別且靈活性更強的一個控件,主要可用於數據列表展示

第一:RecyclerView的基本使用,屬於普及知識→_→

1.佈局管理
LayoutManager有三種: LinearLayoutManager(線性佈局) GridLayoutManager(表格式佈局)StaggeredGridLayoutManager(瀑布流式佈局) 三種佈局樣式,囊括了日常可能涉及到得絕大多數樣式了

2.ItemDecoration分割線定義
可自定義分割線寬度、顏色等屬性

3.ItemAnimator 動畫
控制item的增刪改的刷新動畫,默認有一個漸變的效果

4.ItemTouchHelper
對item的滑動刪除

如果你對以上4中屬性很熟悉,那麼列表在你眼裏,就不是什麼難事兒了

第二:使用中遇到的問題
本文不講解如何使用RecycclerView,主要講解所遇到的問題,只記錄目前遇到的,後續有再更新

問題一:滑動閃爍的問題

在這裏插入圖片描述

之前用RecyclerView做統計圖的時候,左右滑動一旦涉及刷新列表就會閃,前面說了ItemAnimator主要控制item的效果
可是檢查代碼之下,發現自己並沒有設置ItemAnimator,所以自然就腦補源碼了。在查看源碼之後發現RecyclerView默認就有一個動畫效果DefaultItemAnimator。

那麼DefaultItemAnimator裏面做了什麼會造成刷新一閃一閃的呢?

追溯之下發現默認動畫的runPendingAnimations(執行動畫的方法)調用了一個方法如圖:
在這裏插入圖片描述

我們進入這個方法:
在這裏插入圖片描述

重點就是這兩個地方了,默認動畫裏面設置了一個從0-1的alpha(透明度)的變化,所以我們在刷新的時候,就一定會出現閃一下的現象

解決方案:自定義DefaultItemAnimator
知道問題在哪兒了,當然就是直接貼代碼啦,具體使用時直接拷貝DefaultItemAnimator源碼,然後找到下面的方法,直接全部替換

void animateChangeImpl(final CustomDefaultItemAnimator.ChangeInfo changeInfo) {
    final RecyclerView.ViewHolder holder = changeInfo.oldHolder;
    final View view = holder == null ? null : holder.itemView;
    final RecyclerView.ViewHolder newHolder = changeInfo.newHolder;
    final View newView = newHolder != null ? newHolder.itemView : null;
    if (view != null) {
        final ViewPropertyAnimator oldViewAnim = view.animate().setDuration(
                getChangeDuration());
        mChangeAnimations.add(changeInfo.oldHolder);
        oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
        oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
        oldViewAnim.setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animator) {
                dispatchChangeStarting(changeInfo.oldHolder, true);
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                oldViewAnim.setListener(null);
                view.setAlpha(1);
                view.setTranslationX(0);
                view.setTranslationY(0);
                dispatchChangeFinished(changeInfo.oldHolder, true);
                mChangeAnimations.remove(changeInfo.oldHolder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }
    if (newView != null) {
        final ViewPropertyAnimator newViewAnimation = newView.animate();
        mChangeAnimations.add(changeInfo.newHolder);
        newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration())
                .setListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animator) {
                dispatchChangeStarting(changeInfo.newHolder, false);
            }
            @Override
            public void onAnimationEnd(Animator animator) {
                newViewAnimation.setListener(null);
                newView.setAlpha(1);
                newView.setTranslationX(0);
                newView.setTranslationY(0);
                dispatchChangeFinished(changeInfo.newHolder, false);
                mChangeAnimations.remove(changeInfo.newHolder);
                dispatchFinishedWhenDone();
            }
        }).start();
    }
}

問題二:定位不準確
我們想定位指定position的指定位置,可能會出現定位不準的問題,怎麼解決呢?

1.首先我們可以獲取到滑動的偏移值,拿我要定位到指定的item得中間

/**
     * 獲取滑動值 》》滑動偏移 / 每個格子寬度
     *
     * @return 當前值
     */
    private int getScrollPosition() {

        return (int) ((double) (mRecyclerView.computeHorizontalScrollOffset() + HistoryAdapter.getItemStdWidth(mContext) / 2)
                / (double) HistoryAdapter.getItemStdWidth(mContext));
    }

2.獲取中間得位置

/**
     * 獲取中間位置 ITEM_NUM使我們一屏顯示的item總數,基數
     *
     * @return 當前值
     */
    private int getMiddlePosition() {
        return getScrollPosition() + (HistoryAdapter.ITEM_NUM / 2);
    }

3.定位刷新
上面兩部我們獲取得到了最後應該定位的位置,具體定位代碼如此

 mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                // 效果在暫停時顯示, 否則會導致重繪異常
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    if (null == mRecyclerView || mRecyclerView.getChildCount() == 0) {
                        return;
                    }
                    int position = getMiddlePosition();
                    mHistoryAdapter.highlightItem(position);
                    //定位到指定item
                    mRecyclerView.scrollToPosition(getScrollPosition());
                    //滾動到指定的適配器位置,從已解析的佈局獲得給定的偏移量,要更新偏移量,我們必須要通過layoutManager調用這個方法,否則系統不會主動刷新偏移量的
                    mLayoutManager.scrollToPositionWithOffset(getScrollPosition(), 0);
                }
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            }
        });

後續有其他問題在做補充跟進,希望能幫到大家,有什麼問題的,可以給我留言!

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