background

Recently snowball Android team has carried out a series of optimization for the slow frame problem of fund details page, one of the main work is to realize the split screen loading through RecyclerView, the key problem to be solved in the process is “bottom to discuss the floating layer linkage effect”, this paper summarizes the problems encountered and the solution process, I share it with you here.

This linkage effect can be applied in many scenarios. At present, there are not many reference materials, and the external output is also hoped to provide some help to other developers, so that we can avoid some detours.

implementation

Since the problem itself is somewhat complex, it is recommended that you read the following articles before reading this article in order to quickly understand how it is implemented:

  • Android Custom Behavior[1]

  • Use of BottomSheetBehavior [2]

  • Nested sliding mechanism [3]

The overall layout adopts RecyclerView+CoordinatorLayout+ViewPager. It can be seen from the effect diagram that the discussion on the floating layer is CoordinatorLayout+ViewPager. The discussion on the floating layer is RecyclerView. When RecyclerView slides to the end, floating layer will be associated to slide together, and floating layer can also be freely dragged up and down. As for the realization of linkage effect, we need to focus on solving the following problems:

  • Discuss how float layer realizes up and down free drag?

  • Sliding RecyclerView or CoordinatorLayout, sliding to a certain distance RecyclerView and CoordinatorLayout how to do the correlation and rolling together?

  • Sliding down RecyclerView or CoordinatorLayout how do you cancel linkage?

For the above problems, let’s take a look at specific solutions!

Free to drag

<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <... SNBObservableNestedRecycleView android:layout_width="match_parent" android:layout_height="match_parent"/> <androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout ... android:orientation="horizontal" app:behavior_peekHeight="54dp" app:layout_behavior="... BottomSheetBehavior" /> </androidx.coordinatorlayout.widget.CoordinatorLayout> </RelativeLayout>Copy the code

The following diagram shows the 3D structure of the CoordinatorLayout+Behavior design:

This layout structure does not look complicated, discussing the floating layer on top, which hides part of the content by default. The effect of the floating layer dragging freely with gestures is implemented using the BottomSheetBehavior provided by the system. Students who do not know BottomSheetBehavior can refer to the use of BottomSheetBehavior [4]. Let’s see the implementation effect:

Observing the effect diagram, we found that using BottomSheetBehavior alone can realize the free dragging of floating layer, but cannot realize the associated scrolling of RecyclerView and CoordinatorLayout. Therefore, we need to extend the BottomSheetBehavior to achieve the linkage effect.

Linkage effect

Layout structure of SNBObservableNestedRecycleView and SNBBottomSheetBehavior linkage effect is an important implementation class, based on system BottomSheetBehavior extends the following two functions:

  • When the RecyclerView slides to the bottom, if you continue to drag the RecyclerView up and down, you can start to associate the CoordinatorLayout and scroll up and down

  • When has been at RecyclerView associated with CoordinatorLayout rolling state, slide down RecyclerView/CoordinatorLayout, scroll to a certain distance, cancel the associated scroll effect, CoordinatorLayout sits at the bottom and RecyclerView can continue to slide

This looks a little round, but in fact is not difficult to understand, we can compare the above two renderings to help understand.

Behavior

Before introducing the specific implementation, we first talk about an internal class Behavior of CoordinatorLayout. The most important effect of this implementation is the extension of Behavior. About Behavior, we mainly discuss the following issues:

  • What’s Behavior?

  • What are the key methods of Behavior?

  • What is the basic principle of Behavior?

  • What is a nested sliding mechanism?

Behavior is the API in the Material Design library. Its main function is to coordinate the interaction between the direct Child Views in CoordinatorLayout. Behavior can only act on the direct child View of CoordinatorLayout, that is to say, Behavior acts on the parent View. All events are initiated by the child View, and the parent View receives and responds. The events mentioned here are usually basic events such as dragging and sliding. “These Interactions may include drags, swipes, Flings, or any other Gestures” is defined as follows:

  • The class that only implements the NestedScrollingChild interface is the child View, which is the initiator of the event

  • The class that only implements the NestedScrollingParent interface is the parent View, the event receiver responder

