当 NestedScrollView 为空时停止 AppBarLayout 滚动离开屏幕

问题描述 投票:0回答:5

我有一个相当典型的列表功能,使用 CoordinatorLayout、AppBarLayout、SwipeRefreshLayout 和 RecyclerView -

当 RecyclerView 有足够的内容可以滚动时,页面看起来不错。然而,当 RecyclerView 为空或没有足够的内容可供滚动时,行为是具有

app:layout_scrollFlags="scroll|enterAlwaysCollapsed"
的 AppBarLayout 子级将继续滚动 - 这看起来很奇怪。

有没有办法在 NestedScrollView 为空时停止 AppBarLayout 子级滚动?

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    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.CoordinatorLayout
        android:id="@+id/coordinatorLayout"
        android:background="@android:color/transparent"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:elevation="4dp">

            <LinearLayout
                android:id="@+id/eventHeader"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="16dp"
                android:background="@color/green"
                android:orientation="horizontal"
                app:layout_scrollFlags="scroll|enterAlwaysCollapsed">

                <View
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:layout_weight="1"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="scroll|enterAlwaysCollapsed"
                    android:textColor="@color/white"
                    android:textSize="15sp"/>

                <View
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:layout_weight="1"/>

            </LinearLayout>

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

        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swipeToRefresh"
            android:layout_width="match_parent"
            android:layout_gravity="fill_vertical"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/transparent"
                android:dividerHeight="0dp"
                android:layout_gravity="fill_vertical"
                android:drawSelectorOnTop="true"
                android:listSelector="@drawable/selector_ripple_grey_transparent"
                android:scrollbars="vertical"/>

        </android.support.v4.widget.SwipeRefreshLayout>

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

    <TextView
        android:id="@+id/noData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="16dp"
        android:text="@string/no_data_available"
        android:textSize="17sp"/>

</FrameLayout>
android android-recyclerview android-appbarlayout
5个回答
6
投票

不确定这是多么优雅的解决方案,但是,我重写了

onStartNestedScroll()
事件,仅在 NestedScrollView 可滚动时触发(在本例中为 RecyclerView)

在onCreate()中:

CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) mAppBarLayout.getLayoutParams();
layoutParams.setBehavior(new AppBarLayoutNoEmptyScrollBehavior(mAppBarLayout, mRecyclerView));

行为:

public class AppBarLayoutNoEmptyScrollBehavior extends AppBarLayout.Behavior {

    AppBarLayout mAppBarLayout;
    RecyclerView mRecyclerView;
    public AppBarLayoutNoEmptyScrollBehavior(AppBarLayout appBarLayout, RecyclerView recyclerView) {
        mAppBarLayout = appBarLayout;
        mRecyclerView = recyclerView;
    }

    public boolean isRecylerViewScrollable(RecyclerView recyclerView) {
        int recyclerViewHeight = recyclerView.getHeight(); // Height includes RecyclerView plus AppBarLayout at same level
        int appCompatHeight    = mAppBarLayout != null ? mAppBarLayout.getHeight() : 0;
        recyclerViewHeight -= appCompatHeight;

        return recyclerView.computeVerticalScrollRange() > recyclerViewHeight;
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
        if (isRecylerViewScrollable(mRecyclerView)) {
            return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
        }
        return false;
    }

}

编辑

编辑后的解决方案,RecyclerView 给出的高度为 visible RecyclerView 高度和 AppBarLayout 高度(即 CoordinatorLayout 高度)。

但是,如果您的滚动手势在可见的 AppBarLayout 区域上开始,即使您也将此 Behaviour 添加到 AppBarLayout 中,仍然会发生滚动。因此,这个答案并不能解决问题。


3
投票
参考

(1)

