preface

NestedScrolling is a NestedScrolling mechanism introduced by Andorid 5.0. NestedScrollingParent and NestedScrollingChild are used to coordinate scrolling between parent and child views. It’s very convenient for us to deal with nested sliders. By NestedScrolling, we can easily achieve beautiful interaction effects such as the home page of Zhihu and the home page of Qscrolling.

The problem is that NestedScrolling is not friendly to fling results. The child simply throws them to the parent. Fling is handled by either child or parent. When we want a child to handle part of it and a parent to handle the rest of it, it’s a little tiring. The old rule, directly above:

In android 8.0, Google dad is aware of this situation and has released an upgrade version of nestedscrolling parent t2 and NestedScrollingChild2, which handles the fling allocation issue in a friendly way. Can achieve a very silky smooth sliding effect, directly see the picture:

The body of the

NestedScrollingParent and NestedScrollingChild already have a lot of tutorials for you to learn, This article mainly analyzes NestedScrollingParent2 and NestedScrollingChild2;

1. Understand the API first

  • NestedScrollingParent2
Public interface NestedScrollingParent2 extends NestedScrollingParent {/** * * * @param Child nested slide corresponding to the parent class of the startNestedScroll method called by the child control (because nested slide for the parent control is not necessarily one level to find, may pick two levels of the parent control, Child's seniority >=target) * @param Target that subclass of specific nested slides * @param Axes Scrolling directions supported by nested slides * @paramtypeThere are two types of nested swipe,ViewCompat. TYPE_NON_TOUCH fling effect,ViewCompat.TYPE_TOUCH gesture swipereturn trueIndicates that this parent class begins to accept nested slidings, onlytrueTime, */ Boolean onStartNestedScroll(@nonNULL View Child, @nonNULL View target, @ScrollAxis int axes, @NestedScrollType inttype); /** * When onStartNestedScroll returnstrue* * @param Child * @param Target * @param Axes * @param when the parent control accepts nested slidestype
    */
   void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes,
           @NestedScrollType int type); /** * This method is called on the parent control before the child control starts to slide. The parent consumes a portion of the slide distance and passes it to the child in consumed form * before the nested child View slides *, Determine whether the parent view is processed first with the child view (that is, the parent view can be consumed first, * * @param target specifies the subclass of the nested slide * @param dx specifies the distance the horizontal nested slide subview wants to vary * @param dy Specifies the distance the vertical nested slide subview wants to vary dy<0 slide down Dy >0 * @param consumed in the function * @param consumed in the function, Consumed [1] Consumed vertically so that the subview adjusts accordingly * @paramtypeSliding type, */ void onNestedPreScroll(@nonnull View Target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType inttype); /** * In onNestedPreScroll, after the parent control consumes a portion of the distance, the rest is given to the child control again, * after the child control consumes, if there is any left, @param Target (@param dxConsumed) indicates the extent to which a child control in the horizontal direction of a nested slide is consumed @param dxUnconsumed Distance consumed in the horizontal direction of the nested sliding child * @param dyUnconsumed Vertical nested sliding child control unsliding distance (unconsumed distance) * @paramtypeSliding type, */ void onNestedScroll(@nonnull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType inttype); /** * Stop sliding ** @param target * @paramtypeSliding type, */ void onStopNestedScroll(@nonnull View Target, @NestedScrollType inttype);


}
Copy the code
  • NestedScrollingParent2
