Detailed explanation of nested sliding mechanism

Nested sliding mechanism mainly involves two interfaces

public interface NestedScrollingChild {
    void setNestedScrollingEnabled(boolean var1);
    boolean isNestedScrollingEnabled();

    boolean startNestedScroll(int var1);//Open nested scrolling process
    //Loop through the parent. First determine whether it is an instance of NestedScrollingParent. If so, call onStartNestedScroll and onNestedScrollAccepted of the parent

    void stopNestedScroll();
    boolean hasNestedScrollingParent();
    boolean dispatchNestedScroll(int var1, int var2, int var3, int var4, @Nullable int[] var5);

    boolean dispatchNestedPreScroll(int var1, int var2, @Nullable int[] var3, @Nullable int[] var4);
    //This method is called before the child view rolls itself, asking whether the parent view will scroll before sub view.
  //The first two parameters of this method are used to tell the parent view the distance to scroll this time; the third and fourth parameters are used to get the distance consumed by the parent view and the offset of the parent view's position.
  //The first and second parameters are input parameters, which are regular function parameters. When calling a function, we need to pass the exact value for it. The third and fourth parameters are output parameters. When we call a function, we only need to pass the container (in this case, two arrays). After the call, we can get the output value of the function from the container.
  //This method returns true if the parent consumes part or all of the distance.

    boolean dispatchNestedFling(float var1, float var2, boolean var3);
    boolean dispatchNestedPreFling(float var1, float var2);
}

public interface NestedScrollingParent {
    boolean onStartNestedScroll(@NonNull View var1, @NonNull View var2, int var3);//Decide whether to nest and scroll with it

    void onNestedScrollAccepted(@NonNull View var1, @NonNull View var2, int var3);
    void onStopNestedScroll(@NonNull View var1);
    void onNestedScroll(@NonNull View var1, int var2, int var3, int var4, int var5);

    void onNestedPreScroll(@NonNull View var1, int var2, int var3, @NonNull int[] var4);
    //When child calls dispatchNestedPreScroll(), it can be called back to the parent's onnestedpresccroll(), and the parent can scroll ahead of the child in this callback.

    boolean onNestedFling(@NonNull View var1, float var2, float var3, boolean var4);
    boolean onNestedPreFling(@NonNull View var1, float var2, float var3);
    int getNestedScrollAxes();
}

log them as follows

E/child: startNestedScroll ------ open or not
 E/parent: onStartNestedScroll - match or not

E/child: dispatchNestedPreScroll -- if true, give the event to the parent
 E / parent: onnestedperscroll - at this time, dy has data and consumer has no data. Assign a value to consumer data. Send back the value immediately
 E / data parent: dy = 14, satisfied [1] 0
 E / data child: dy = 14, satisfied [1] 14

So generally, nested sliding child ren have the following important functions

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        //Press down
        case MotionEvent.ACTION_DOWN:
            lastY = (int) event.getRawY();
            startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL | ViewCompat.SCROLL_AXIS_VERTICAL);//Open nested slide
            break;
        //move
        case MotionEvent.ACTION_MOVE:
            ......
            if (dispatchNestedPreScroll(0, dy, consumed, offset)) {//If a parent class supporting nested sliding is found, the parent class makes a series of sliding
                Log.e("data child", "dy=" + dy + ",consumed[1]" + consumed[1]);
                //Obtain the sliding distance for sliding, and analyze the specific situation
                int remain = dy - consumed[1];
                if (remain != 0) {
                    scrollBy(0, -remain);
                }
            } else {//If there is no nested slide, slide by yourself
                scrollBy(0, -dy);
            }
            break;
        case MotionEvent.ACTION_UP:
            stopNestedScroll();
            break;
    }
    return true;
}

The most important function in the parent instance

//Here, you can judge which child view the parameter target is and the direction of scrolling, and then decide whether to nest scrolling with it
@Override
public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
    Log.e("parent", "onStartNestedScroll");
    if (target instanceof MyNestedScrollingChild2) {
        return true;
    }
    return false;
}

