2016-02-05 24 views
9

Mam dość typową funkcjonalność listy używając CoordinatorLayout, AppBarLayout, SwipeRefreshLayout i RecyclerView -Zatrzymaj przewijanie AppBarLayout wyłączyć ekran podczas NestedScrollView jest pusty

Gdy RecyclerView ma wystarczająco dużo treści do przewijania strony wydaje się w porządku. Kiedy widok RecyclerView jest pusty lub nie ma wystarczającej ilości treści do przewinięcia, zachowanie jest takie, że dzieci AppBarLayout z app:layout_scrollFlags="scroll|enterAlwaysCollapsed" będą nadal przewijać - co wygląda dziwnie.

Czy istnieje sposób, aby zatrzymać przewijanie dzieci AppBarLayout, gdy NestedScrollView jest pusty?

enter image description here

<?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> 

Odpowiedz

5

Nie wiem, jak eleganckie rozwiązanie to jest, ale ja overrode zdarzenie onStartNestedScroll() do jedynego ognia jeśli NestedScrollView jest przewijana (W tym przypadku RecyclerView)

w onCreate ():

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

Zachowanie:

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

} 

EDIT

Pod roztwór w RecyclerView daje wysokość jak widocznego wzrostu RecyclerView i wysokości AppBarLayout (który oznacza poziom CoordinatorLayout).

Jeśli jednak gest przewijania zostanie uruchomiony na widocznym obszarze AppBarLayout, przewijanie będzie trwało nadal, nawet jeśli dodasz to zachowanie również do AppBarLayout. Ta odpowiedź nie jest rozwiązaniem problemu.

2

(Na podstawie: Reference)

(1) Tworzenie tej klasy.

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) Dodaj do AppBarLayout elementu XML następujący atrybut:

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

Po ładowania danych do RecyclerView, jeśli jest pusta, tylko disalbe flagę przewijać ręcznie:

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

A jeśli nie jest pusty, cofnij flagę przewijania:

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 odpowiedź jest ok, ale dodałem też w konstruktorze

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

więc kiedy RecyclerView jest pusty również wyłączyć przeciągnij z AppBarLayout

Powiązane problemy