Public interface NestedScrollingChild2 extends NestedScrollingChild {public interface NestedScrollingChild2 extends NestedScrollingChild {/** * This method is usually provided in onInterceptTouchEvent or onTouch, Notifying the parent method to start sliding * calls both methods onStartNestedScroll onNestedScrollAccepted * * @param Axes * @paramtypeStart the slide with the type thetype of input which cause this scroll event
    * @returnHas a superview and starts to slide, returnstrue*/ Boolean startNestedScroll(@scrolint axes, @nestedscrollType inttype); /** * child control to stop sliding, such as finger lift, inertia slide to end ** @paramtypeVoid stopNestedScroll(@nestedScrollType int)type); /** * Determine if a parent View supports nested sliding */ Boolean hasNestedScrollingParent(@nestedScrollType int)type); * When the sliding distance is consumed by the parent control, the parent control passes the remaining distance to the child control again. * After the child control consumes part of the distance again, it continues to distribute the remaining distance to the parent control, and the parent control determines whether the remaining distance is consumed. * If the four distances consumed are all 0, then there is no god to consume, and it will return directlyfalseOtherwise, the parent control's * onNestedScroll method is called, * @param dxConsumed in RecycleView * * @param dxConsumed in RecycleView dx<0 slide right dx>0 Slide left * @param Dy <0 Downward dy>0 upward (consistent with RecycleView) * @param dxUnconsumed * @param dyUnconsumed (RecycleView) * @param dyUnconsumed (RecycleView)dy<0 Slide down dy>0 Slide up (keep the same as RecycleView) * @param offsetInWindow child control offset in the current window * @returnIf the returntrue*/ Boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxConsumed, int dyConsumed, @Nullable int[] offsetInWindow, @NestedScrollType inttype); /** * The child control notifies the parent control to start sliding, and the parent control will consume the sliding time * In the onInterceptTouchEvent or onTouch of the child View, Call this method to notify the parent View of the distance to slide * eventually call the onNestedPreScroll method on the parent View * * @param dx The distance the children of the horizontal nested slide want to change dx<0 slide right dx>0 slide left (hold the sum RecycleView consistent) * @param dy Specifies the distance that the child wants to change dy<0 slide down dy>0 slide up (stays consistent with the RecycleView) * @param consumed the distance that the parent controls consume, After consuming the parent control, the rest is consumed by the child control. The child control uses consumed to handle the actual sliding distance * @param offsetInWindow the offset of the child control in the current window * @paramtypeFling effect (ViewCompat.TYPE_NON_TOUCH) and gesture (ViewCompatreturn trueIndicates that the parent control has made a sliding consumption and needs to handle the consumed value,falseIndicates that the parent control does not consume the sliding distance, and it can ignore consumed data processing. */ Boolean dispatchNestedPreScroll(int dx, int dy, @nullable int[] consumed, @Nullable int[] offsetInWindow, @NestedScrollType inttype);
}
Copy the code
  • Calling process

I have made a very detailed annotation of the API above, it should not be difficult to understand, combing down, the general process is:

Normally, the event starts with the child’s touch event,

  1. First call the child. StartNestedScroll () method, this method through internal NestedScrollingChildHelper calls and returned to the parent. OnStartNestedScroll () method of result, is true, Call the parent accepts the nested sliding, and the parent onNestedScrollAccepted () method, this began to slide nested;

  2. In slip incident, child by child. DispatchNestedPreScroll () method of the distribution of sliding distance, Will first invoke the parent child. DispatchNestedPreScroll (). The onNestedPreScroll () method, handled by the parent to sliding distance.

  3. After the parent has consumed the remaining distance, it passes the remaining distance to the child, who gets the remaining distance and deals with the remaining distance himself.

  4. If the child controls and untreated, will all the rest of the distance by the child again. DispatchNestedScroll () method call parent. OnNestedScroll () method, the remaining distance to pay a parent to processing

  5. When the slide is over, call Child.stopnestedScroll () to notify the parent that the slide is over

  6. After the touch slide is over, the child will continue the inertial slide, which can be implemented by Scroller, and the specific slide can be handled by itself. In the fling process, just like the touch slide call process, the type parameter needs to be differentiated to notify the parent of two different sliding processes

At this point, the process and main methods of NestedScrollingParent2 and NestedScrollingChild2 are clear; But not only see here should still have more difficult to understand, after all, no code API and rogue no difference, next, or on the source code;

Learn NestedScrollingChild2 by RecycleView

RecycleView is our most commonly used list components, but also the most nested sliding requirements of the component, it also implemented NestedScrollingChild2, here as an example of the analysis;