CoordinatorLayout implements the NestedScrollingParent interface, and usually CoordinatorLayout acts as the parent View which is the event receiver responder, RecyclerView is a subclass of NestedScrollingChild, which is all the event initiators in this article, The following figure introduces the execution process between NestedScrollingChild, NestedScrollingParent, CoordinatorLayout and Behavior to help you understand the relationship between the four:

The core methods of Behavior are as follows:

When the child View calls startNestedScroll (directly or indirectly), the Behavior will call back the onStartNestedScroll method and return a value indicating whether the parent control receives the nested slide event:

@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
    return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}

Copy the code

When the child View calls dispatchNestedPreScroll, it calls the Behavior’s onNestedPreScroll method. The parent View responds to the sliding operation first and consumes part or all of the sliding distance:

@Override
public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
    super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
}

Copy the code

The parent View receives the information about the sliding distance after the child View has processed the sliding distance. Here the parent control can choose whether to process the remaining sliding distance. If you want this method to be called back, onStartNestedScroll must return true:

@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, @NonNull int[] consumed) {
    super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed);
}

Copy the code

Nested slide end (ACTION_UP or ACTION_CANCEL) call onStopNestedScroll:

@Override
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) {
    super.onStopNestedScroll(coordinatorLayout, child, target, type);
}

Copy the code

So with Behavior, what is nested sliding

Nested sliding mechanism

The nested slide mechanism is used to handle nested slides (child and parent View can slide at the same time). Generally speaking, there are three ways to handle nested slides:

  • Traditional event distribution mechanism, this way is very troublesome to deal with, serious coupling

  • NestedScrollingChild and NestedScrollingParent implementation

  • CoordinatorLayout and Behavior are used

This paper uses the third scheme, which is based on the second scheme. For all the events initiated by NestedScrollingChild in the nesting sliding mechanism, CoordinatorLayout as the parent View has corresponding implementation, and Beahvior is the specific implementation class of all the logic of the parent View. In other words, Beahvior can be understood as the encapsulation of the second scheme, and the basic principles are as follows:

  • When the child View receives a slide event and is ready to slide, it will notify the parent control (startNestedScroll) first.

  • Before the child View slides, the parent control is asked if it wants to slide (dispatchNestedPreScroll)

  • If the parent control slides in response to the event, the child control is notified of how much sliding distance it has consumed, and the child control handles the remaining sliding distance

  • After the child control is finished sliding, if there is still some distance left, the parent control will be asked again whether to continue sliding the remaining distance (dispatchNestedScroll)…

The call relationship between child and parent Views is as follows:

The specific implementation

We can discuss the specific implementation in two cases:

  • Situation 1: When sliding RecyclerView, how to associate the floating layer to roll together?

  • Case 2: When sliding floating layer, how to associate RecyclerView to roll together?

Is a

Based on the above principle and call flow, let’s take a look at the implementation scheme of the first case:

When the RecyclerView slides to the end and continues to slide up and down, call setSlideHeight to change the Behavior state. SetSlideHeight calls the requestLayout of the child View to trigger the onLayoutChild callback. We change the position of the child View in onLayoutChild by doing the following:

