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) {
            }
        });

后续有其他问题在做补充跟进,希望能帮到大家,有什么问题的,可以给我留言!

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