创建此类。 public class AppBarLayoutBehaviorForEmptyRecyclerView extends AppBarLayout.Behavior { private boolean canRecyclerViewBeScrolled = false; public AppBarLayoutBehaviorForEmptyRecyclerView() { } public AppBarLayoutBehaviorForEmptyRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) { return canRecyclerViewBeScrolled && super.onInterceptTouchEvent(parent, child, ev); } @Override public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) { updateScrollable(target); return canRecyclerViewBeScrolled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes); } @Override public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) { return canRecyclerViewBeScrolled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed); } private void updateScrollable(View targetChild) { if(targetChild instanceof RecyclerView) { RecyclerView.Adapter adapter = ((RecyclerView) targetChild).getAdapter(); canRecyclerViewBeScrolled = adapter != null && adapter.getItemCount() > 0; } else { canRecyclerViewBeScrolled = true; } } }

(2)

将以下属性添加到您的 AppBarLayout XML 元素:


app:layout_behavior="com.xxxx.xxxxxx.AppBarLayoutBehaviorForEmptyRecyclerView"



2
投票
Graeme

答案是好的,但我还在构造函数中添加了 public AppBarLayoutOnEmptyRecyclerViewScrollBehavior(@NonNull AppBarLayout appBarLayout, @NonNull RecyclerView recyclerView) { this.appBarLayout = checkNotNull(appBarLayout); this.recyclerView = checkNotNull(recyclerView); setDragCallback(new DragCallback() { @Override public boolean canDrag(@NonNull AppBarLayout appBarLayout) { return isRecylerViewScrollable(recyclerView); } }); }

所以当 
RecyclerView

为空时,我也禁用从

AppBarLayout
 的拖动
    


0
投票

AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mEventHeader.getLayoutParams(); toolbarLayoutParams.setScrollFlags(0); mEventHeader.setLayoutParams(toolbarLayoutParams);

如果不为空,则设置回滚动标志:

AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mEventHeader.getLayoutParams(); toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); mEventHeader.setLayoutParams(toolbarLayoutParams);



0
投票
Graeme

Kostek。 谢谢你们! 我的解决方案结合了它们+它是

Kotlin

实现并修复了已弃用的代码。 创建班级

AppBarLayoutNoNeedScrollBehavior

:

/**
 * Behavior to skip scrolling (hide) [AppBarLayout] if [RecyclerView] not scrollable.
 */
class AppBarLayoutNoNeedScrollBehavior(
    private val appBarLayout: AppBarLayout,
    private val recyclerView: RecyclerView
) : AppBarLayout.Behavior() {

    init {
        setDragCallback(object : DragCallback() {
            override fun canDrag(appBarLayout: AppBarLayout): Boolean = isScrollable()
        })
    }

    fun isScrollable(): Boolean {
        /** Check [recyclerView] has some content. */
        recyclerView.adapter?.itemCount?.takeIf { it > 0 } ?: return false

        /** [recyclerView] height includes [appBarLayout], because we must exclude it. */
        var recyclerHeight = recyclerView.height - appBarLayout.height

        /** In case if we have some padding at the bottom - it should be excluded too. */
        recyclerHeight -= recyclerView.paddingBottom

        return recyclerView.computeVerticalScrollRange() > recyclerHeight
    }

    override fun onStartNestedScroll(
        parent: CoordinatorLayout,
        child: AppBarLayout,
        directTargetChild: View,
        target: View,
        nestedScrollAxes: Int,
        type: Int
    ): Boolean {
        /** If [recyclerView] not scrollable - don't allow to scroll. */
        if (!isScrollable()) return false

        return super.onStartNestedScroll(
            parent, child, directTargetChild, target, nestedScrollAxes, type
        )
    }
}

在您的代码中:

val appBarLayout = .. val recyclerView = .. val layoutParams = appBarLayout.layoutParams as? CoordinatorLayout.LayoutParams layoutParams?.behavior = AppBarLayoutNoNeedScrollBehavior(appBarLayout, recyclerView)

© www.soinside.com 2019 - 2024. All rights reserved.