addOnScrollListener(object : OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) totalDy += dy if (! scrollByOutsideView) { bottomSheetBehavior? .let {// omit some code // calculate slide offset val slideHeight = measuredHeight - ((RecyclerView high-totaldy) + it.peekheight // indicate slide bottom if (isPlaceholderViewVisible) {this.slideOffset = parentHeight - slideHeight childView.requestLayout() } } } scrollByOutsideView = false } })Copy the code
@Override public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, Int layoutDirection) {// omit some code... If (slideByOutsideView) {/ / said NestedScrollView sliding to the end, and continue to slide up state ViewCompat. OffsetTopAndBottom (child, slideOffset); dispatchOnSlide(slideOffset); } else {// omit some code... } return true; }Copy the code

Associate child View scroll key code is ViewCompat offsetTopAndBottom (child, slideOffset); Calculating the sliding distance is the most important step in using this method. The slideOffset refers to the position to which the subview should slide. The calculation method of the sliding distance is as follows:

Int slideHeight = measuredHeight - (RecyclerView actual height - scrollY()) + measuredHeight; SlideOffset = parentHeight - slideHeight;Copy the code

Case 2

Let’s take a look at the implementation of the second case:

When sliding discusses the floating layer (the child View of CoordinatorLayout), related RecyclerView sliding together, the basic principle of this step is as follows:

  • When sliding the float, dispatchNestedPreScroll is called in the MOVE event corresponding to onTouchEvent

  • Call the parent View’s onNestedPreScroll method, where you can get the parent View slide distance distanceY

  • Call recyclerView. scrollBy(0, distanceY) method to achieve linkage

When sliding discusses the float, the child View calls dispatchNestedPreScroll, which calls the Behavior’s onNestedPreScroll method, and in onNestedPreScroll we get the slide distance, Then call recyclerView. scrollBy method to achieve associated rolling, the core code is as follows:

@override public void onNestedPreScroll() {Override public void onNestedPreScroll() { int currentTop = child.getTop(); int newTop = currentTop - dy; If (dy > 0) {// Scroll up if (newTop < getExpandedOffset()) {consumed[1] = CurrentTop-getExpandeDoffSet (); ViewCompat.offsetTopAndBottom(child, -consumed[1]); // RecyclerView. scrollBy(0, Consumed [1]); setStateInternal(STATE_EXPANDED); } else { consumed[1] = dy; ViewCompat.offsetTopAndBottom(child, -dy); Recyclerview. scrollBy(0, dy); setStateInternal(STATE_DRAGGING); }} else if (dy < 0) {// Scroll down if (! target.canScrollVertically(-1) || (slideByOutsideView && state == STATE_DRAGGING)) { if (newTop <= collapsedOffset || hideable) { consumed[1] = dy; ViewCompat.offsetTopAndBottom(child, -dy); Recyclerview. scrollBy(0, dy); setStateInternal(STATE_DRAGGING); } else { consumed[1] = currentTop - collapsedOffset; ViewCompat.offsetTopAndBottom(child, -consumed[1]); // RecyclerView. scrollBy(0, Consumed [1]); setStateInternal(STATE_COLLAPSED); }}}}Copy the code

summary

Content Review:

  • Effect demonstration and overall structure overview

  • Discusses the implementation of floating layer dragging up and down freely

  • Behavior and sliding nesting mechanism

  • RecyclerView and CoordinatorLayout associated sliding implementation

In fact, the use of Behavior is extremely flexible and powerful, and many complex effects can be achieved. What is used in this paper is only part of the function of Behavior. However, no matter how complex the situation is, as long as we first understand its principle, we can quickly understand its implementation mode. When the path is long and obstructed, it will come.

Demo address: github.com/liuyak/Nest…

One more thing

Snowball business is developing by leaps and bounds, and the engineer team is looking forward to joining us. If you are interested in “being the premier online wealth management platform for Chinese people”, we hope you can join us. Click “Read the article” to check out the hot jobs.

Hot positions: Big front-end architect, Android/iOS/FE technical expert, recommendation algorithm engineer, Java development engineer.

The resources

[1]

Android custom behaviors: www.jianshu.com/p/b987fad8f…

[2]

BottomSheetBehavior of use: www.jianshu.com/p/99c6813e5…

[3]

Nested sliding mechanism: www.jianshu.com/p/cb3779d36…

[4]

The use of BottomSheetBehavior: developer. The android. Google. Cn/reference/c…