Preface: not tears is youth, nor recklessly blood is young. If you do not forget why you started, you will always be young. How many people treat indulgence as blood, and laugh at precocity and self-discipline as banality. Before much time has passed, they are physically and mentally drained of their youth.
An overview,
The last article analyzed the sliding principle and drawing process of RecyclerView. The sliding principle is responded by onTouchEvent() touch event. Finally, through traversing all sub-views, Each child View calls the offsetTopAndBottom() or offsetLeftAndRight() methods of the underlying View to slide. Different is that RecyclerView uses nested sliding mechanism, sliding events will be notified to support nested sliding parent View to make a decision first. So what is nested sliding? How does RecyclerView handle nested sliding? The following uses RecyclerView to cooperate with the use of CoordinatorLayout and Behavior to illustrate the nesting sliding mechanism.
What is nested sliding?
Let’s take a look at a RecyclerView sliding effect:
This is the effect of nested sliding. When a View generates a nested sliding event, it will first notify its parent View and ask whether the parent View handles the event. If it handles the event, the child View does not handle it. It can be seen that when sliding RecyclerView from bottom to top, the sliding event of the head is processed first, and then the sliding event of RecyclerView itself is processed. When sliding down from top to top, the parent View is also asked whether the sliding event is processed. If not, the sliding event is processed by RecyclerView itself.
<? xml version="1.0" encoding="utf-8"? > <androidx.coordinatorlayout.widget.CoordinatorLayout 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"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="#FF5722"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/flower"
android:scaleType="centerCrop"
app:layout_scrollFlags="scroll|enterAlwaysCollapsed" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="40dp"
app:layout_collapseMode="pin"
app:title="Secondary headings" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Copy the code
This kind of lofty visual interaction effect is called “CoordinatorLayout”, and the core class to achieve this effect is a CoordinatorLayout, which follows the Material Design style and combines AppbarLayout, CollapsingToolbarLayout etc CollapsingToolbarLayout can produce a variety of cool suspension effects.
Second, CoordinatorLayout and Behavior
If you look at the XML layout, it’s done, but do you have a lot of questions about what CoordinatorLayout is? What does it do? What does AppBarLayout do? CollapsingToolbarLayout The above layout controls in addition to ImageView, Toolbar, RecyclerView these controls, the other I basically do not know.
This is a screenshot of CoordinatorLayout, it’s a super FrameLayout, notice it inherits from the ViewGroup, it doesn’t inherit from FrameLayout, and then it implements the NestedScrollingParent interface.
CoordinatorLayout can act as a container for specific interactions with one or more child views. By specifying Behaviors as child views, you can provide many different interactions within a single parent view that can interact with each other.
Behavior is an interactive plug-in for a CoordinatorLayout subview. A Behavior implements an interaction that one or more users can have on the subview. These interactions may include dragging, sliding, swinging, or any other gesture.
The plug-in means that if a child View needs some kind of interaction, it needs to load the corresponding Behavior, otherwise it is not interactive. Behavior is an abstract class, and its implementation classes are all designed to allow users to drag, slide, quickly swipe and other gesture operations on a View. If you need other interactions, you need to customize behaviors.
But have we learned the true meaning of Behavior? What exactly does it do?
As I mentioned earlier, CoordinatorLayout can define interactions with its child views or interactions between certain child views. Let’s look at the source of Behavior:
public static abstract class Behavior<V extends View> {
public Behavior(a) {}
public Behavior(Context context, AttributeSet attrs) {}
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
return false;
}
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
return false;
}
public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {}public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
return false;
}
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
// Do nothing
}
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int type) {
// Do nothing
}
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
// Do nothing
}
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx,
int dy, int[] consumed, int type) {
// Do nothing
}
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY, boolean consumed) {
return false;
}
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {
return false; }}Copy the code
There are only two general purposes for our custom Behavior. One is to perform corresponding operations according to the position changes of some dependent views. The other is to respond to the sliding events of some components in CoordinatorLayout. Let’s look at the first case:
2.1 Dependency between two Views
If a View needs to depend on another View, you may need to operate the following API:
Determines whether the provided subview has another specific sibling view as a layout dependency
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) { return false; }
// This method is called when the dependent View changes in size or position outside the standard layout flow.
//Behavior Can use this method to update the subview, returning true if the size or position of the subview changes.
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) { return false; }
// The response subview is removed from the dependent View
public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {}
Copy the code
LayoutDependsOn () specifies whether a View is dependent on another View. Child (dependency) returns true if the layout of child (dependency) is dependent on the layout of dependency. The reverse is not true. Of course you can copy this method to type a dependency and then decide whether to depend on it. OnDependentViewChanged () and onDependentViewRemoved() will only be executed if layoutDependsOn() returns true.
The onDependentViewChanged() method of the Behavior is called when the layout of the dependent View is changed to update the responding child View as appropriate. Returns true if the Behavior changes the position and size of the child, and false by default.
OnDependentViewRemoved () Response The child is removed from the property view. This method is called after removing dependency from parent, and the Behavior can use this method to update the child view appropriately in response.
But that’s a little abstract, who depends on whom? What is dependency?
DependencyView: Select a View that responds to screen drag.
Its code is simple, inherited from TextView, and moves itself based on the touch event onTouchEvent().
public class DependencyView extends AppCompatTextView {
private int mTouchSlop;
private float mLastY;
private float mLastX;
public DependencyView(Context context) {
this(context, null);
}
public DependencyView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public DependencyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setClickable(true);
// The touch distance in pixels may drift before the user scrolls
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:/ / press
mLastX = event.getX();
mLastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:/ / move
float moveX = event.getX() - mLastX;
float moveY = event.getY() - mLastY;
if (Math.abs(moveX) > mTouchSlop || Math.abs(moveY) > mTouchSlop) {
ViewCompat.offsetLeftAndRight(this, (int) moveX);
ViewCompat.offsetTopAndBottom(this, (int) moveY);
mLastX = event.getX();
mLastY = event.getY();
}
break;
case MotionEvent.ACTION_UP:/ / lift
mLastX = event.getX();
mLastY = event.getY();
break;
}
return true; }}Copy the code
And then you implement a Behavior that dominates a View, that’s dependent on the View that it dominates. Here we make the dependent View display below the dependent View, no matter how the position of the dependent View changes, the dependent View changes with:
public class MyBehavior extends CoordinatorLayout.Behavior<View> {
private static final String TAG = "MyBehavior";
public MyBehavior(a) {}public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
Determines whether the provided subview has another specific sibling view as a layout dependency
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
return dependency instanceof DependencyView;
}
// This method is called when the dependent View changes in size or position outside the standard layout flow.
//Behavior Can use this method to update the subview, returning true if the size or position of the subview changes.
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
int bottom = dependency.getBottom();
child.setY(bottom );
child.setX(dependency.getLeft());
Log.d(TAG, "onDependentViewChanged: " + dependency);
return true; }}Copy the code
DependencyView () : layoutDependency () : DependencyView () : layoutDependsOn() : layoutDependsOn() : layoutDependsOn() : layoutDependsOn() : layoutDependsOn() : layoutDependsOn() Then onDependentViewChanged() obtains the dependency position parameter to set the child position parameter, so that the child position changes with the dependency position change.
To verify MyBehavior, we set MyBehavior to the ImageView in the layout file and observe how it behaves:
<? xml version="1.0" encoding="utf-8"? > <androidx.coordinatorlayout.widget.CoordinatorLayout 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"
android:fitsSystemWindows="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_head"
app:layout_behavior="com.antiphon.recyclerviewdemo.weight.MyBehavior" />
<com.antiphon.recyclerviewdemo.weight.DependencyView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/colorAccent"
android:padding="4dp"
android:text="DependencyView"
android:textColor="#fff"
android:textSize="18sp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Copy the code
Note: The outermost layer of the layout needs to be CoordinatorLayout as the parent, which looks like this:
We can see that when we drag the DependencyView, the ImageView moves with the DependencyView. Of course, this dependence is not only one-to-one, but also one-to-many or many-to-many.
Let’s change the code in MyBehavior to display it above the Dependency if Child is a TextView, below it otherwise:
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
/ / the child coordinates
float childX = child.getX();
float childY = child.getY();
//dependency top bottom coordinates
int dependencyTop = dependency.getTop();
int dependencyBottom = dependency.getBottom();
childX = dependency.getX();
if (child instanceof TextView) {// Displays above dependency if it is TextView, below otherwise
childY = dependencyTop + child.getHeight();
} else {
childY = dependencyBottom;
}
child.setX(childX);
child.setY(childY);
return true;
Copy the code
Let’s add another TextView to our XML layout file and set MyBehavior:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_head"
app:layout_behavior="com.antiphon.recyclerviewdemo.weight.MyBehavior" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="helloWord"
android:textColor="# 000"
android:textSize="14sp"
app:layout_behavior="com.antiphon.recyclerviewdemo.weight.MyBehavior" />
Copy the code
The effect is as follows:
Here we know how to deal with the size and position of the dependent object in Behavior, and then we will deal with the sliding operations.
2.2 Behavior Response to sliding Events
The sliding controls that we usually touch are ScrollView and RecyclerView, can you slide for CoordinatorLayout? How, if at all, who slides in response to whom.
The sliding code for Behavior is as follows:
Behavior.java
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
return false;
}
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
// Do nothing
}
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int type) {
// Do nothing
}
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
// Do nothing
}
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx,
int dy, int[] consumed, int type) {
// Do nothing
}
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY, boolean consumed) {
return false;
}
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {
return false;
}
Copy the code
To observe the Behavior, I added log to the relevant slide method:
Behavior.java
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
Log.e(TAG, "onStartNestedScroll:axes == " + axes);
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
Log.e(TAG, "onNestedScrollAccepted:axes == " + axes + " | type == " + type);
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes, type);
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
Log.e(TAG, "onNestedScrollAccepted:dxConsumed == " + dxConsumed + " | dyConsumed == "
+ dyConsumed + " | dxUnconsumed == " + dxUnconsumed + " | dyUnconsumed == " + dyUnconsumed);
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx,
int dy, int[] consumed, int type) {
Log.e(TAG, "onNestedScrollAccepted:dx == " + dx + " | dy == " + dy + " | type == " + type);
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int type) {
Log.e(TAG, "onNestedScrollAccepted:type == " + type);
super.onStopNestedScroll(coordinatorLayout, child, target, type);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY, boolean consumed) {
Log.e(TAG, "onNestedScrollAccepted:velocityX == " + velocityX + " | velocityY == "
+ velocityY + " | consumed == " + consumed);
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {
Log.e(TAG, "onNestedScrollAccepted:velocityX == " + velocityX + " | velocityY == " + velocityY);
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
Copy the code
Then I used the mouse to swipe the CoordinatorLayout on the simulator to create some slide events, see if the slide function API related to MyBehavior can be triggered, and then observe the log:
As you can see, no functions are fired. So let’s look at the API for nested sliding events:
Behavior.java
/** * If the descendant of the CoordinatorLayout is called when the View tries to initiate a nested slide * Any Behavior associated with any direct child of the CoordinatorLayout can respond to this event and return true, * To indicate that the CoordinatorLayout should be the parent View of the slide nesting slide and can only receive subsequent nested slide events if it returns true. * /
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
return false;
}
Copy the code
The comment states that when a descendant of the CoordinatorLayout wants to trigger a nested scroll event, this method is called and the subsequent nested scroll event will only respond if onStartNestedScroll() returns true. The subsequent response functions refer to these functions:
Behavior.java
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {}
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int type) {}
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {}
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx,
int dy, int[] consumed, int type) {}
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY, boolean consumed) {}
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {}
Copy the code
So, let’s start with onStartNestedScroll() to find out where to call this method:
You can see that MyBehavior’s onStartNestedScroll() is called, which is called from the CoordinatorLayout:
CoordinatorLayout.java
@Override
public boolean onStartNestedScroll(View child, View target, int axes, int type) {
boolean handled = false;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = getChildAt(i);
if (view.getVisibility() == View.GONE) {
// If it's GONE, don't dispatch
continue;
}
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
final Behavior viewBehavior = lp.getBehavior();
if(viewBehavior ! =null) {
final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child,
target, axes, type);
handled |= accepted;
lp.setNestedScrollAccepted(type, accepted);
} else {
lp.setNestedScrollAccepted(type, false); }}return handled;
}
Copy the code
This is a method in CoordinatorLayout, you get the child View under the CoordinatorLayout, and then you get the Behavior in the child View, and then you call the onStartNestedScroll() method of the Behavior.
If you go further, who called the CoordinatorLayoutonStartNestedScroll()
?
Keep tracking:
You can see that there are multiple places to call it, but it all comes down to View or ViewParentCompat. Here’s a View example:
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {// Whether to enable nesting sliding
ViewParent p = getParent();
View child = this;
while(p ! =null) {
try {
if (p.onStartNestedScroll(child, this, axes)) {
mNestedScrollingParent = p;
p.onNestedScrollAccepted(child, this, axes);
return true; }}if (p instanceofView) { child = (View) p; } p = p.getParent(); }}return false;
}
Copy the code
When a View triggers onStartNestedScroll(), if nested scroll is applied, the Parent is fetched and the Parent’s onStartNestedScroll() method is called through the while loop. Because CoordinatorLayout is a ViewGroup, it’s a ViewParent object, and if a parent View of CoordinatorLayout triggers the onStartNestedScroll() method, if it does, The CoordinatorLayout’s onStartNestedScroll() method is called, and then the Behavior’s onStartNestedScroll() method is called.
When isNestedScrollingEnabled() = true, its ViewParent’s onStartNestedScroll() will be triggered to determine whether it can nest slides. If true, it will be used as a subview of nested slides. Can pass setNestedScrollingEnabled (Boolean enabled) to set up the View whether has the capacity to nested sliding.
2.3 Use TextView to generate nested slide events
If a View in line with the nested sliding event, namely through setNestedScrollingEnabled (true), and then call it onStartNestedScroll () method, which can generate nested sliding event is the theory. Let’s try it out by adding a normal TextView to the layout and setting the click event.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mTv_nested_scroll.setNestedScrollingEnabled(true);
mTv_nested_scroll.startNestedScroll(View.SCROLL_AXIS_HORIZONTAL);
}
Copy the code
Previously, the corresponding nested slide method in MyBehavior printed a log, so if a nested slide event occurs in CoordinatorLayout, the log has output:
If you see events that match nested slides in a View, call its onStartNestedScroll() method. Android version 21(LOLLIPOP, 5.0) and above can only call View’s nested sliding API. In fact, the system is compatible with:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
ViewCompat.setNestedScrollingEnabled(mTv_nested_scroll, true);
ViewCompat.startNestedScroll(mTv_nested_scroll, ViewCompat.SCROLL_AXIS_HORIZONTAL);
}
Copy the code
We know that more than 5.0 version View already sliding with nested functions and related attributes, can be done according to ViewCompat this class low version of the compatible operation, continue to follow ViewCompat. StartNestedScroll () :
public static void setNestedScrollingEnabled(@NonNull View view, boolean enabled) {
if (Build.VERSION.SDK_INT >= 21) {
view.setNestedScrollingEnabled(enabled);
} else {
if (view instanceofNestedScrollingChild) { ((NestedScrollingChild) view).setNestedScrollingEnabled(enabled); }}}public static boolean startNestedScroll(@NonNull View view, @ScrollAxis int axes) {
if (Build.VERSION.SDK_INT >= 21) {
return view.startNestedScroll(axes);
}
if (view instanceof NestedScrollingChild) {
return ((NestedScrollingChild) view).startNestedScroll(axes);
}
return false;
}
Copy the code
StartNestedScroll (); otherwise, it will determine whether the view is an instance of NestedScrollingChild. If so, call the startNestedScroll() method of NestedScrollingChild, which is an interface.
So above 5.0 we can view.startNestedScroll(), but below 5.0, if a view wants to initiate nested scroll events, you have to make sure that the view implements NestedScrollingChild.
2.4 Do you want to trigger events? Are you Nesteds RollingChild?
Let’s look at NestedScrollingChild:
public interface NestedScrollingChild {
/** * Enable or disable nesting sliding for this View. * Note: If this property is set to true, the View is allowed to initiate nested slides using compatible parent views in the current hierarchy. * This method has no effect if the View does not have a nested sliding task. * /
void setNestedScrollingEnabled(boolean enabled);
/** * Returns true if nested sliding is enabled for this View. * If nested sliding is enabled and the View class implementation supports nested sliding, the View will act as a nested slide child when applicable, * forwarding data about the ongoing scroll operation to a parent View that is compatible and collaboratively nested sliding. * /
boolean isNestedScrollingEnabled(a);
/** * Begins a sliding operation that can nest a scroll in a given direction (vertical or horizontal). * Returns true to handle nested scroll events passed by the parent View. NestedScrollingParent's onStartNestedScroll() * is called@paramAxes indicate direction */
boolean startNestedScroll(int axes);
/** * stops the nesting slide in progress, the nesting slide ends */
void stopNestedScroll(a);
/** * Returns true */ if the View has a nested sliding parent
boolean hasNestedScrollingParent(a);
/** * Scheduling an ongoing slide step * is called after the View that created the nested slide has finished sliding. This method distributes the remaining unconsumed distance into the View *@paramDxConsumed indicates how far this View consumes on the x axis *@paramDyConsumed represents the distance the View consumes on the Y-axis *@paramDxUnconsumed indicates the unconsumed distance * of the View on the X axis@paramDyUnconsumed represents the unconsumed distance * of the View in the Y-axis@paramOffsetInWindow indicates how far the View slides across the screen, including the distance on the X axis and the distance on the y axis. * Returns true if the parent thread consumed some or all of the sliding, false if it did not consume */
boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow);
/** * Schedule a step of the nested slide before the View consumes any part of the slide. Provides an opportunity for a parent View in a nested slide to use some or all of the slides before the child View uses the slides. * before dispatchNestedScroll is called, so the distance is created, but the View hasn't slid yet. Report the sliding distance to the parent View to see whether the parent View has priority to consume this part of the distance *@paramDx the distance generated along the x axis *@paramThe distance produced on the Y-axis dy *@paramA value of consumed index 0 represents the distance consumed by the parent View on the X axis, and a value of consumed index 1 represents the distance consumed by the parent View on the y axis *@param* Returns true if the parent thread consumes some or all of the sliding distance, false if the parent View does not consume */
boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
/** * If the parent View does not do any inertia sliding, the child View calls this method to report that the parent View is holding *@paramVelocityX The velocity along the x axis *@paramVelocityY the velocity of the y axis *@paramConsumed True indicates that the child View is acting on the fling event,false indicates that there is no action * Returns true indicates that the parent View of the nested slide consumed or otherwise reacted to the inertia slide,false does not */
boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed);
/** * This method is used to ask the parent View if the fling has been acted on before the child has acted on it */
boolean dispatchNestedPreFling(float velocityX, float velocityY);
}
Copy the code
This interface should be implemented by a subclass of View and hopefully support the dispatch of nested slides to the collaborating parent View(ViewGroup). To implement the interface class should create NestedScrollingChildHelper and entrust any View it. Views that invoke nested slides should always be executed from the ViewCompat, ViewGroupCompat, ViewParentCompat compatibility pad static methods, ensuring interoperability of nested slides for Android 5.0 and above.
Using the AndroidStudio shortcut Ctrl+H, you can see the current implementer of NestedScrollingChild:
Implementors of NestedScrollingChild have two SwipleRefreshLayout and NestedScrollingChild2, And NestedScrollingChild2 also has two implementors RecyclerView and NestedScrollingChild3, so RecyclerView is also the indirect implementor of NestedScrollingChild. The implementer of NestedScrollingChild3 is RecyclerView and NestedScrollView.
So come here, NestedScrollingChild can be said to have three implementation classes, RecyclerView, NestedScrollView, SwipleRefreshLayout, the above three controls we all know, are their own sliding controls.
2.5 How does Behavior respond to sliding events?
We verified the dependent interaction between children above, so how does the Behavior respond to the sliding event? We need to find a View that triggers nested slides, and we add RecyclerView to the CoordinatorLayout, and the RecyclerView, which creates nested slides:
<? xml version="1.0" encoding="utf-8"? > <androidx.coordinatorlayout.widget.CoordinatorLayout 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"
android:fitsSystemWindows="true">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_marginTop="10dp"
android:src="@mipmap/ic_gesture_down"
app:layout_behavior="com.antiphon.recyclerviewdemo.weight.MyBehavior" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="50dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Copy the code
Behavior needs to be processed for its own business logic. When we slide RecyclerView content, MyBehavior specifies the corresponding displacement of the associated ImageView, mainly in the vertical direction. In MyBehavior onStartNestedScroll() do something special:
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild,
View target, int axes, int type) {
Log.e(TAG, "onStartNestedScroll:axes == " + axes);
return child instanceof ImageView && axes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
Copy the code
As long as the child is ImageView and the slide direction is vertical, return true in response to the subsequent nested slide event, operating on the child for the displacement returned by the slide event:
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx,
int dy, int[] consumed, int type) {
Log.e(TAG, "onNestedScrollAccepted:dx == " + dx + " | dy == " + dy + " | type == " + type);
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
ViewCompat.offsetTopAndBottom(child, dy);// Move the child vertically
}
Copy the code
Copy the onNestedPreScroll() method to move the child vertically by reading the dy value. Dx is the horizontal displacement of sliding, dy is the vertical displacement of sliding, which is called before the sliding event slides onNestedScroll() and passes the consumed distance into the Consumed array. OnNestedScroll () is called during a slide event, and its parameters include displacement information and the number of dispositions that have been consumed in onNestedPreScroll(). Just implement the onNestedPreScroll() method.
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
return false;
// return dependency instanceof DependencyView;
}
Copy the code
Apply some of the action that MyBehavior did to the DependencyView. The effect is as follows:
There are two more apis related to inertial sliding:
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY, boolean consumed) {
return false;
}
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {
return false;
}
Copy the code
The user’s finger does not stop when the user is sliding. The user’s finger does not stop when the user is sliding. If the Fling() returns true, the recycle View will not allow the child to Fling itself. If the Fling() returns true, the Recycle View will not allow the child to Fling itself. Because the Child consumes the Fling event.
Let’s verify that when the MyBehavior responds to the fling event and slides down, the ImageView expands and slides up, the ImageView shrinks.
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target,
float velocityX, float velocityY) {
Log.e(TAG, "onNestedScrollAccepted:velocityX == " + velocityX + " | velocityY == " + velocityY);
if (velocityY > 0) {// downward inertial sliding
child.animate().scaleX(2f).scaleY(2f).setDuration(2000).start();
} else {// Upward inertial slide
child.animate().scaleX(1f).scaleY(1f).setDuration(2000).start();
}
return false;
// return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
Copy the code
The effect is as follows:
2.6 Summary of Behavior
- 1. Determine the relationship between views in CoordinatorLayout
layoutDependsOn()
Method returns true to indicate dependency, otherwise not; - 2. When the size or position of a dependent dependency changes, the dependent party will obtain it through Behavior and change it in
onDependentViewChanged()
If the size or position of the child in the method changes, return true; - 3. When the View in Behavior is ready for a nested slide in response, it does not need to pass
layoutDependsOn()
To do the dependency binding, just need to be inonStartNestedScroll()
The return value tells ViewParent whether nesting slides are enabled or not, and returns true for subsequent nested slides to respond. - 4. Nested slides include ordinary scroll and inertia slide.
I’ve tried to figure out who can create nested slide events, and it turns out that it needs to be NestedScrollChild, but when NestedScrollChild calls startNestedScroll(), it needs to use the power of its parent View, Only if the parent View’s startNestedScroll() returns true will its subsequent events continue.
View.java
public boolean startNestedScroll(int axes) {
if (hasNestedScrollingParent()) {
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {
ViewParent p = getParent();
View child = this;
while(p ! =null) {
try {
if (p.onStartNestedScroll(child, this, axes)) {
mNestedScrollingParent = p;
p.onNestedScrollAccepted(child, this, axes);
return true; }}if (p instanceofView) { child = (View) p; } p = p.getParent(); }}return false;
}
Copy the code
As you can see, ViewParent plays a very important role in calling back the parent View’s onStartNestedScroll() method:
public interface ViewParent {
public void requestLayout(a);
public ViewParent getParent(a);
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
public void onStopNestedScroll(View target);
public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed);
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
public boolean onNestedPreFling(View target, float velocityX, float velocityY);
}
Copy the code
ViewParent is an interface. The common implementation class is ViewGroup, which provides a nested sliding API. It is added in Android5.0. This provides the View and its parent class with the opportunity to perform the initial configuration of nested sliding. If there is an implementation of this method, the parent class should be called to implement this method:
ViewParentCompat.java
public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
int nestedScrollAxes, int type) {
if (parent instanceof NestedScrollingParent2) {
// First try the NestedScrollingParent2 API
return ((NestedScrollingParent2) parent).onStartNestedScroll(child, target,
nestedScrollAxes, type);
} else if (type == ViewCompat.TYPE_TOUCH) {
// Else if the type is the default (touch), try the NestedScrollingParent API
if (Build.VERSION.SDK_INT >= 21) {/ / 5.0 and above
return parent.onStartNestedScroll(child, target, nestedScrollAxes);
} else if (parent instanceof NestedScrollingParent) {/ / 5.0 below
return((NestedScrollingParent) parent).onStartNestedScroll(child, target, nestedScrollAxes); }}return false;
}
Copy the code
If a ViewParent needs to respond to nested sliding events, it needs to be a NestedScrollingParent object:
public interface NestedScrollingParent {
boolean onStartNestedScroll(View child, View target, int axes);
void onNestedScrollAccepted(View child, View target, int axes);
void onStopNestedScroll(View target);
void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed);
void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
boolean onNestedPreFling(View target, float velocityX, float velocityY);
int getNestedScrollAxes(a);
}
Copy the code
NestedScrollingParent has the following implementation classes:
Because NestedScrollingParent2 is also an implementation class of NestedScrollingParent, CoordinatorLayout indirectly implements NestedScrollingParent, there are four implementation classes: CoordinatorLayout, NestedScrollView, SwipeRefreshLayout, ActionBarOverlayLayout. So CoordinatorLayout can handle nested slide events because it is itself a NestedScrollingParent.
Nested slide flow: In general, the start of a nested slide event, initiated by a NestedScrollingChild, traverses parent up, using parent’s nested slide correlation method to complete the interaction. Note for Android 5.0 or later, the parent interface must be NestedScrollingParent.
Three, RecyclerView nested sliding events
To support nested sliding, the child View must implement the NestedScrollingChild interface and the parent View must implement the NestedScrollingParent interface. RecyclerView realized NestedScrollingChild interface, CoordinatorLayout realized NestedScrollingParent interface.
Warm prompt: in this paper, the source code based on androidx. Recyclerview: recyclerview: 1.2.0 – alpha01
Nested sliding mechanism mainly use interfaces and classes: NestedScrollingChild, NestedScrollingParent, NestedScrollingParentHelper, NestedScrollingChildHelper. Here’s a quick summary:
The name of the class | meaning |
---|---|
NestedScrollingChild | If a View wants to generate a sliding event, the View must implement the NestedScrollingChild interface. As of Android5.0, the View implements this interface, there is no need to implement it manually |
NestedScrollingParent | This interface is typically implemented by a ViewGroup and represents the ability to receive nested sliding events from child views |
NestedScrollingChildHelper | This class is usually used as a helper in views that implement the NestedScrollingChild interface. It is usually used to report nested sliding events generated by child views to the parent View. In other words, a child View wants to produce a nested sliding event to the parent View, then NestedScrollingChildHelper will help us to deal with. |
NestedScrollingParentHelper | Similar to the above, are helper classes that pass events. Usually used in interface realized NestedScrollingParent parent View, if the parent View didn’t want to handle this issue, pass through NestedScrollingParentHelper |
3.1 ACTION_DOWN event
Combined with RecyclerView source code to analyze the principle of nested event transfer, first take a look at the RecyclerView ACTION_DOWN event processing:
RecyclerView.java
case MotionEvent.ACTION_DOWN: {
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
if (canScrollHorizontally) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
}
if (canScrollVertically) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
}
// Start nesting slides
startNestedScroll(nestedScrollAxis, TYPE_TOUCH);
} break;
Copy the code
After initialization of the nestedscrolparametric variable, startNestedScroll() will be called to tell the parent View that the sliding event has started and that you need to do something. How does it pass an event to the parent View?
@Override
public boolean startNestedScroll(int axes, int type) {
return getScrollingChildHelper().startNestedScroll(axes, type);
}
private NestedScrollingChildHelper getScrollingChildHelper(a) {
if (mScrollingChildHelper == null) {
mScrollingChildHelper = new NestedScrollingChildHelper(this);
}
return mScrollingChildHelper;
}
Copy the code
Event is passed through NestedScrollingChildHelper help class instance through NestedScrollingChildHelper call startNestedScroll () :
NestedScrollingChildHelper.java
public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {
if (hasNestedScrollingParent(type)) {// Nesting slide in progress, return true
// Already in progress
return true;
}
if (isNestedScrollingEnabled()) {// Does the parent View support nested sliding
ViewParent p = mView.getParent();
View child = mView;
// The while loop retrieves the parent view to see if nested sliding is supported, and returns true if it is. If not, continue to access the upper View.
while(p ! =null) {
// This calls the parent View's method, which returns true if the parent View runs a nested slide, and then consumes the slide distance passed by the subview.
if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
setNestedScrollingParentForType(type, p);
ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
return true;
}
if (p instanceofView) { child = (View) p; } p = p.getParent(); }}return false;
}
Copy the code
The parent View must implement the NestedScrollingParent interface. The onStartNestedScroll() method returns true. If the parent View cannot handle nested sliding events, it will recursively look up while(). NestedScrollingChildHelper have rely on ViewParentCompat class to help pass events, actually ViewParentCompat also help us to call the parent View onStartNestedScroll () method, The goal is to be compatible with different versions. As mentioned earlier, in Android5.0, the View implements the NestScrollingChild interface, but later versions need to be implemented manually.
3.2 ViewParentCompat
How do events get passed to the parent View? ViewParentCompat plays an important role in system compatibility. How does it ensure system compatibility?
ViewParentCompat.java
public static void onNestedScroll(ViewParent parent, View target, int dxConsumed,
int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type,
@NonNull int[] consumed) {
// If we call a function smaller than NestedScrollingParent3, add the unused distance to the used argument, so that the NestedScrollingChild3 implementation is told that the entire scrolldistance has been used (for a backward comparison).
if (parent instanceof NestedScrollingParent3) {
((NestedScrollingParent3) parent).onNestedScroll(target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type, consumed);
} else {
if (parent instanceof NestedScrollingParent2) {
((NestedScrollingParent2) parent).onNestedScroll(target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, type);
} else if (type == ViewCompat.TYPE_TOUCH) {
// If the type is default (touch), try NestedScrollingParent API
if (Build.VERSION.SDK_INT >= 21) {
parent.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed,
dyUnconsumed);
} else if (parent instanceofNestedScrollingParent) { ((NestedScrollingParent) parent).onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); }}}}Copy the code
NestedScrollingParent3 inherits from NestedScrollingParent2, NestedScrollingParent2 inherits from NestedScrollingParent, If parent implements NestedScrollingParent3, onNestedScroll() in NestedScrollingParent3 is called; If parent implements NestedScrollingParent2, onNestedScroll() in NestedScrollingParent2 is called; If parent implements NestedScrollingParent’s interface, onNestedScroll() in NestedScrollingParent is called; OnNestedScroll () is called on Android5.0 or higher.
This method reports the used and unused portions of the scrolling distance to the ViewParent in response to nested sliding in progress. So if a parent View wants to receive events from the child View, it implements NestedScrollingParent and other related interfaces.
3.3 ACTION_MOVE events
Sliding process is involved in ACTION_DOWN nested NestedScrollingChildHelper initialization, looking for support nested sliding parent View, then in the heart of the ACTION_MOVE sliding distance to the first find the parent View, The parent View will write the consumed sliding distance into the parameter passed, and then RecycleView will know whether the sliding distance is consumed by the parent View, to determine whether the RecycleView sliding update.
RecyclerView.java
case MotionEvent.ACTION_MOVE:{
if (dispatchNestedPreScroll(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
mReusableIntPair, mScrollOffset, TYPE_TOUCH
))
} break;
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
int type) {
return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
type);
}
Copy the code
The dispatchNestedPreScroll() method is called primarily through the ScrollingChildHelper class:
ScrollingChildHelper.java
public boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
@Nullable int[] offsetInWindow, @NestedScrollType int type) {
if (isNestedScrollingEnabled()) {
// If the parent View of the previous nested slide does not match, return directly
final ViewParent parent = getNestedScrollingParentForType(type);
if (parent == null) {
return false;
}
if(dx ! =0|| dy ! =0) {
int startX = 0;
int startY = 0;
// If offsetInWindow is not empty, write the view coordinates into the array
if(offsetInWindow ! =null) {
mView.getLocationInWindow(offsetInWindow);
startX = offsetInWindow[0];
startY = offsetInWindow[1];
}
if (consumed == null) {
consumed = getTempNestedScrollConsumed();
}
consumed[0] = 0;
consumed[1] = 0;
// Calls back to the parent View method to write consumed data to the Consumed array
ViewParentCompat.onNestedPreScroll(parent, mView, dx, dy, consumed, type);
if(offsetInWindow ! =null) {
mView.getLocationInWindow(offsetInWindow);
offsetInWindow[0] -= startX;
offsetInWindow[1] -= startY;
}
// The parent View consumes dy or dx and returns true
return consumed[0] != 0 || consumed[1] != 0;
} else if(offsetInWindow ! =null) {
offsetInWindow[0] = 0;
offsetInWindow[1] = 0; }}return false;
}
Copy the code
3.4 ACTION_UP event
ACTION_UP Determines whether inertia sliding fling() is needed based on the lifting speed of X and Y.
RecyclerView.java
case MotionEvent.ACTION_UP: {
final float yvel = canScrollVertically
? -mVelocityTracker.getYVelocity(mScrollPointerId) : 0;
if(! ((xvel ! =0|| yvel ! =0) && fling((int) xvel, (int) yvel))) {
setScrollState(SCROLL_STATE_IDLE);
}
resetScroll();
} break;
Copy the code
The main method is to fling() :
public boolean fling(int velocityX, int velocityY) {· · · · · ·// If the parent View does not consume the fling
if(! dispatchNestedPreFling(velocityX, velocityY)) {final boolean canScroll = canScrollHorizontal || canScrollVertical;
// Nested inertial sliding
dispatchNestedFling(velocityX, velocityY, canScroll);
if(mOnFlingListener ! =null && mOnFlingListener.onFling(velocityX, velocityY)) {
return true;
}
// If you can still roll, use recyclerView's own mechanism to implement a section of inertial sliding
if (canScroll) {
int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
if (canScrollHorizontal) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
}
if (canScrollVertical) {
nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
}
startNestedScroll(nestedScrollAxis, TYPE_NON_TOUCH);
velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity));
velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity));
// Implement inertial sliding according to the calculated speed
mViewFlinger.fling(velocityX, velocityY);
return true; }}return false;
}
Copy the code
DispatchNestedFling () dispatchNestedFling() dispatchNestedFling() dispatchNestedFling() dispatchNestedFling() dispatchNestedFling() dispatchNestedFling()
NestedScrollingChildHelper.java
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
if (isNestedScrollingEnabled()) {
// Get parent by type
ViewParent parent = getNestedScrollingParentForType(TYPE_TOUCH);
if(parent ! =null) {
returnViewParentCompat.onNestedFling(parent, mView, velocityX, velocityY, consumed); }}return false;
}
Copy the code
Call the onNestedFling() method of ViewParentCompat:
ViewParentCompat.java
public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
float velocityY, boolean consumed) {
if (Build.VERSION.SDK_INT >= 21) {
return parent.onNestedFling(target, velocityX, velocityY, consumed);
} else if (parent instanceof NestedScrollingParent) {
return ((NestedScrollingParent) parent).onNestedFling(target, velocityX, velocityY,
consumed);
}
return false;
}
Copy the code
The parent view’s onNestedFling() method is called if it is above Android5.0, and the parent view’s onNestedFling() method is called if it is below Android5.0. (Related inertia sliding principle of RecyclerView can refer to here)
Draw a diagram to summarize the nested sliding process of RecyclerViewonTouchEvent() :
Simple summary of RecyclerView nested sliding:
- 1. Nested sliding events are passed from child View to parent View, distributed from bottom to top;
- 2. If a view wants to pass nested sliding events, implement the NestedScrollingChild interface and support nested sliding; If the parent View wants to support nested sliding events, it must implement the NestedScrollingParent interface.
Four,
1.CoordinatorLayout is a normal ViewGroup and a super FrameLayout that can interact with child through Behavior;
2. In BehaviorlayoutDepentOn()
Return true or specify the target control in XML to determine the dependency between two Views when the size and position of the dependent party changeonDependentViewChanged()
If the size and position of the actively dependent View are changed, the method returns true;
3.Behavior is a plug-in mechanism that can determine the measurement size, layout position and touch response of CoordinatorLayout corresponding to childView; If there is no Behavior, CoordinatorLayout is no different from FrameLayout.
4. Nested slides are classified into nested scroll and fling, and whether childView accepts responses is determined byonStartNestedScroll()
The return value is determined, generally instartNestedScroll()
Process the corresponding nested Scroll event inonNestedFling()
Processing fling events;
5.RecyclerView can generate nested sliding events because NestedScrollingChild interface is realized. CoordinatorLayout can respond to nested sliding events because NestedScrollingParent interface is realized. Initiated by childView, the interaction is completed by iterating from bottom to top over the parent using the nested sliding correlation method of the parent.
Here is a nested sliding start to thoroughly out, RecyclerView to achieve NestedScrollingChild interface, launched nested sliding (Android5.0 below need setNestedScrollEnable(true)), Parent implements the NestedScrollingParent interface and calls onStartNestedScroll(), And then the CoordinatorLayout calls the onStartNestedScroll() method, the CoordinatorLayout actually calls onStartNestedScroll() in the Behavior, The ViewParent is told if it is interested in nested slides, and if it returns true, the nested slides in the CoordinatorLayout will be responded to. (under Android5.0 parent must implement NestedScrollingParent interface)
Use a picture to get a clearer idea:
The source address
Pay attention and don’t get lost
Well everyone, that’s all for this article. Thank you very much for reading it. I am Suming, thank you for your support and recognition, your praise is the biggest motivation for my creation. Landscape has to meet, we will see you in the next article!
If there are any mistakes in this blog, please comment, thank you very much!
Reference links:
- A detailed analysis of CoordinatorLayout and Behavior