使用带有enterAlways scrollFlag的视图和另一个带有exitUntilCollapsed的视图在AppBarLayout中使用scrollFlag

时间:2016-04-29 17:40:33

标签: android material-design android-coordinatorlayout android-appbarlayout

我正在尝试使用CoordinatorLayout和AppBarLayout构建以下布局:

|查看1(标题)|
|查看2 ------------ |
| RecyclerView --- |

我想要实现的行为如下:

  • 当我滚动RecyclerView时,视图1将完全崩溃。
  • 当我继续滚动时,View 2将会崩溃,直到它处于“崩溃”状态。
  • 在View 2折叠后,RecyclerView应该开始滚动。
  • 当我从中间向上滚动RecyclerView时,View 1应立即返回,而View 2则保持其折叠状态。
  • 一旦RecyclerView到达顶部,它就应该展开View 2.

这是我作为概念证明创建的测试布局。

<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.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/view1"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:text="TEST TITLE"
            android:textSize="50sp"
            app:layout_scrollFlags="scroll|enterAlways|snap" />

        <TextView
            android:id="@+id/view2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="TEST TEST TEST TEST TEST TEST TEST TEST"
            android:textSize="70sp"
            android:minHeight="50dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed" />

    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</android:support.design.widget.CoordinatorLayout>

我创建了一个测试适配器来在RV中添加几个TextView项目(这里没什么好看的)。当我运行代码时,它实际上并没有按预期工作。

  1. Initial Screen

  2. 当我向下滚动时,我确认视图1已完全折叠。

  3. 向下滚动更多。视图2折叠直到达到minHeight。之后RV开始滚动。到目前为止,这是按预期工作的。

  4. Here comes the problem. When I scroll up the RV. View 2 is expanded by the height of View 1. I'd like to see View 1 appear again.

  5. 我查看了AppBarLayout实现,问题似乎是因为AppBarLayout根据scrollFlags计算整个视图的滚动范围,并根据滚动偏移量偏移整个视图,而不是更新每个子视图。 / p>

    有没有人知道是否有任何解决方法或开源库来解决此问题?它不一定是CoordinatorLayout / AppBarLayout方法,但我需要产生这种行为。

    提前谢谢。

1 个答案:

答案 0 :(得分:0)

好的,我自己找到了一个解决方案,并决定为有类似问题的人发布我的解决方案。

解决方案是创建一个NestedCoordinatorLayout,通过实现NestedScollingChild扩展CoordinatorLayout,以便我们可以在两个AppBarLayouts之间进行交互。我在这篇文章https://stackoverflow.com/a/36881816/6272520中引用了NestedScrollView源代码和答案,但是我必须做一些修改才能使它按照我想要的方式工作。

这是NestedCoordinatorLayout的代码。

public class NestedCoordinatorLayout extends CoordinatorLayout implements NestedScrollingChild {

    private final NestedScrollingChildHelper scrollingChildHelper;

    private final int[] parentOffsetInWindow = new int[2];
    private final int[] parentScrollConsumed = new int[2];

    public NestedCoordinatorLayout(Context context) {
        this(context, null);
    }

    public NestedCoordinatorLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NestedCoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        scrollingChildHelper = new NestedScrollingChildHelper(this);
        setNestedScrollingEnabled(true);
    }

    //NestedScrollingChild

    @Override
    public void setNestedScrollingEnabled(boolean enabled) {
        scrollingChildHelper.setNestedScrollingEnabled(enabled);
    }

    @Override
    public boolean isNestedScrollingEnabled() {
        return scrollingChildHelper.isNestedScrollingEnabled();
    }

    @Override
    public boolean startNestedScroll(int axes) {
        return scrollingChildHelper.startNestedScroll(axes);
    }

    @Override
    public void stopNestedScroll() {
        scrollingChildHelper.stopNestedScroll();
    }

    @Override
    public boolean hasNestedScrollingParent() {
        return scrollingChildHelper.hasNestedScrollingParent();
    }

    @Override
    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
                                    int dyUnconsumed, int[] offsetInWindow) {
        return scrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
        return scrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
    }

    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return scrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
    }

    @Override
    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
        return scrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
    }

    //NestedScrollingParent

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        scrollingChildHelper.startNestedScroll(nestedScrollAxes);
        return super.onStartNestedScroll(child, target, nestedScrollAxes);
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        final int[] parentConsumed = parentScrollConsumed;
        //This is where the most important change happens.
        //During the prescroll, we want to decrease dx/dy.
        //This will make sure the top bar gets the scroll event first.
        if (dispatchNestedPreScroll(dx, dy, parentConsumed, null)) {
            dx -= parentConsumed[0];
            dy -= parentConsumed[1];
            consumed[0] += parentConsumed[0];
            consumed[1] += parentConsumed[1];
        }
        super.onNestedPreScroll(target, dx, dy, consumed);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, parentOffsetInWindow);
        super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
    }

    @Override
    public void onStopNestedScroll(View target) {
        scrollingChildHelper.onStopNestedScroll(target);
        super.onStopNestedScroll(target);
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        scrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);
        return super.onNestedFling(target, velocityX, velocityY, consumed);
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        scrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY);
        return super.onNestedPreFling(target, velocityX, velocityY);
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        super.onNestedScrollAccepted(child, target, axes);
        startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL);
    }
}

更新的xml文件如下所示:

<?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.design.widget.AppBarLayout
        android:id="@+id/app_bar_layout_for_view1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/view1"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:text="TEST TITLE"
            android:background="@android:color/white"
            android:textSize="50sp"
            app:layout_scrollFlags="scroll|enterAlways|snap" />

    </android.support.design.widget.AppBarLayout>

    <!-- Consider this like a NestedScrollView.
         You need to have a scrolling behavior -->
    <NestedCoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar_layout_for_view2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/view2"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:minHeight="50dp"
                android:text="TEST TEST TEST TEST TEST TEST TEST TEST"
                android:textSize="70sp"
                app:layout_scrollFlags="scroll|exitUntilCollapsed" />

        </android.support.design.widget.AppBarLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </NestedCoordinatorLayout>

</android.support.design.widget.CoordinatorLayout>

它就像一个魅力