RecycleView NestedScrollingChild2

First, let’s find a way to RecycleView NestedScrollingChild2;

  @Override
   public boolean startNestedScroll(int axes, int type) {
       return getScrollingChildHelper().startNestedScroll(axes, type);
   }

   @Override
   public void stopNestedScroll(int type) {
       getScrollingChildHelper().stopNestedScroll(type);
   }

 
   @Override
   public boolean hasNestedScrollingParent(int type) {
       return getScrollingChildHelper().hasNestedScrollingParent(type);
   }

 
   @Override
   public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
           int dyUnconsumed, int[] offsetInWindow, int type) {
       return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed,
               dxUnconsumed, dyUnconsumed, offsetInWindow, type);
   }

 

   @Override
   public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
           int type) {
       return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow,
               type);
   }
  private NestedScrollingChildHelper getScrollingChildHelper() {
       if (mScrollingChildHelper == null) {
           mScrollingChildHelper = new NestedScrollingChildHelper(this);
       }
       return mScrollingChildHelper;
   }
Copy the code

From the above you can see, RecycleView itself is not to deal with NestedScrollingChild2 method, but rather to NestedScrollingChildHelper method for processing, NestedScrollingChildHelper the leading role and the parent of some data transfer between processing, logic is simple, the space is limited, is described in detail.

2, NestedScrollingChild2 In RecycleView touch the logic of the slide process

RecycleView source code itself is very complex, in order to facilitate understanding here I eliminate some of the logic irrelevant code, according to the above logic, first find the startNestedScroll () method, and to start step by step follow up:

