【问题标题】:Stop AppBarLayout scrolling off screen when NestedScrollView is empty当 NestedScrollView 为空时,停止 AppBarLayout 滚出屏幕
【发布时间】:2015-08-20 03:51:29
【问题描述】:

我有一个相当典型的列表功能,使用 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


    【解决方案1】:

    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 的拖动

    【讨论】:

      【解决方案2】:

      将数据加载到您的 RecyclerView 后,如果它是空的,只需手动禁用滚动标志:

      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);
      

      【讨论】:

        【解决方案3】:

        (基于:Reference)

        (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"
        

        【讨论】:

          【解决方案4】:

          不确定这是多么优雅的解决方案,但我覆盖了 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 区域开始,滚动将仍然发生,即使您将此 Behavior 添加到 AppBarLayout 也是如此。因此,此答案不能解决问题。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-12-14
            • 1970-01-01
            • 1970-01-01
            • 2015-08-24
            • 2019-11-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多