1. Application scenario of snaphelper
Generally, when we use RecyclerView to implement the simple picture rotation picture Banner, we need to realize the page turning effect by picture, but RecyclerView will "stay in the process" during the rolling process and cannot achieve the "page turning" effect. At this time, we have to use SnapHelper class to make RecyclerView have the ability of "page turning" effect similar to ViewPager. However, with the complexity of page UI layout, sometimes we need to nest RecyclerView and combine SnapHelper.
2. Problem phenomenon
When multi-layer recyclerviews are nested, the SnapHelper tool class is used to cooperate. It is normal to scroll down the Item list, but scrolling up will immediately force back and kill the app program. Error reporting problem: "java.lang.IllegalStateException: An instance of OnFlingListener already set."
3. Analyze the causes
First, let's understand a concept. The finger slides the RecyclerView on the screen, and then let go. The content in the RecyclerView will continue to scroll in the direction of finger sliding along the inertia until it stops. This process is called flying. The flip operation is triggered instantly when the finger leaves the screen and ends when the scrolling stops. OnFlingListener is obviously a listener that listens to flying scrolling events.
4. Reasons and key points: (SnapHelper is created multiple times and bound to the same RecyclerView)
Usually, we will encounter such problems when nesting RecyclerView, because it is written in onBindViewHolder every time:
SnapHelper snapHelper = new PagerSnapHelper() snapHelper.attachToRecyclerView(recyclerView)
Each time you slide the RecyclerView, you need to recreate the SnapHelper object and attach it to the RecyclerView. As a result, a recycliview will bind multiple snaphelpers. When you draw the RecyclerView back, you will find that the SnapHelper instance (multiple) of a RecyclerView is repeatedly set, resulting in a problem with the scrolling event and exit the scrolling, resulting in the collapse and exit of the entire app application!
5. Solutions
5.1 the first method:
When repainting the RecyclerView, first remove the OnFlingListener listener of the previous SnapHelper instance you created.
Tips: that is, when the RecyclerView slides to this position for the second time
Java language
SnapHelper snapHelper = new PagerSnapHelper() recycleView.setOnFlingListener(null) snapHelper.attachToRecyclerView(recyclerView)
Kotlin language
val snapHelper: SnapHelper = PagerSnapHelper() recycleView.onFlingListener = null snapHelper.attachToRecyclerView(recyclerView)
Tips: SnapHelper is attached to the RecyclerView through the attachToRecyclerView() method, so as to realize the auxiliary RecyclerView rolling alignment operation.
5.2 the second method:
Snaphelper = New
PagerSnapHelper() is placed in the global definition (for the class), allowing only one SnapHelper object to exist in the class. The onFlingListener of the SnapHelper instance object is always called each time the RecyclerView is redrawn.
Tips: this method does not need to add any code. It only needs to SnapHelper snapHelper = new
PagerSnapHelper() is placed at the same level as the override method onBindViewHolder().
6 principle analysis
According to the following source code, when the OnFlingListener of the SnapHelper instance object bound to RecyclerView has been set, the system will throw an exception again: "An instance of OnFlingListener already set “
7 source code analysis:
Error type&Specific error: 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); }