@Override public boolean onTouchEvent(MotionEvent e) { // ... The switch (action) {logic that has little to do with nested sliders has been removed.caseMotionevent. ACTION_DOWN: {mLastTouchX = (int) (equetx () + 0.5f); MLastTouchY = (int) (equety () + 0.5f); StartNestedScroll (nestedscrolsop, TYPE_TOUCH); }break;


           caseMotionevent. ACTION_MOVE: {int dx = mlastTouchx-x; int dy = mLastTouchY - y; // Before starting the slide, move your finger to the parentif(dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset, TYPE_TOUCH)) { Dx-= mScrollConsumed[0]; dy -= mScrollConsumed[1]; } // omit the RecycleView itself slide logic //...... //scrollByInternal () essentially calls the dispatchNestedScroll () method after the parent control has consumed, and after the parent control has consumed itself, This example demonstrates this example by transferring the remaining distance again to the parent control. This example demonstrates scrollByInternal(canscroll? dx : 0, canScrollVertically ? dy : 0, vtev); }break;
           caseMotionevent. ACTION_UP: {// omit the code related to speed calculation //.... fling((int) xvel, (int) yvel); resetTouch(); }break;
       }
       return true;
   }
Copy the code

By removing the irrelevant logic, touch events become very simple and clear

1. In motionEvent.action_Down, start the slide and call child.startnestedScroll ()
2. In motionEvent.action_move, call dispatchNestedPreScroll () and dispatchNestedScroll ()

ACTION_MOVE: RecycleView RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: RecycleView: dispatchNestedPreScroll (

 dx -= mScrollConsumed[0];
 dy -= mScrollConsumed[1];
Copy the code

RecycleView reduces the sliding distance in the parent control;

The RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView RecycleView dispatchNestedScroll ()

3. Start inertial slide in MotionEvent.action_up, and call stopNestedScroll () to notify to stop touch slide

Fling ((int) xvel, (int) yvel) and resetTouch(); The stopNestedScroll () method is called and the parent control is notified to stop the touch scroll ()

  private void resetTouch() {
     if(mVelocityTracker ! = null) { mVelocityTracker.clear(); } stopNestedScroll(TYPE_TOUCH); releaseGlows(); }Copy the code

At this point, the RecycleView is done with touch-swiping in nested interactions, and has begun to fling

3, NestedScrollingChild2 in the RecycleView inertial sliding process logic

Fling (int) xvel, (int) yvel) fling(int) To understand the logic processing of NestedScrollingChild2 during inertial sliding:

1. To start the inertial scroll, call the startNestedScroll () method

So, as usual, let’s get rid of some irrelevant code, and you can see,

public boolean fling(int velocityX, int velocityY) { //... StartNestedScroll (nestedscrolsop, TYPE_NON_TOUCH); velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity)); velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity)); mViewFlinger.fling(velocityX, velocityY);return true;
   }
Copy the code

As you can see, the Fling () method essentially does only two things

  1. Call startNestedScroll(nestedscrolepidemics, TYPE_NON_TOUCH) to notify the parent to start inertia sliding. Note that the second parameter, TYPE_NON_TOUCH, is different from TYPE_TOUCH when touching the slider, and is the parent control’s key parameter for distinguishing the slider state
  2. Start inertia slip
2. Start logic processing after inertial sliding

The run () method in ViewFlinger is the most important part of the logic processing. After removing some unimportant code from the run () method, we get the following pseudo-code:

     public void run() { final OverScroller scroller = mScroller; final SmoothScroller smoothScroller = mLayout.mSmoothScroller; // Before starting the inertial slide, first hand the data to the parent control processingif(dispatchNestedPreScroll(dx, dy, scrollConsumed, null, TYPE_NON_TOUCH)) {// Consumed by the parent control dx -= scrollConsumed[0]; dy -= scrollConsumed[1]; } / /... RecycleView itself inertial sliding logic processing // the remaining distance to a parent control for processingif(! dispatchNestedScroll(hresult, vresult, overscrollX, overscrollY, null, TYPE_NON_TOUCH) && (overscrollX ! = 0 || overscrollY ! = 0)) {} stopNestedScroll(TYPE_NON_TOUCH); }Copy the code

Inertial sliding process and touch sliding is very similar, although only a parameter, but has been inertial sliding data passed to the parent control, very simple to complete the entire process of processing, have to say, Google dad is always Google dad;

So far, we have a complete analysis of the RecycleView as a child logic process, I believe for NestedScrollingChild2 has also had a preliminary understanding; NestedScrollingParent2 is relatively simple, so there is no detailed analysis here, as long as it is processed according to the data from NestedScrollingChild2

3, actual combat, write a complete nested sliding

Only talk is false, in the case of have a preliminary understanding of the RecycleView process, write a small Demo of their own, to achieve the beginning of the effect, directly on the code:

1, NestedScrollingParent2Layout inheritance NestedScrollingParent2 realize the parent code logic

The RecycleView and an ImageView can be wrapped directly in this codeCopy the code
package com.sang.refrush; import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.NestedScrollingParent2; import androidx.core.view.NestedScrollingParentHelper; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; import com.sang.refrush.utils.FRLog; / * * * Description: nested sliding under NestedScrolling2 mechanism, implement NestedScrollingParent2 interface, Fling effect difference * / public class NestedScrollingParent2Layout extends LinearLayout implements NestedScrollingParent2 { private View mTopView; private View mContentView; private View mBottomView; private int mTopViewHeight; private int mGap; private int mBottomViewHeight; private NestedScrollingParentHelper mNestedScrollingParentHelper = new NestedScrollingParentHelper(this); public NestedScrollingParent2Layout(Context context) { this(context, null); } public NestedScrollingParent2Layout(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public NestedScrollingParent2Layout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);setOrientation(VERTICAL); } /** * @param child @param child @param child @param child @param child @param child @param child @param child @param child @param child @param child Child's generation >=target) * @param Target specifies the subclass of nested slides * @param Axes Specifies the scrolling direction supported by nested slides * @paramtypeThere are two types of nested swipe,ViewCompat. TYPE_NON_TOUCH fling effect,ViewCompat.TYPE_TOUCH gesture swipereturn trueIndicates that this parent class begins to accept nested slidings, onlytrueTime, */ @override public Boolean onStartNestedScroll(@nonnull View Child, @NonNull View target, int axes, inttype) {
       if(mContentView ! = null && mContentView instanceof RecyclerView) { ((RecyclerView) mContentView).stopScroll(); } mTopView.stopNestedScroll();return(axes & ViewCompat.SCROLL_AXIS_VERTICAL) ! = 0; } /** * When onStartNestedScroll returnstrue* * @param Child * @param Target * @param Axes * @param when the parent control accepts nested slidestype
    */
   @Override
   public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes, int type) {
       mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes, type); } /** * This method is called on the parent control before the child control starts to slide. The parent consumes part of the slide distance and passes it to the child in consumed form * before the nested child View slides *, Determine whether the parent view is processed first with the child view (that is, the parent view can be consumed first, * * @param target specifies the subclass of the nested slide * @param dx specifies the distance the horizontal nested slide subview wants to vary * @param dy Specifies the distance the vertical nested slide subview wants to vary dy<0 slide down Dy >0 * @param consumed in the function * @param consumed in the function, Consumed [1] Consumed vertically so that the subview adjusts accordingly * @paramtypeSliding type, */ @override public void onNestedPreScroll(@override public void onNestedPreScroll) target, int dx, int dy, @NonNull int[] consumed, inttypeBoolean hideTop = dy > 0 && getScrollY() < mTopViewHeight; boolean showTop = dy < 0 && getScrollY() >= 0 && ! target.canScrollVertically(-1) && ! mContentView.canScrollVertically(-1) &&target! =mBottomView ; boolean cunsumedTop = hideTop || showTop; // For the bottom layout Boolean hideBottom = dy < 0 && getScrollY() > mTopViewHeight; boolean showBottom = dy > 0 && getScrollY() >= mTopViewHeight && ! target.canScrollVertically(1) && ! mContentView.canScrollVertically(1) &&target! =mTopView ; boolean cunsumedBottom = hideBottom || showBottom;if (cunsumedTop) {
           scrollBy(0, dy);
           consumed[1] = dy;
       } else if(cunsumedBottom) { scrollBy(0, dy); consumed[1] = dy; }} /** * In onNestedPreScroll, after the parent control has consumed a portion of the distance, the rest is given to the child control again, * after the child control has consumed, if there is still any left, @param Target (@param dxConsumed) indicates the extent to which a child control in the horizontal direction of a nested slide is consumed @param dxUnconsumed Distance consumed in the horizontal direction of the nested sliding child * @param dyUnconsumed Public void onNestedScroll(@nonnull View target, int dxConsumed, public void onNestedScroll(@nonnull View Target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, inttype) {
       if(dyUnconsumed<0){// For downward swipeif(target == mBottomView){ mContentView.scrollBy(0, dyUnconsumed); }}else {
           if(target == mTopView){ mContentView.scrollBy(0, dyUnconsumed); }}} /** * Stop sliding ** @param target * @paramtype
    */
   @Override
   public void onStopNestedScroll(@NonNull View target, int type) {
       if (type == ViewCompat.TYPE_NON_TOUCH) {
           System.out.println("onStopNestedScroll");
       }

       mNestedScrollingParentHelper.onStopNestedScroll(target, type);
   }


   @Override
   public int getNestedScrollAxes() {
       returnmNestedScrollingParentHelper.getNestedScrollAxes(); } @Override protected void onMeasure(int widthMeasureSpec, Int heightMeasureSpec) {//ViewPager modified height = total height - navigation height super.onMeasure(widthMeasureSpec, heightMeasureSpec); ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams(); layoutParams.height = getMeasuredHeight(); mContentView.setLayoutParams(layoutParams); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected voidonFinishInflate() {
       super.onFinishInflate();
       if (getChildCount() > 0) {
           mTopView = getChildAt(0);
       }
       if (getChildCount() > 1) {
           mContentView = getChildAt(1);
       }
       if (getChildCount() > 2) {
           mBottomView = getChildAt(2);
       }

   }

   @Override
   protected void onSizeChanged(int w, int h, int oldw, int oldh) {
       super.onSizeChanged(w, h, oldw, oldh);
       if(mTopView ! = null) { mTopViewHeight = mTopView.getMeasuredHeight() ; }if(mBottomView ! = null) { mBottomViewHeight = mBottomView.getMeasuredHeight(); } } @Override public void scrollTo(int x, int y) { FRLog.d("scrollTo:" + y);
       if(y < 0) { y = 0; } // Modify the sliding distanceif(mContentView canScrollVertically (1)) {/ / can slip upif(y > mTopViewHeight) { y = mTopViewHeight-mGap; }}else if ((mContentView.canScrollVertically(-1))) {
           if(y < mTopViewHeight) { y = mTopViewHeight+mGap ; }}if(y > mTopViewHeight + mBottomViewHeight) { y = mTopViewHeight + mBottomViewHeight; } super.scrollTo(x, y); }}Copy the code

