我想在我的应用中实现类似iOS的反弹过度滚动效果。
我遇到了这个link,建议您创建自定义ScrollView
。但问题是,当我快速上下滚动它工作正常但是一旦我拉动屏幕的底部或顶部它就会卡住而且效果不再起作用。
作为我想要实现的动画类型的一个示例,您可以看一下:
这是我目前的代码:
public class ObservableScrollView extends ScrollView
{
private static final int MAX_Y_OVERSCROLL_DISTANCE = 150;
private Context mContext;
private int mMaxYOverscrollDistance;
public ObservableScrollView(Context context)
{
super(context);
mContext = context;
initBounceScrollView();
}
public ObservableScrollView(Context context, AttributeSet attrs)
{
super(context, attrs);
mContext = context;
initBounceScrollView();
}
public ObservableScrollView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
mContext = context;
initBounceScrollView();
}
private void initBounceScrollView()
{
//get the density of the screen and do some maths with it on the max overscroll distance
//variable so that you get similar behaviors no matter what the screen size
final DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
final float density = metrics.density;
mMaxYOverscrollDistance = (int) (density * MAX_Y_OVERSCROLL_DISTANCE);
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)
{
//This is where the magic happens, we have replaced the incoming maxOverScrollY with our own custom variable mMaxYOverscrollDistance;
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, mMaxYOverscrollDistance, isTouchEvent);
}
}
答案 0 :(得分:25)
我已经快速整理了一个基于CoordinatorLayout.Behavior
的简单解决方案。它并不完美,你可以花一些时间对它进行微调,但它并不坏。无论如何,结果看起来应该是这样的:
在我开始回答之前作为一个小旁注:我强烈建议您使用支持库中的NestedScrollView
而不是普通的ScrollView
。它们在任何方面都是相同的,但NestedScrollView
在较低的API级别上实现了正确的嵌套滚动行为。
无论如何,让我们从我的回答开始:我提出的解决方案适用于任何可滚动的容器,无论是ScrollView
,ListView
还是RecyclerView
,您都可以#39; t需要继承任何Views
来实现它。
首先,如果您尚未使用Google的设计支持库,则需要将其添加到项目中:
compile 'com.android.support:design:25.0.1'
请记住,如果您没有针对API级别25(顺便提一下),那么您需要为API级别添加最新版本(例如,compile 'com.android.support:design:24.2.0'
用于API级别24)。
您使用的任何可滚动容器都需要包含在布局中的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">
<!-- content -->
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
CoordinatorLayout
允许您为其直接子视图分配Behavior
。在这种情况下,我们将Behavior
分配给NestedScrollView
,这将实现过度滚动反弹效果。
让我们来看看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
参数,该参数告诉我们滚动容器没有消耗多少垂直滚动(换句话说是过度滚动),然后将滚动容器的子项转换为该数量。由于我们只是得到delta值,我们需要在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">
<!-- content -->
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
请注意我在CoordinatorLayout
上添加的名称空间以及app:layout_behavior
上添加的NestedScrollView
属性。
这就是你要做的一切!虽然这个答案比我预想的要长,但我跳过了一些基于CoordinatorLayout
和Behaviors
的基础知识。因此,如果您对这些不熟悉或有其他任何问题,请随时提出。
答案 1 :(得分:2)
感谢 Xaver Kapeller ,我使用 kotlin 和 androidx
添加协调者依赖性
implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0"
创建一个扩展CoordinatorLayout.Behavior的新类
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.ViewCompat
class OverScrollBehavior(context: Context, attributeSet: AttributeSet)
: CoordinatorLayout.Behavior<View>() {
companion object {
private const val OVER_SCROLL_AREA = 4
}
private var overScrollY = 0
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
directTargetChild: View,
target: View,
axes: Int,
type: Int
): Boolean {
overScrollY = 0
return true
}
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int,
type: Int,
consumed: IntArray
) {
if (dyUnconsumed == 0) {
return
}
overScrollY -= (dyUnconsumed/OVER_SCROLL_AREA)
val group = target as ViewGroup
val count = group.childCount
for (i in 0 until count) {
val view = group.getChildAt(i)
view.translationY = overScrollY.toFloat()
}
}
override fun onStopNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
type: Int
) {
// Smooth animate to 0 when the user stops scrolling
moveToDefPosition(target)
}
override fun onNestedPreFling(
coordinatorLayout: CoordinatorLayout,
child: View,
target: View,
velocityX: Float,
velocityY: Float
): Boolean {
// Scroll view by inertia when current position equals to 0
if (overScrollY == 0) {
return false
}
// Smooth animate to 0 when user fling view
moveToDefPosition(target)
return true
}
private fun moveToDefPosition(target: View) {
val group = target as ViewGroup
val count = group.childCount
for (i in 0 until count) {
val view = group.getChildAt(i)
ViewCompat.animate(view)
.translationY(0f)
.setInterpolator(AccelerateDecelerateInterpolator())
.start()
}
}
}
使用CoordinatorLayout和NestedScrollView创建XML文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior=".OverScrollBehavior">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAlignment="center"
android:padding="10dp"
android:text="@string/Lorem"/>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
别忘了添加
app:layout_behavior=".OverScrollBehavior" // Or your file name
您的NestedScrollView XML标记的字段
答案 2 :(得分:1)
如果您想使用库,那么这个 Bouncy 是最符合您要求的最佳库。
答案 3 :(得分:-3)
使用此
Private ScrollView scrMain;
scrMain = (ScrollView) v.findViewbyId(R.id.scrMain);
OverScrollDecorHandler.setScrollView(scrMain);