Android nested sliding mechanism NestedScrolling

Android nested sliding mechanism NestedScrolling

I was going to write this article on Jianshu, but because the page cannot support both rich text and markdown at the same time, I decided to give up Jianshu after seeing the cool and pleasing effects in Gemini's article. Reading articles is boring, and if there is no beautiful effect, wouldn't I have to fall asleep while reading? The Internet has given us so many choices, so I will definitely choose the best experience.

The specific effects can be compared:

Speaking of Gemini, I also came across it these two days when I was learning about NestedScrolling. I took a quick look at the information and article views. Great! My great god!

Okay, that’s all for the previous part, let’s get to the point, NestedScrolling.

Before I learned about NestedScrolling, I read some blogs, including Gemini's segmentfault. I didn't pay much attention when I read it at that time. Later I found that this blog was the clearest introduction to NestedScrolling. As a punishment or as a worship, I manually typed out the blog that I could have CVed over, and by the way, added some of my own additional understanding.

Thanks again Gemini

After Android released the Lillipop version, Google provided the NestedScrolling mechanism for Android's sliding mechanism for a better user experience.

Where can the characteristics of NestedScrolling be reflected?

For example, if you use a Toolbar and a ScrollView below, scroll up to hide the Toolbar, and scroll down to show the Toolbar. Logically, this is a NestedScrolling - because in the process of scrolling the View including the entire Toolbar, you also nested and scrolled the ScrollView inside.

As shown in the figure:

Before this, we know that Android has its own mechanism for distributing touch events. There are mainly three functions:

dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent.

This distribution mechanism has a loophole:

If the child view gets the opportunity to handle the touch event, the parent view will no longer have the opportunity to handle the touch event until the next finger triggers.

That is to say, when we slide the child view, if the child view does not need to handle the sliding event, it can only discard the touch event and will not pass it to the parent view for processing.

But Google's new NestedScrolling mechanism solves this problem very well.

There are four main classes in NestedScrolling that need attention:

  • NestedScrollingChild
  • NestedScrollingChildHelper
  • NestedScrollingParent
  • NestedScrollingParentHelper

The above four classes are all provided in the support-v4 package. Some Views in Lollipop implement NestedScrollingChild or NestedScrollingParent by default.

In the v4 package, NestedScrollView implements both NestedScrollingChild and NestedScrollingParent.

Generally, it is enough to implement NestedScrollingChild, and the parent View can use the CoordinatorLayout that implements NestedScrollingParent provided by support-design.

  1. @Override
  2. public void setNestedScrollingEnabled(boolean enabled) {
  3. super.setNestedScrollingEnabled(enabled);
  4. mChildHelper.setNestedScrollingEnabled(enabled);
  5. }
  6.  
  7. @Override
  8. public boolean isNestedScrollingEnabled() {
  9. return mChildHelper.isNestedScrollingEnabled();
  10. }
  11.  
  12. @Override
  13. public boolean startNestedScroll( int axes) {
  14. return mChildHelper.startNestedScroll(axes);
  15. }
  16.  
  17. @Override
  18. public void stopNestedScroll() {
  19. mChildHelper.stopNestedScroll();
  20. }
  21.  
  22. @Override
  23. public boolean hasNestedScrollingParent() {
  24. return mChildHelper.hasNestedScrollingParent();
  25. }
  26.  
  27. @Override
  28. public boolean dispatchNestedScroll( int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int [] offsetInWindow) {
  29. return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
  30. }
  31.  
  32. @Override
  33. public boolean dispatchNestedPreScroll( int dx, int dy, int [] consumed, int [] offsetInWindow) {
  34. return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
  35. }
  36.  
  37. @Override
  38. public boolean dispatchNestedFling( float velocityX, float velocityY, boolean consumed) {
  39. return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
  40. }
  41.  
  42. @Override
  43. public boolean dispatchNestedPreFling( float velocityX, float velocityY) {
  44. return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
  45. }

Simple logic can achieve nested sliding.