2, NestedScrollingChild2View inheritance NestedScrollingChild2 child code logic implementation

Of course, the top image doesn’t have a RecycleView function. Sometimes we need the top layout to have touch and inertial sliding events, too. The code logic is relatively complex at first, but I’ve commented it out as much as I can, so it should be easy to understand. Focus on the onTouchEvent () and inertia slide code

public class NestedScrollingChild2View extends LinearLayout implements NestedScrollingChild2 {


  private NestedScrollingChildHelper mScrollingChildHelper = new NestedScrollingChildHelper(this);
  private final int mMinFlingVelocity; private final int mMaxFlingVelocity; private Scroller mScroller; private int lastY = -1; private int lastX = -1; private int[] offset = new int[2]; private int[] consumed = new int[2]; private int mOrientation; private boolean fling; / / to determine whether the current slide can be inertia public NestedScrollingChild2View (Context Context) {this (Context, null); } public NestedScrollingChild2View(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public NestedScrollingChild2View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr);setOrientation(VERTICAL);
      mOrientation = getOrientation();
      setNestedScrollingEnabled(true);
      ViewConfiguration vc = ViewConfiguration.get(context);
      mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity(); mScroller = new Scroller(context); } /** * this method is called in the onInterceptTouchEvent or onTouch. Notifying the parent method to start sliding * calls both methods onStartNestedScroll onNestedScrollAccepted * * @param Axes * @paramtypeStart the slide with the type thetype of input which cause this scroll event
   * @returnHas a superview and starts to slide, returnstrue*/ @override public Boolean onStartNestedScroll (int Axes, int)type) {
      return mScrollingChildHelper.startNestedScroll(axes, type); } /** * In the child View onInterceptTouchEvent or onTouch, Call this method to notify the parent View of the distance to slide * eventually call the onNestedPreScroll method on the parent View * * @param dx The distance the children of the horizontal nested slide want to change dx<0 slide right dx>0 slide left (hold the sum RecycleView consistent) * @param dy Specifies the distance that the child wants to change dy<0 slide down dy>0 slide up (stays consistent with the RecycleView) * @param consumed the distance that the parent controls consume, After consuming the parent control, the rest is consumed by the child control. The child control uses consumed to handle the actual sliding distance * @param offsetInWindow the offset of the child control in the current window * @paramtypeFling effect (ViewCompat.TYPE_NON_TOUCH) and gesture (ViewCompatreturn trueIndicates that the parent control has made a sliding consumption and needs to handle the consumed value,falseIndicates that the parent control does not consume the sliding distance, and it can ignore consumed data processing. */ @override public Boolean dispatchNestedPreScroll(int dx, int dy, @nullable int[] consumed, @Nullable int[] offsetInWindow, inttype) {
      return mScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type); } /** * Called after dispatchNestedPreScroll * When the sliding distance is consumed by the parent control, the parent control passes the remaining distance to the child control again, * after the child control consumes part of the distance again, And continue to distribute the remaining distance to the parent control, by the parent control to determine whether to consume the remaining distance. * If the four distances consumed are all 0, then there is no god to consume, and it will return directlyfalseOtherwise, the parent control's * onNestedScroll method is called, * @param dxConsumed in RecycleView * * @param dxConsumed in RecycleView dx<0 slide right dx>0 Slide left * @param Dy <0 Downward dy>0 upward (consistent with RecycleView) * @param dxUnconsumed * @param dyUnconsumed (RecycleView) * @param dyUnconsumed (RecycleView)dy<0 Slide down dy>0 Slide up (keep the same as RecycleView) * @param offsetInWindow child control offset in the current window * @returnIf the returntrue*/ @override public Boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, inttype) {
      return mScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow, type); } /** * child control to stop sliding, such as finger raised, inertial sliding end ** @paramtype*/ @override public void stopNestedScroll(int)type) {
      mScrollingChildHelper.stopNestedScroll(type); } /** * sets whether the current child control supports nested slide. if not, then the parent control cannot respond to nested slide. ** @param Enabledtrue*/ @override public voidsetNestedScrollingEnabled(boolean enabled) { mScrollingChildHelper.setNestedScrollingEnabled(enabled); } /** * Whether the current child control supports nested sliding */ @override public BooleanisNestedScrollingEnabled() {
      returnmScrollingChildHelper.isNestedScrollingEnabled(); } /** * Determine whether the current child control has a nested sliding parent */ @override public Boolean hasNestedScrollingParent(int)type) {
      return mScrollingChildHelper.hasNestedScrollingParent(type); } private VelocityTracker mVelocityTracker; @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getActionMasked(); cancleFling(); // Stop the inertia slideif(lastX == -1 || lastY == -1) { lastY = (int) event.getRawY(); lastX = (int) event.getRawX(); } // Add a speed detector to handle the Fling effectif (mVelocityTracker == null) {
          mVelocityTracker = VelocityTracker.obtain();
      }
      mVelocityTracker.addMovement(event);

      switch (action) {
          caseMotionevent.action_down: {// When the finger is pressed lastY = (int) event.getrawy (); lastX = (int) event.getRawX(); // We are about to start sliding. We support vertical slidingif(mOrientation == VERTICAL) {// This method determines which direction and type to start the slide, which is VERTICAL, StartNestedScroll (viewCompat.scroll_axis_vertical, TYPE_TOUCH); }else {
                  startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL, TYPE_TOUCH);

              }
              break;
          }
          caseMotionevent.action_move :// When the finger slides int currentY = (int) (event.getrawy ()); int currentX = (int) (event.getRawX()); int dy = lastY - currentY; int dx = lastX - currentX; // Before starting the slide, notify the parent control to confirm whether the parent control needs to consume part of the slide firsttrueThat means you have to consume some of it firstif(dispatchNestedPreScroll(dx, dy, consumed, offset, TYPE_TOUCH)) {// Consumed by the parent control dy -= consumed[1]; dx -= consumed[0]; }, int consumedX = 0, consumedY = 0;if (mOrientation == VERTICAL) {
                  consumedY = childConsumedY(dy);
              } else{ consumedX = childConsumeX(dx); } // After the child control has finished processing the sliding event, the remaining control is passed to the parent control again for consumption // Since there is no sliding event, the number of times is 0. DispatchNestedScroll (consumedX, consumedY, dx-Consumedx, dy-consumedy, null, TYPE_TOUCH); lastY = currentY; lastX = currentX;break;

          caseMotionevent.action_up: // When the finger is lifted, end the nested slip pass and determine if the fling effect is generatedcaseMotionevent. ACTION_CANCEL: // Stop the scroll (TYPE_TOUCH); // Stop the scroll (TYPE_TOUCH); // Stop the scroll (TYPE_TOUCH). / / determine whether need inertia sliding mVelocityTracker.com puteCurrentVelocity (1000, mMaxFlingVelocity); int xvel = (int) mVelocityTracker.getXVelocity(); int yvel = (int) mVelocityTracker.getYVelocity(); fling(xvel, yvel);if(mVelocityTracker ! = null) { mVelocityTracker.clear(); } lastY = -1; lastX = -1;break;


      }

      return true; } private Boolean fling(int velocityX, int velocityY) {// Check whether the speed is large enough. Fling if it is large enoughif (Math.abs(velocityX) < mMinFlingVelocity) {
          velocityX = 0;
      }
      if (Math.abs(velocityY) < mMinFlingVelocity) {
          velocityY = 0;
      }
      if (velocityX == 0 && velocityY == 0) {
          return false; } // Notify the parent control to start the inertial slideif(mOrientation == VERTICAL) {// This method determines which direction and type to start the slide, which is VERTICAL, StartNestedScroll (viewCompat.scroll_axis_vertical, viewCompat.type_non_touch); }else{ startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL, ViewCompat.TYPE_NON_TOUCH); } velocityX = Math.max(-mMaxFlingVelocity, Math.min(velocityX, mMaxFlingVelocity)); velocityY = Math.max(-mMaxFlingVelocity, Math.min(velocityY, mMaxFlingVelocity)); // Start the inertia slidedoFling(velocityX, velocityY);
      return true;

  }

  private int mLastFlingX;
  private int mLastFlingY;
  private final int[] mScrollConsumed = new int[2];

  /**
   * 实际的fling处理效果
   */
  private void doFling(int velocityX, int velocityY) {
      fling = true;
      mScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
      postInvalidate();
  }

  @Override
  public void computeScroll() {
      if (mScroller.computeScrollOffset() && fling) {
          int x = mScroller.getCurrX();
          int y = mScroller.getCurrY();
          int dx = mLastFlingX - x;
          int dy = mLastFlingY - y;
          FRLog.i("y: " + y + " X: " + x + " dx: " + dx + " dy: "+ dy); mLastFlingX = x; mLastFlingY = y; // Before the child controls process fling, determine whether the parent controls are consumedif(dispatchNestedPreScroll(dx, dy, mScrollConsumed, null, viewCompat.type_non_touch)) { Dx -= mScrollConsumed[0]; dy -= mScrollConsumed[1]; } hResult = 0; hResult = 0; hResult = 0; hResult = 0; int vResult = 0; int leaveDx = 0; Int leaveDy = 0; // Fling fling = 0; // The parent control is running out and the child control is running outif(dx ! = 0) { leaveDx = childFlingX(dx); hResult = dx - leaveDx; // Get the horizontal distance left after the child control is consumed}if(dy ! = 0) { leaveDy = childFlingY(dy); VResult = dy-leavedy; vResult = dy-leavedy; } // Return the last part to the parent control again. DispatchNestedScroll (leaveDx, leaveDy, hResult, vResult, null, viewCompat.type_non_touch); postInvalidate(); }else {
          stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
          cancleFling();
      }
  }

  private void cancleFling() {
      fling = false; mLastFlingX = 0; mLastFlingY = 0; Fling (private Boolean) fling (private Boolean); fling (private Boolean) fling (private Boolean)canScroll() {// Implement the concrete logic itselfreturn true; } /** how many vertical fling fling the child controls consume ** @param dyreturnPrivate int childFlingY(int dy) {private int childFlingY(int dy) {return0; } /** How many vertical fling fling the child controls consume ** @param dx the parent controls consume the rest of the horizontal fling * @returnPrivate int childFlingX(int dx) {private int childFlingX(int dx) {return0; } /** * fling (fling (fling (fling)) ** @param dy (fling) ** @param dy (fling) ** @param dy (flingreturnPrivate childConsumedY(int dy) {private childConsumedY(int dy) {return0; ** @param dx ** @param dx ** @param dx ** @param dx ** @param dx ** @param dxreturnPrivate int childConsumeX(int dx) {return 0;
  }



Copy the code

At the top of the picture with child wrap, you will find that the picture also has a touch sliding and inertia sliding effect, and can pass the remaining sliding distance to RecycleView;

So far, we have finished the study of nested sliding, the time is short, if there is not perfect, please make corrections

Finally, part of the content reference some big guy’s code, because time is too long to remember, did not do one by one note, if cause discomfort please leave a message or private message to me;

The last of the last: source code