我基于 CoordinatorLayout.Behavior
快速搭建了一个简单的解决方案。虽然不是完美的,但你可以花点时间微调一下,它还是不错的。无论如何,最终结果应该像这样:
![enter image description here](https://istack.dev59.com/7sHHM.gif)
在开始回答之前,先小小提示一下:强烈建议使用支持库中的 NestedScrollView
,而不是普通的 ScrollView
。两者没有任何不同,但是 NestedScrollView
在较低的 API 级别上实现了正确的嵌套滚动行为。
接下来让我们开始回答:我提出的解决方案适用于任何可滚动的容器,例如 ScrollView
、ListView
或 RecyclerView
,而且你不需要子类化任何 Views
来实现它。
首先,如果你还没有使用 Google 的设计支持库,你需要将其添加到你的项目中:
compile 'com.android.support:design:25.0.1'
记住,如果您没有针对API 25进行目标设置(顺便说一下,这是您应该做的),那么您需要包括适用于您的API级别的最新版本(例如,对于API级别24,使用
compile 'com.android.support:design:24.2.0'
)。
无论您使用什么可滚动容器,在布局中都需要将其包装在
CoordinatorLayout
中。在我的示例中,我正在使用
NestedScrollView
:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
CoordinatorLayout
允许您为其直接子视图分配 Behavior
。在这种情况下,我们将为 NestedScrollView
分配一个实现过度滚动反弹效果的 Behavior
。
让我们来看一下 Behavior
的代码:
public class OverScrollBounceBehavior extends CoordinatorLayout.Behavior<View> {
private int mOverScrollY;
public OverScrollBounceBehavior() {
}
public OverScrollBounceBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
mOverScrollY = 0;
return true;
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
if (dyUnconsumed == 0) {
return;
}
mOverScrollY -= dyUnconsumed;
final ViewGroup group = (ViewGroup) target;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
view.setTranslationY(mOverScrollY);
}
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
final ViewGroup group = (ViewGroup) target;
final int count = group.getChildCount();
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
ViewCompat.animate(view).translationY(0).start();
}
}
}
解释什么是
Behavior
以及它们如何工作超出了本答案的范围,因此我只会快速解释上面的代码所做的事情。
Behavior
拦截在
CoordinatorLayout
的直接子项中发生的所有滚动事件。在
onStartNestedScroll()
方法中,我们返回
true
,因为我们对任何滚动事件都感兴趣。在
onNestedScroll()
中,我们查看
dyUnconsumed
参数,该参数告诉我们竖直滚动中有多少未被滚动容器使用(换句话说,是超过滚动范围的)。然后将滚动容器的子项移动相应的距离。由于我们只是获取增量值,因此需要将所有增量值加起来存储在
mOverscrollY
变量中。
onStopNestedScroll()
在滚动事件停止时被调用。这是我们将滚动容器的所有子项动画返回到其原始位置的时候。
要将Behavior
分配给NestedScrollView
,我们需要使用layout_behavior
xml属性并传递要使用的Behavior
的完整类名。在我的示例中,上述类位于com.github.wrdlbrnft.testapp
包中,因此我必须将com.github.wrdlbrnft.testapp.OverScrollBounceBehavior
设置为值。 layout_behavior
是CoordinatorLayout
的自定义属性,因此我们需要使用正确的命名空间前缀。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.github.wrdlbrnft.testapp.OverScrollBounceBehavior">
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
请注意我在CoordinatorLayout
上添加的命名空间以及我在NestedScrollView
上添加的app:layout_behavior
属性。
这就是你需要做的全部内容!虽然我的回答比我预想的要长,但我省略了一些关于CoordinatorLayout
和Behaviors
的基础知识。如果你不熟悉这些或有任何其他问题,请随时问我。