The above interfaces are all called by the business logic. How is NestedScrollingChildHelper implemented? Let's take a look at the startNestedScroll method first.

  1. /**
  2. * Start a new nested scroll   for this view .
  3. *
  4. * <p>This is a delegate method. Call it from your {@link android. view . View   View } subclass
  5. * method/{@link NestedScrollingChild} interface method with the same signature to implement
  6. * the standard policy.</p>
  7. *
  8. * @param axes Supported nested scroll axes.
  9. * See {@link NestedScrollingChild#startNestedScroll( int )}.
  10. * @return   true if a cooperating parent view was found and nested scrolling started successfully
  11. */
  12. public boolean startNestedScroll( int axes) {
  13. if (hasNestedScrollingParent()) {
  14. // Already in progress
  15. return   true ;
  16. }
  17. if (isNestedScrollingEnabled()) {
  18. ViewParent p = mView.getParent();
  19. View child = mView;
  20. while (p != null ) {
  21. if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
  22. mNestedScrollingParent = p;
  23. ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
  24. return   true ;
  25. }
  26. if (p instanceof View ) {
  27. child = ( View ) p;
  28. }
  29. p = p.getParent();
  30. }
  31. }
  32. return   false ;
  33. }

You can see here are some methods that help you implement interaction with NestedScrollingParent.

ViewParentCompat is a compatibility class that interacts with the parent View. It determines the API version. If it is on Lollipop, it calls the method that comes with View. Otherwise, if NestedScrollingParent is implemented, it calls the method that implements the interface.

The interaction process between child View and parent View is as follows:

1. startNestedScroll

First, the child View needs to start the whole process (triggering a touch event by sliding the screen), and find and notify the onStartNestedScroll and onNestedScrollAccepted methods in the parent View that implements NestedScrollingParent through NestedScrollingChildHelper.

2. dispatchNestedPreScroll

In the onIterceptTouchEvent and onTouch of the child View (usually in the MontionEvent.ACTION_MOVE event), call this method to notify the parent View of the sliding distance. The third and fourth parameters of this method return the scroll length consumed by the parent View and the window offset of the child View. If the scroll is not consumed, the child View handles the remaining distance. Since the window is moved, if the finger's initial position is recorded, the offset needs to be calculated based on the fourth parameter offsetInWindow to ensure that the calculation of the next touch event is correct.

If the parent View accepts the scroll parameter and consumes part of it, the function returns true, otherwise it returns false. This function is usually called before the child View processes the Scroll.

3. dispatchNestedScroll

Report the scrolling status to the parent View, including the values ​​consumed and not consumed by the child View.

If the parent View accepts the scroll parameters, the function returns true if partial consumption is done, otherwise it returns false.

This function is usually called after the child View processes Scroll.

4. stopNestedScroll

End the entire nested sliding process.

The correspondence between NestedScrollingChild and NestedScrollingParent in the process is as follows:

NestedScrollingChildImpl NestedScrollingParentImpl
onStartNestedScroll onStartNestedScroll , onNestedScrollAccepted
dispatchNestedPreScroll onNestedPreScroll
dispatchNestedScroll onNestedScroll
stopNestedScroll onStopNestedScroll

Generally, the child View initiates the call and the parent View accepts the callback.

You need to pay attention to the consumed parameter in dispatchNestedPreScroll:

  1. public boolean dispatchNestedPreScroll( int dx, int dy, int [] consumed, int [] offsetInWindow);

This parameter is an int array with a length of 2. The first element is the scrolling distance consumed by the parent View in the x-axis direction, and the second element is the scrolling distance consumed by the parent View in the y-axis direction. If both values ​​are not 0, it means that the parent View has consumed the scrolling distance, and the child View's scrolling distance needs to be corrected. Because of this parameter, the idea of ​​handling scrolling events is clearer and will not be confused by a bunch of scrolling parameters as before.

A brief flowchart of NestedScrolling that I understand (excluding the logic of Fling events and return values):

<<:  Android quickly implements WeChat payment

>>:  10 Key Steps to Turn Your Mobile App Idea into Reality

Recommend

How to formulate an event operation strategy!

All operating systems and organizations are only ...

Although the iPhone 6 is popular, Android sells better

In today's smartphone market, looking at mark...

What is a leap year? Why is there an extra day every 4 years?

We usually celebrate our birthday once a year. Fo...

Inventory: Five regular and long-term side projects that can make money at home

Today’s content is a bit too much, the full text ...

iOS 13.5.5 code suggests Apple is developing a service package

According to foreign media reports, there are rum...

How can a product achieve viral popularity?

Internet screen-sweeping phenomena emerge in an e...

First UI: A great choice for efficient cross-platform mobile development

introduction In the field of mobile application d...

While flying, the window disappeared and the captain was sucked out...

At 7:20 in the morning, the plane took off as usu...

“Meituan Takeout” product analysis report!

Take-out has become a must-have for urbanites, sa...

In a few thousand days, will humans enter the intelligent age?

[Editor's Note] Currently, general artificial...

Ten Thousand Leagues Under the Sea

For reprinting, business cooperation, please scan...