web-dev-qa-db-ja.com

RecyclerViewが画面に収まったときにツールバーを折りたたまないでください


Android Design Libraryを使用して、ツールバーとTabLayoutでアプリを作成しました。
実際には2つのタブがあり、どちらにも2つのRecyclerViewがあり、スクロールするとツールバーが自動的に折りたたまれます。

私の質問は次のとおりです。RecyclerViewに項目がほとんどなく、画面に完全に収まった場合(TAB 2のように)、ツールバーの折りたたみを無効にできますか?

CheeseSquare のような多くの例を見てきました。これは、問題がまだ存在するGoogle従業員によって作成されました。RecyclerViewに1つのアイテムしかない場合でも、ツールバーはスクロールを隠し続けます。

enter image description here

私は、RecyclerViewの最初の項目が画面上に表示されているかどうか、そしてもしそうであればツールバーの折りたたみを無効にするかどうかを知ることができると思います。前者は実装が簡単ですが、後者はどうですか?

これは私のレイアウトです:

<Android.support.design.widget.CoordinatorLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/coordinator_layout"
    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:fitsSystemWindows="true"
            Android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlwaysCollapsed"
            Android:background="?attr/colorPrimary"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

        <Android.support.design.widget.TabLayout
            Android:id="@+id/tab_layout"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:background="@color/glucosio_pink"
            app:tabSelectedTextColor="@Android:color/white"
            app:tabIndicatorColor="@color/glucosio_accent"
            app:tabTextColor="#80ffffff"/>
        </Android.support.design.widget.AppBarLayout>

        <Android.support.v4.view.ViewPager
            Android:id="@+id/pager"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"/>

    <Android.support.design.widget.FloatingActionButton
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:id="@+id/main_fab"
        Android:layout_margin="16dp"
        Android:onClick="onFabClicked"
        app:backgroundTint="@color/glucosio_accent"
        Android:src="@drawable/ic_add_black_24dp"
        Android:layout_gravity="bottom|right"
        />
    </Android.support.design.widget.CoordinatorLayout>




33
Paolo Rotolo

最終ソリューション(MichałZに感謝)

ツールバーのスクロールをオフ/オンにする方法:

public void turnOffToolbarScrolling() {
    Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
    AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);

    //turn off scrolling
    AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
    toolbarLayoutParams.setScrollFlags(0);
    mToolbar.setLayoutParams(toolbarLayoutParams);

    CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
    appBarLayoutParams.setBehavior(null);
    appBarLayout.setLayoutParams(appBarLayoutParams);
}

public void turnOnToolbarScrolling() {
    Toolbar mToolbar = (Toolbar) findViewById(R.id.toolbar);
    AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout);

    //turn on scrolling
    AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
    toolbarLayoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
    mToolbar.setLayoutParams(toolbarLayoutParams);

    CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
    appBarLayoutParams.setBehavior(new AppBarLayout.Behavior());
    appBarLayout.setLayoutParams(appBarLayoutParams);
}


RecyclerViewの最後のアイテムがフラグメントに表示されているかどうかを確認します。
はいの場合、スクロールを無効にします。

public void updateToolbarBehaviour(){
    if (mLayoutManager.findLastCompletelyVisibleItemPosition() == items.size()-1) {
        ((MainActivity) getActivity()).turnOffToolbarScrolling();
    } else {
        ((MainActivity)getActivity()).turnOnToolbarScrolling();
    }
}
26
Paolo Rotolo

RecyclerViewが(バージョン23.2以降)wrap_contentをサポートするようになりました。高さとしてwrap_contentを使用するだけです。

8
Eric Cochran

RecyclerViewの最後のアイテムが表示されているかどうかを確認できます。そうでない場合は、このメソッドを使用してプログラムでスクロールをオフにします。

            //turn off scrolling
            AppBarLayout.LayoutParams toolbarLayoutParams = (AppBarLayout.LayoutParams) mToolbar.getLayoutParams();
            toolbarLayoutParams.setScrollFlags(0);
            mToolbar.setLayoutParams(toolbarLayoutParams);

            CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
            appBarLayoutParams.setBehavior(null);
            appBarLayout.setLayoutParams(appBarLayoutParams);
7
Michał Z.

私はこれを解決するためにわずかに異なるアプローチを取りました。

セルに基づいて自己を無効にするカスタムAppBarBehaviorを作成しました。

public class CustomAppBarBehavior extends AppBarLayout.Behavior {

    private RecyclerView recyclerView;
    private boolean enabled;

    public CustomAppBarBehavior() {
    }

    public CustomAppBarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
        updatedEnabled();
        return enabled && super.onInterceptTouchEvent(parent, child, ev);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
        return enabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
        return enabled && super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
    }

    private void updatedEnabled() {
        enabled = false;
        if(recyclerView != null) {
            RecyclerView.Adapter adapter = recyclerView.getAdapter();
            if (adapter != null) {
                int count = adapter.getItemCount();
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                if (layoutManager != null) {
                    int lastItem = 0;
                    if (layoutManager instanceof LinearLayoutManager) {
                        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
                        lastItem = Math.abs(linearLayoutManager.findLastCompletelyVisibleItemPosition());
                    } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                        StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                        int[] lastItems = staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(new int[staggeredGridLayoutManager.getSpanCount()]);
                        lastItem = Math.abs(lastItems[lastItems.length - 1]);
                    }
                    enabled = lastItem < count - 1;
                }
            }
        }
    }

    public void setRecyclerView(RecyclerView recyclerView) {
        this.recyclerView = recyclerView;
    }
}

次に、アプリバーレイアウトでカスタム動作を設定します

appBarBehavior = new CustomAppBarBehavior();
CoordinatorLayout.LayoutParams appBarLayoutParams = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
appBarLayoutParams.setBehavior(appBarBehavior);
appBarLayout.setLayoutParams(appBarLayoutParams);

ビューページャーの最終ページの変更により、動作のRecyclerViewが更新されました

private ViewPager.OnPageChangeListener pageChangeListener = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }

        @Override
        public void onPageSelected(final int position) {             
            appBarLayout.setExpanded(true, true);
            appBarLayout.post(new Runnable() {
                @Override
                public void run() {
                    appBarBehavior.setRecyclerView(childFragments.get(position).getRecyclerView());
                }
            });
        }

        @Override
        public void onPageScrollStateChanged(int state) { }
    };

これは、データセットの変更で機能するはずです。

4
Stuart Campbell

アダプターのデータを変更した後、このコードを追加します。

recyclerView.afterMeasured {
    val isTurnedOff = recyclerView.turnOffNestedScrollingIfEnoughItems()
    if (isTurnedOff) appBarLayout.setExpanded(true)
}

そして、これは機能です:

inline fun <T: View> T.afterMeasured(crossinline action: T.() -> Unit) {
    viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {             
            viewTreeObserver.removeOnGlobalLayoutListener(this)
            action()
        }
    })
}


fun RecyclerView.turnOffNestedScrollingIfEnoughItems(): Boolean {
    val lm = (layoutManager as LinearLayoutManager)
    val count = if (lm.itemCount <= 0) 0 else lm.itemCount - 1
    val isFirstVisible = lm.findFirstCompletelyVisibleItemPosition() == 0
    val isLastItemVisible = lm.findLastCompletelyVisibleItemPosition() == count

    isNestedScrollingEnabled = !(isLastItemVisible && isFirstVisible)
    return isNestedScrollingEnabled.not()
}
1
Artur Dumchev