Skip to content

Commit

Permalink
fix(android): nested scroll conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
iPel authored and siguangli committed Nov 25, 2022
1 parent acfebf4 commit 739a966
Show file tree
Hide file tree
Showing 5 changed files with 842 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,20 @@

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.NestedScrollingChild2;
import androidx.core.view.NestedScrollingParent2;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.HippyRecyclerViewBase;
import androidx.recyclerview.widget.IHippyViewAboundListener;
import androidx.recyclerview.widget.LinearLayoutManager;

import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import com.tencent.mtt.hippy.HippyEngineContext;
import com.tencent.mtt.hippy.utils.LogUtils;
import com.tencent.mtt.hippy.utils.PixelUtil;
Expand All @@ -42,7 +43,8 @@
* Created on 2020/12/22. Description
*/
public class HippyRecyclerView<ADP extends HippyRecyclerListAdapter> extends HippyRecyclerViewBase
implements IHeaderAttachListener, IHippyViewAboundListener {
implements IHeaderAttachListener, IHippyViewAboundListener,
NestedScrollingChild2, NestedScrollingParent2 {

private static int DEFAULT_ITEM_VIEW_CACHE_SIZE = 8;
protected HippyEngineContext hippyEngineContext;
Expand All @@ -59,6 +61,8 @@ public class HippyRecyclerView<ADP extends HippyRecyclerListAdapter> extends Hip
private int mInitialContentOffset;
private boolean isTvPlatform = false;
private HippyRecycleViewFocusHelper mFocusHelper = null;
private int mNestedScrollAxesTouch;
private int mNestedScrollAxesNonTouch;

public HippyRecyclerView(Context context) {
super(context);
Expand All @@ -72,6 +76,13 @@ public HippyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs,
super(context, attrs, defStyle);
}

@Override
protected void init() {
super.init();
// enable nested scrolling
setNestedScrollingEnabled(true);
}

public void onDestroy() {
if (stickyHeaderHelper != null) {
stickyHeaderHelper.detachSticky();
Expand Down Expand Up @@ -123,20 +134,22 @@ public void initRecyclerView() {

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean isIntercept = super.onInterceptTouchEvent(ev);
return isIntercept;
if (!isEnableScroll || mNestedScrollAxesTouch != SCROLL_AXIS_NONE) {
// We want to prevent the same direction intercepts only, so we can't use
// `requestDisallowInterceptTouchEvent` for this purpose.
// `mNestedScrollAxesTouch != SCROLL_AXIS_NONE` means has nested scroll child, no
// need to intercept
return false;
}
return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent e) {
if (!isEnableScroll) {
return false;
}
boolean handled = super.onTouchEvent(e);
if (handled && (e.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
requestDisallowInterceptTouchEvent(true);
}
return handled;
return super.onTouchEvent(e);
}

public void setInitialContentOffset(int initialContentOffset) {
Expand Down Expand Up @@ -521,4 +534,148 @@ public String toString() {
+ getStateInfo()
+ "}";
}

private int computeHorizontallyScrollDistance(int dx) {
if (dx < 0) {
return Math.max(dx, -computeHorizontalScrollOffset());
}
if (dx > 0) {
int avail = computeHorizontalScrollRange() - computeHorizontalScrollExtent()
- computeHorizontalScrollOffset() - 1;
return Math.min(dx, avail);
}
return 0;
}

private int computeVerticallyScrollDistance(int dy) {
if (dy < 0) {
return Math.max(dy, -computeVerticalScrollOffset());
}
if (dy > 0) {
int avail = computeVerticalScrollRange() - computeVerticalScrollExtent()
- computeVerticalScrollOffset() - 1;
return Math.min(dy, avail);
}
return 0;
}

@Override
public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes) {
return onStartNestedScroll(child, target, axes, ViewCompat.TYPE_TOUCH);
}

@Override
public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes,
int type) {
if (!isEnableScroll) {
return false;
}
// Determine whether to respond to the nested scrolling event of the child
LayoutManager manager = getLayoutManager();
if (manager == null) {
return false;
}
int myAxes = SCROLL_AXIS_NONE;
if (manager.canScrollVertically() && (axes & SCROLL_AXIS_VERTICAL) != 0) {
myAxes |= SCROLL_AXIS_VERTICAL;
}
if (manager.canScrollHorizontally() && (axes & SCROLL_AXIS_HORIZONTAL) != 0) {
myAxes |= SCROLL_AXIS_HORIZONTAL;
}
if (myAxes != SCROLL_AXIS_NONE) {
if (type == ViewCompat.TYPE_TOUCH) {
mNestedScrollAxesTouch = myAxes;
} else {
mNestedScrollAxesNonTouch = myAxes;
}
return true;
}
return false;
}

@Override
public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes) {
onNestedScrollAccepted(child, target, axes, ViewCompat.TYPE_TOUCH);
}

@Override
public void onNestedScrollAccepted(@NonNull View child, @NonNull View target, int axes,
int type) {
startNestedScroll(
type == ViewCompat.TYPE_TOUCH ? mNestedScrollAxesTouch : mNestedScrollAxesNonTouch,
type);
}

@Override
public void onStopNestedScroll(@NonNull View child) {
onStopNestedScroll(child, ViewCompat.TYPE_TOUCH);
}

@Override
public void onStopNestedScroll(@NonNull View target, int type) {
if (type == ViewCompat.TYPE_TOUCH) {
mNestedScrollAxesTouch = SCROLL_AXIS_NONE;
} else {
mNestedScrollAxesNonTouch = SCROLL_AXIS_NONE;
}
stopNestedScroll(type);
}

@Override
public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed,
int dyUnconsumed) {
onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
ViewCompat.TYPE_TOUCH);
}

@Override
public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed,
int dyUnconsumed, int type) {
// Process the current View first
int myDx = dxUnconsumed != 0 ? computeHorizontallyScrollDistance(dxUnconsumed) : 0;
int myDy = dyUnconsumed != 0 ? computeVerticallyScrollDistance(dyUnconsumed) : 0;
if (myDx != 0 || myDy != 0) {
scrollBy(myDx, myDy);
dxConsumed += myDx;
dyConsumed += myDy;
dxUnconsumed -= myDx;
dyUnconsumed -= myDy;
}
// Then dispatch to the parent for processing
int parentDx = dxUnconsumed;
int parentDy = dyUnconsumed;
if (parentDx != 0 || parentDy != 0) {
dispatchNestedScroll(dxConsumed, dyConsumed, parentDx, parentDy, null, type);
}
}

@Override
public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
onNestedPreScroll(target, dx, dy, consumed, ViewCompat.TYPE_TOUCH);
}

@Override
public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed,
int type) {
// Dispatch to the parent for processing first
int parentDx = dx;
int parentDy = dy;
if (parentDx != 0 || parentDy != 0) {
// Temporarily store `consumed` to reuse the Array
int consumedX = consumed[0];
int consumedY = consumed[1];
consumed[0] = 0;
consumed[1] = 0;
dispatchNestedPreScroll(parentDx, parentDy, consumed, null, type);
consumed[0] += consumedX;
consumed[1] += consumedY;
}
}

@Override
public int getNestedScrollAxes() {
return mNestedScrollAxesTouch | mNestedScrollAxesNonTouch;
}
}
Loading

0 comments on commit 739a966

Please sign in to comment.