【发布时间】:2015-04-26 23:53:53
【问题描述】:
通过 support-v4 库 22.1.0,android 支持嵌套滚动(android 5.0 之前)。不幸的是,此功能并未真正记录在案。有两个接口(@987654322@ 和 NestedScrollingChild)以及两个助手委托类(NestedScrollingChildHelper 和 NestedScrollingParentHelper)。
有人在 Android 上使用过 NestedScrolling 吗?
我尝试设置一个小例子,我使用NestedScrollView 实现NestedScrollingParent 和NestedScrollingChild。
我的布局是这样的:
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:id="@+id/header"
android:layout_width="match_parent" android:layout_height="100dp"
android:background="#AF1233"/>
<android.support.v4.widget.NestedScrollView
android:id="@+id/child"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#12AF33"
android:text="@string/long_text"/>
</FrameLayout>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
我想在NestedScrollView(id = 父级)中显示一个header view 和另一个NestedScrollView(id = 子级)。
想法是,在运行时使用OnPredrawListener调整子滚动视图的高度:
public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final NestedScrollView parentScroll = (NestedScrollView) findViewById(R.id.parent);
final NestedScrollView nestedScroll = (NestedScrollView) findViewById(R.id.child);
parentScroll.setNestedScrollingEnabled(false);
final View header = findViewById(R.id.header);
parentScroll.getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override public boolean onPreDraw() {
if (parentScroll.getHeight() > 0) {
parentScroll.getViewTreeObserver().removeOnPreDrawListener(this);
nestedScroll.getLayoutParams().height = parentScroll.getHeight() - 40;
nestedScroll.setLayoutParams(nestedScroll.getLayoutParams());
nestedScroll.invalidate();
return false;
}
return true;
}
});
}
}
所以标题视图将被部分滚动,40 像素将保持可见,因为我将嵌套子滚动视图的高度设置为parentScroll.getHeight() - 40。
好的,在运行时设置高度并滚动父滚动视图的工作方式与预期一样(标题滚动出去,40 像素保持可见,然后子滚动视图填充标题下方的其余屏幕)。
我希望“NestedScrolling”意味着我可以在屏幕上的任何位置做出滚动手势(父滚动视图捕获的触摸事件),并且如果父滚动视图已到达末尾,嵌套的子滚动视图开始滚动。 然而,情况似乎并非如此(简单的滚动手势和滑动手势都不是)。
如果触摸事件从其边界开始,则触摸事件总是由嵌套的子滚动视图处理,否则由父滚动视图处理。
这是“嵌套滚动”的预期行为还是可以选择更改该行为?
我还尝试用NestedRecyclerView 替换嵌套的子滚动视图。我继承了RecyclerView 并实现了NestedScrollingChild,我将所有方法委托给NestedScrollingChildHelper:
public class NestedRecyclerView extends RecyclerView implements NestedScrollingChild {
private final NestedScrollingChildHelper scrollingChildHelper =
new NestedScrollingChildHelper(this);
public void setNestedScrollingEnabled(boolean enabled) {
scrollingChildHelper.setNestedScrollingEnabled(enabled);
}
public boolean isNestedScrollingEnabled() {
return scrollingChildHelper.isNestedScrollingEnabled();
}
public boolean startNestedScroll(int axes) {
return scrollingChildHelper.startNestedScroll(axes);
}
public void stopNestedScroll() {
scrollingChildHelper.stopNestedScroll();
}
public boolean hasNestedScrollingParent() {
return scrollingChildHelper.hasNestedScrollingParent();
}
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow) {
return scrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed,
dyUnconsumed, offsetInWindow);
}
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return scrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
}
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return scrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
}
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return scrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
}
但NestedRecyclerView 根本不滚动。所有的触摸事件都被父滚动视图捕获。
【问题讨论】:
-
我面临同样的问题,并认为应该改进这个小部件以允许父级将未使用的滚动分派给它的子级。
-
我认为让回收器视图成为一个nestedScrollingChild 并不是那么容易,因为它使用 LayoutManager 来滚动和定位它的项目。因此,仅使用 NestedScrollingChildHelper 实现 NestedScrollingChild 将不起作用,因为在滚动期间不会调用这些函数。
-
我不确定。我猜 NestedScrollingParent / NestedScrollingChild 是用来转发触摸事件的。我希望
NestedScrollingChild在内部这样做,我看不出有任何理由为什么它应该适用于ScrollView但不适用于RecyclerView...LayoutManager不应该是一个问题... -
如果你查看RecyclerView的源代码,在
onTouchEvent方法中,没有调用那些NestedScrollingChild方法,而NestedScrollView有。 -
如果您想要实现的只是从父级开始触摸事件并继续滚动到子级。您可以在 OnInterceptionTouchEvent 中偏移运动事件,以使触摸事件似乎是从子滚动视图开始的。然后在 onPreNestedScroll 方法中,你消耗 dy 直到父滚动视图的 scrollY 达到一定的限制。通过使用这个技巧,当您不再希望在 OnNestedPreScroll 上消耗任何 dy 时,请记住消耗应用于运动事件的额外偏移量是 OnInterceptionTouchEvent