美文网首页Android解决Bug篇
完美解决:RecyclerViwe中使用SnapHelper报错

完美解决:RecyclerViwe中使用SnapHelper报错

作者: 千夜零一 | 来源:发表于2020-10-27 15:37 被阅读0次

    SnapHelper的应用情景

      通常我们使用RecyclerView来实现简单图片轮播图Banner时,需要实现按图片翻页效果,但RecyclerView会在滚动过程中是“过程停留”无法达到“翻页”效果,这时候我们就不得不借助SnapHelper类来使得RecyclerView具备类似ViewPager“翻页”效果的能力。但随着页面UI布局的复杂性,有时候我们需要嵌套RecyclerView并结合SnapHelper。


    问题现象

      多层RecyclerView嵌套时使用SnapHelper工具类配合,向下滚动Item列表正常,但向上滚动会立即强退并杀死app程序。报错问题:“java.lang.IllegalStateException: An instance of OnFlingListener already set.”


    分析原因

      首先来了解一个概念,手指在屏幕上滑动RecyclerView然后松手,RecyclerView中的内容会顺着惯性继续往手指滑动的方向继续滚动直到停止,这个过程叫做Fling。Fling操作从手指离开屏幕瞬间被触发,在滚动停止时结束。而OnFlingListener显然就是监听Fling滚动事件的监听器。

    原因重点:(SnapHelper被多次创建并绑定到同一个RecyclerView)

      通常我们在做RecyclerView的嵌套时总会遇到这样的问题,是因为每次在onBindViewHolder中都这样写:

    SnapHelper snapHelper = new PagerSnapHelper()
    snapHelper.attachToRecyclerView(recyclerView)
    

      每次滑动RecyclerView都需要重新创建SnapHelper对象并将其附着到RecyclerView上,导致一个RecyclerIView会绑定多个SnapHelper,在回头绘制RecyclerView时,会发现一个RecyclerView的SnapHelper实例(多个)重复设置,导致滚动事件出问题而退出滚动,致使整个app应用崩溃退出!


    解决方法

    第一种方法:

      在重新绘制RecyclerView时首先移除创建的前一个SnapHelper实例的OnFlingListener监听器。

    Tips: (也就是RecyclerView在第二次滑动到该位置时)

    Java语言

     SnapHelper snapHelper = new PagerSnapHelper()
     banner_rv.setOnFlingListener(null)
     snapHelper.attachToRecyclerView(recyclerView)
    

    Kotlin语言

    val snapHelper: SnapHelper = PagerSnapHelper()
    banner_rv.onFlingListener = null
    snapHelper.attachToRecyclerView(recyclerView)
    

      Tips:SnapHelper通过attachToRecyclerView()方法附着到RecyclerView上,从而实现辅助RecyclerView滚动对齐操作。

    第二种方法:

      将SnapHelper snapHelper = new PagerSnapHelper()放在全局定义(针对类),允许类中只存在一个SnapHelper对象。每次重新绘制RecyclerView时总是调用该SnapHelper实例对象的onFlingListener。

      Tips:此方法不用添加任何代码,仅需要将SnapHelper snapHelper = new PagerSnapHelper()放在与重写方法onBindViewHolder()同级的位置。


    原理剖析

      据下面的源码可以看到当与RecyclerView绑定的SnapHelper实例对象的OnFlingListener已经被设置时,再次设置系统会抛出异常:”An instance of OnFlingListener already set.“


    源码解析:

    错误类型&具体错误:IllegalArgumentException:An instance of OnFlingListener already set.

     /**
         * Attaches the {@link SnapHelper} to the provided RecyclerView, by calling
         * {@link RecyclerView#setOnFlingListener(RecyclerView.OnFlingListener)}.
         * You can call this method with {@code null} to detach it from the current RecyclerView.
         *
         * @param recyclerView The RecyclerView instance to which you want to add this helper or
         *                     {@code null} if you want to remove SnapHelper from the current
         *                     RecyclerView.
         *
         * @throws IllegalArgumentException if there is already a {@link RecyclerView.OnFlingListener}
         * attached to the provided {@link RecyclerView}.
         *
         */
    
    /**
         * Called when an instance of a {@link RecyclerView} is attached.
         */
        private void setupCallbacks() throws IllegalStateException {
            if (mRecyclerView.getOnFlingListener() != null) {
                throw new IllegalStateException("An instance of OnFlingListener already set.");
            }
            mRecyclerView.addOnScrollListener(mScrollListener);
            mRecyclerView.setOnFlingListener(this);
        }
    

    大功告成!

    相关文章

      网友评论

        本文标题:完美解决:RecyclerViwe中使用SnapHelper报错

        本文链接:https://www.haomeiwen.com/subject/lrdxvktx.html