//Roll before child
//The first three are input parameters, and the last one is output parameters
@Override
public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
    Log.e("parent", "onNestedPreScroll");
    Log.e("data parent", "dy=" + dy + ",consumed[1]" + consumed[1]);
    if (showImg(dy) || hideImg(dy)) {//If you need to show or hide pictures, you need to scroll by yourself (parent)
        scrollBy(0, -dy);//Roll
        consumed[1] = dy;//Tell child how much I spent
    }
}

Reference article: https://www.cnblogs.com/wjtaigwh/p/6398562.html

however

In general, it's unlikely to be a naked nested scrolling child. If the child is recycleview (there are sliding events in it, and it inherits NestedScrollingChild itself)
Let's take a look at the onTouchEvent function of recycleview

switch (action) {
    case MotionEvent.ACTION_DOWN: {
        mScrollPointerId = MotionEventCompat.getPointerId(e, 0);
        mInitialTouchX = mLastTouchX = (int) (e.getX() + 0.5f);
        mInitialTouchY = mLastTouchY = (int) (e.getY() + 0.5f);

        int nestedScrollAxis = ViewCompat.SCROLL_AXIS_NONE;
        if (canScrollHorizontally) {
            nestedScrollAxis |= ViewCompat.SCROLL_AXIS_HORIZONTAL;
        }
        if (canScrollVertically) {
            nestedScrollAxis |= ViewCompat.SCROLL_AXIS_VERTICAL;
        }
        startNestedScroll(nestedScrollAxis);//******************************************************************
    } break;

    case MotionEventCompat.ACTION_POINTER_DOWN: {
        mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex);
        mInitialTouchX = mLastTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f);
        mInitialTouchY = mLastTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f);
    } break;

    case MotionEvent.ACTION_MOVE: {
        final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId);
        if (index < 0) {
            Log.e(TAG, "Error processing scroll; pointer index for id " +
                    mScrollPointerId + " not found. Did any MotionEvents get skipped?");
            return false;
        }

        final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f);
        final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f);
        int dx = mLastTouchX - x;
        int dy = mLastTouchY - y;

        if (dispatchNestedPreScroll(dx, dy, mScrollConsumed, mScrollOffset)) {//*************************************************
            dx -= mScrollConsumed[0];
            dy -= mScrollConsumed[1];
            vtev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
            // Updated the nested offsets
            mNestedOffsets[0] += mScrollOffset[0];
            mNestedOffsets[1] += mScrollOffset[1];
        }

        if (mScrollState != SCROLL_STATE_DRAGGING) {
            boolean startScroll = false;
            if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
                if (dx > 0) {
                    dx -= mTouchSlop;
                } else {
                    dx += mTouchSlop;
                }
                startScroll = true;
            }
            if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
                if (dy > 0) {
                    dy -= mTouchSlop;
                } else {
                    dy += mTouchSlop;
                }
                startScroll = true;
            }
            if (startScroll) {
                setScrollState(SCROLL_STATE_DRAGGING);
            }
        }

        if (mScrollState == SCROLL_STATE_DRAGGING) {//*****************************In this case, it moves as the fingers move. Call scrollByInternal
            mLastTouchX = x - mScrollOffset[0];
            mLastTouchY = y - mScrollOffset[1];

            if (scrollByInternal(
                    canScrollHorizontally ? dx : 0,
                    canScrollVertically ? dy : 0,
                    vtev)) {
                getParent().requestDisallowInterceptTouchEvent(true);
            }
        }
    } break;

For the specific parent code, please refer to the following article (the reason for adding dispatchTouchEvent is to increase the quick sliding effect, otherwise the sliding effect is too stiff)

Reference article: http://blog.csdn.net/lmj121212/article/details/53046582

Posted by suomynonA on Thu, 30 Apr 2020 23:16:38 -0700