iOS喜欢Android上的滚动效果 [英] iOS like over scroll effect on Android
问题描述
我想在我的应用中实现类似iOS的反弹过度滚动效果。
我遇到了这个
在我开始回答之前,请注意:我强烈建议您使用支持库中的 NestedScrollView
而不是普通的 ScrollView
。它们在任何方面都是相同的,但 NestedScrollView
在较低的API级别上实现了正确的嵌套滚动行为。
无论如何让我们从我的答案开始:我提出的解决方案适用于任何可滚动容器,无论是 ScrollView
, ListView
或 RecyclerView
并且您不需要继承任何 Views
来实现它。
首先,如果您尚未使用Google的设计支持库,则需要将其添加到项目中:
编译'com.android.support:design:25.0.1'
请记住,如果你不是没有针对API级别25(顺便提一下),那么您需要包含API级别的最新版本(例如 compile'com.android.support:design:24.2.0'
for API level 24)。
您使用的任何可滚动容器都需要包含在 CoordinatorLayout $ c $中c> in你的布局。在我的示例中,我使用
NestedScrollView
:
<?xml version =1.0encoding =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
允许您指定行为
到其直接子视图。在这种情况下,我们将行为
分配给 NestedScrollView
,这将实现过度滚动跳出效果。
让我们看一下行为
的代码:
公共类OverScrollBounceBehavior扩展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;
返回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();
}
}
}
解释什么是行为
以及它们的工作方式超出了本答案的范围,因此我将快速解释上述代码的作用。 行为
拦截在 CoordinatorLayout
的直接子节点中发生的所有滚动事件。在 onStartNestedScroll()
方法中,我们返回 true
,因为我们对任何滚动事件感兴趣。在 onNestedScroll()
中,我们查看 dyUnconsumed
参数,该参数告诉我们没有消耗多少垂直滚动。滚动容器(换句话说,滚动)然后将滚动容器的子项转换为该数量。由于我们只是得到delta值,我们需要在 mOverscrollY
变量中总结所有这些值。当滚动事件停止时,将调用 onStopNestedScroll()
。这是我们将滚动容器的所有子项设置为原始位置的动画。
将行为
分配给 NestedScrollView
我们需要使用 layout_behavior
xml属性并传入我们想要使用的行为
。在我的例子中,上面的类在包 com.github.wrdlbrnft.testapp
中,所以我必须设置 com.github.wrdlbrnft.testapp.OverScrollBounceBehavior
作为价值。 layout_behavior
是 CoordinatorLayout
的自定义属性,因此我们需要在其前面添加正确的命名空间:
<?xml version =1.0encoding =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上添加的命名空间
和我在 NestedScrollView
上添加的 app:layout_behavior
属性。
这就是你要做的一切!虽然这个答案比我预想的要长,但我跳过了一些基本概念,其中包括 CoordinatorLayout
和行为
。因此,如果您对这些不熟悉或有任何其他问题,请随时提出。
I want to implement the iOS-like bounce overscroll effect in my app.
I came across this link which suggests creating a custom ScrollView
. But the problem is that when I am scrolling up and down fast it's working fine but as soon as I pull the bottom or top of the screen it's just stuck and the effect is not working anymore.
As an example of the kind of animation I want to achieve you can look at this:
This is the code I currently have:
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);
}
}
I have quickly put together a simple solution based on a CoordinatorLayout.Behavior
. It's not perfect, you can maybe spend some time fine tuning it a bit, but it's not bad. Anyway the result should look something like this:
As a small side note before I start with the answer: I strongly recommend that you use the NestedScrollView
from the support library instead of a normal ScrollView
. They are identical in any way, but the NestedScrollView
implements correct nested scrolling behaviour on lower API levels.
Anyway let's start with my answer: The solution I came up with would work with any scrollable container, be it a ScrollView
, ListView
or RecyclerView
and you don't need to subclass any Views
to implement it.
First you need to add Google's Design Support Library to your project if you aren't already using it:
compile 'com.android.support:design:25.0.1'
Remember that if you aren't targeting API level 25 (which you should by the way) then you need to include the newest version for your API level (eg. compile 'com.android.support:design:24.2.0'
for API level 24).
Whatever scrollable container you are using needs to wrapped in a CoordinatorLayout
in your layout. In my example I am using a 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>
The CoordinatorLayout
allows you to assign a Behavior
to its direct child views. In this case we are going to assign a Behavior
to the NestedScrollView
which is going implement the overscroll bounce effect.
Let's just take a look at the code of the 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();
}
}
}
Explaining what a Behavior
is and how they work is beyond the scope of this answer so I am just going to quickly explain what the above code does. The Behavior
intercepts all scroll events that happen in the direct children of the CoordinatorLayout
. In the onStartNestedScroll()
method we return true
since we are interested in any scroll events. In onNestedScroll()
we look at the dyUnconsumed
parameter which tells us how much of the vertical scroll was not consumed by the scrolling container (in other words overscroll) and then translate the children of the scrolling container by that amount. Since we are just getting delta values we need to sum up all of them in the mOverscrollY
variable. onStopNestedScroll()
is called when the scrolling event stops. This is when we animate all children of the scrolling container back to their original position.
To assign the Behavior
to the NestedScrollView
we need to use the layout_behavior
xml attribute and pass in the full class name of the Behavior
we want to use. In my example the above class is in the package com.github.wrdlbrnft.testapp
so I have to set com.github.wrdlbrnft.testapp.OverScrollBounceBehavior
as value. layout_behavior
is a custom attribute of the CoordinatorLayout
so we need to prefix it with the correct namespace:
<?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>
Notice the namespace I added on the CoordinatorLayout
and the app:layout_behavior
attribute I added on the NestedScrollView
.
And that is all you have to do! While this answer turned out to be longer than I intended I skipped over some of the basics concering the CoordinatorLayout
and Behaviors
. So if you are unfamiliar with these or have any other further questions feel free to ask.
这篇关于iOS喜欢Android上的滚动效果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!