Android工具栏+选项卡布局+抽屉,滚动时隐藏工具栏并将TabLayout移到顶部

25
我有一个活动,其中带有抽屉。 抽屉的每个菜单都是一个 fragment,并且在其中一个菜单下,我有一个带有 TabLayout 的 fragment,每个选项卡包含一个 RecyclerView。 现在,当我滚动 RecyclerView 时,选项卡布局会被隐藏,但 ToolBar 仍然在顶部。 我需要的是使 ToolBar 隐藏(scrollFlags:scroll|enterAlways),并让 TabLayout 显示在顶部。
因此,当前设置为: 附有 DrawerLayout 的 Activity -> 带有 TabLayout 的 Fragment -> 带有 RecyclerView 的 Tab Fragment 1 -> 带有 RecyclerView 的 Tab Fragment 2

你尝试过使用 hide(); 隐藏工具栏吗?getSupportedActionbar.hide(); - Mariano Zorrilla
如果我选择手动隐藏它,那么我就必须要跟踪滚动位置,而这是我不想做的。 - Vishal Pawale
你能发布一下你的layout.xml文件吗? - GPack
为什么不使用另一个活动来制作选项卡,然后只需将CollapsingToolbarLayout放置在其中...因为如果您不想在其他片段上使用选项卡,那么在同一活动中进行操作会让您感到繁琐。 - H Raval
10个回答

14

少写代码,更有效

你好 @Vishal,我找到了太多关于这个主题的内容。因为我之前也在搜索这个主题。

我发现了一个名为MaterialViewPager的杰出库,它可以完全按照您想要在滚动模式下隐藏的方式进行自定义。

请在https://www.youtube.com/watch?v=r95Tt6AS18c上观看视频。

输入图像描述


9

您能使用支持设计库吗?它内置了这种行为,可以完全实现您所描述的功能。它使用CoordinatorLayout来完成此操作。

<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:animateLayoutChanges="true"
    >
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
        <android.support.design.widget.TabLayout
            android:id="@+id/tabanim_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager
        android:id="@+id/tabanim_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_alarm_add_white_48dp"
        app:layout_anchor="@id/tabanim_viewpager"
        app:layout_anchorGravity="bottom|right|end"
        android:layout_margin="16dp"
        />

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

6

首先需要实现一个滚动监听器。您可以在这里找到一个示例HidingScrollListener。

public abstract class HidingScrollListener extends RecyclerView.OnScrollListener {

    private static final float HIDE_THRESHOLD = 10;
    private static final float SHOW_THRESHOLD = 70;

    private int mToolbarOffset = 0;
    private boolean mControlsVisible = true;
    private int mToolbarHeight;
    private int mTotalScrolledDistance;
    private int previousTotal = 0;
    private boolean loading = true;
    private int visibleThreshold = 4;
    int firstVisibleItem, visibleItemCount, totalItemCount;
    private LinearLayoutManager layoutManager;

    public HidingScrollListener(Context context, LinearLayoutManager layoutManager) {
        mToolbarHeight = Tools.getToolbarHeight(context);
        this.layoutManager = layoutManager;
    }

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            if (mTotalScrolledDistance < mToolbarHeight) {
                setVisible();
            } else {
                if (mControlsVisible) {
                    if (mToolbarOffset > HIDE_THRESHOLD) {
                        setInvisible();
                    } else {
                        setVisible();
                    }
                } else {
                    if ((mToolbarHeight - mToolbarOffset) > SHOW_THRESHOLD) {
                        setVisible();
                    } else {
                        setInvisible();
                    }
                }
            }
        }

    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        clipToolbarOffset();
        onMoved(mToolbarOffset);

        if ((mToolbarOffset < mToolbarHeight && dy > 0) || (mToolbarOffset > 0 && dy < 0)) {
            mToolbarOffset += dy;
        }
        if (mTotalScrolledDistance < 0) {
            mTotalScrolledDistance = 0;
        } else {
            mTotalScrolledDistance += dy;
        }
        // for load more
        visibleItemCount = recyclerView.getChildCount();
        totalItemCount = layoutManager.getItemCount();
        firstVisibleItem = layoutManager.findFirstVisibleItemPosition();

        if (loading) {
            if (totalItemCount > previousTotal) {
                loading = false;
                previousTotal = totalItemCount;
            }
        }
        if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
            // End has been reached
            // Do something

            loading = true;
            onLoadMore();
        }
    }

    private void clipToolbarOffset() {
        if (mToolbarOffset > mToolbarHeight) {
            mToolbarOffset = mToolbarHeight;
        } else if (mToolbarOffset < 0) {
            mToolbarOffset = 0;
        }
    }

    private void setVisible() {
        if (mToolbarOffset > 0) {
            onShow();
            mToolbarOffset = 0;
        }
        mControlsVisible = true;
    }

    private void setInvisible() {
        if (mToolbarOffset < mToolbarHeight) {
            onHide();
            mToolbarOffset = mToolbarHeight;
        }
        mControlsVisible = false;
    }

    public abstract void onMoved(int distance);

    public abstract void onShow();

    public abstract void onHide();

    public abstract void onLoadMore();
}

您需要修改RecyclerView适配器。您需要在RecyclerView顶部添加一个空视图,高度与您的工具栏相同。

这是一个空视图的示例布局。

<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/abc_action_bar_default_height_material" />

您需要覆盖适配器的getItemViewType和getITemCount方法,如下所示。
@Override
    public int getItemViewType(int position) {
        if (isPositionHeader(position)) {
            return TYPE_HEADER;
        }
        return TYPE_ITEM;
    }

    private boolean isPositionHeader(int position) {
        return position == 0;
    }

    @Override
    public int getItemCount() {
        return mObjects.size() + 1;
    }

然后在适配器的onCreateViewHolder方法中返回一个适合你的RecyclerView位置的布局,如下所示:

 @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = null;
        switch (viewType) {
            case TYPE_HEADER:
                view = LayoutInflater.from(mContext).inflate(R.layout.layout_recycler_header, parent, false);
                return new RecyclerHeaderViewHolder(view);

            default:
                view = LayoutInflater.from(mContext).inflate(R.layout.row_recyclerview_category, parent, false);
                return new ViewHolder(view);
        }

    }

最后,将您的HidingScrollListener实现添加到RecyclerView中,如下所示:
final int mToolbarHeight = Tools.getToolbarHeight(getActivity());
        RecyclerView mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(linearLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
        mViewPager.setPadding(mRecyclerView.getPaddingLeft(),
                mToolbarHeight,
                mRecyclerView.getPaddingRight(),
                mRecyclerView.getPaddingBottom());

        mHidingScrollListener = new HidingScrollListener(getActivity(), linearLayoutManager) {
            @Override
            public void onMoved(int distance) {
              mToolbarContainer.setTranslationY(-distance);
            }

            @Override
            public void onShow() {
                mToolbarContainer.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();
            }

            @Override
            public void onHide() {
               mToolbarContainer.animate()
                        .translationY(-mToolbarHeight)
                        .setInterpolator(new AccelerateInterpolator(2))
                        .start();
            }

            @Override
            public void onLoadMore() {
            }
        };
        mRecyclerView.setOnScrollListener(mHidingScrollListener);

我希望我正确理解了你的问题,并且我的实现可以帮助到你。

祝好运。

编辑:你可以轻松地将 LoadMorePullToRefresh 实现到这个解决方案中。你可以在 loadMore 中添加你的 API 请求。 PullToRefresh 有一个棘手的部分。在你下拉刷新之后,清除你的数据并通知适配器,不要忘记在隐藏滚动实现中将 visibleItemCount 和 totalItemCount 设置为0。如果你没有设置为 0,在刷新后,你的加载更多将无法正常工作。你会得到比分页中的项目数少的数据。


2

是的,你说得对,但是这个问题的作者没有展示他的XML或代码,他只展示了步骤并遇到了问题,即当滚动工具栏和选项卡布局时,因此为了帮助人们,我分享了这段代码作为答案。 - Gujarat Santana
抱歉我没有更新链接,现在已经更新了,希望能有所帮助。 - Gujarat Santana

2
您可以尝试以下方法,我曾用它来隐藏工具栏,并使滚动视图占据整个屏幕以进行全页阅读。

具体步骤如下:

  1. 隐藏工具栏。
  2. 获取手机屏幕的宽度和高度。
  3. 将选项卡布局的高度和宽度存储到一个临时变量中。
  4. 将手机屏幕的宽度和高度分配给您的选项卡布局。

可以按照以下方式完成此操作:

getSupportActionBar().hide();    
int mwidth = getApplicationContext().getResources().getDisplayMetrics().widthPixels;
int mheight = getApplicationContext().getResources().getDisplayMetrics().heightPixels;
temp = (TabLayout) myfrag.getActivity().findViewById(R.id.TabLayout_genesis);
int getwidth = temp.getWidth();
int getheight = temp.getHeight();
temp.setMinimumHeight(mheight);
temp.setMinimumWidth(mwidth);

希望能帮到你。

2
据我所知,没有内置的工具能够为您完成这项任务。不过您可以查看Google IO源代码,特别是BaseActivity。搜索“auto hide”或查看onMainContentScrolled。
要隐藏Toolbar,您只需执行以下操作:
toolbar.animate().translationY(-toolbar.getBottom()).setInterpolator(new AccelerateInterpolator()).start();

如果您想再次显示,可以调用以下代码:
toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator()).start();

在这里找到:安卓棒棒糖工具栏:如何在滚动时隐藏/显示工具栏?


2

我在我的应用程序中采用了相同的架构,这是我实现它的方式:

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
>

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

<android.support.v7.widget.Toolbar
    android:id="@+id/main_toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="@color/chat_primary_color"
    app:layout_scrollFlags="scroll|enterAlways"
    android:elevation="4dp"/>

<android.support.design.widget.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    android:elevation="6dp"
    android:minHeight="?attr/actionBarSize"
    android:layout_below="@id/main_toolbar"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
</android.support.design.widget.AppBarLayout>

<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:layout_below="@id/appbar_layout"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    />
<android.support.v4.widget.NestedScrollView
    android:id="@+id/container_fragment"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:layout_below="@id/appbar_layout"
    android:fillViewport="true"
    android:foregroundGravity="center"
    app:layout_behavior=".FixedScrollingViewBehavior"
    />

对于选项卡,使用ViewPager,对于其他片段,使用NestedScrollView作为FrameLayout。需要选项卡的片段中显示ViewPager,否则隐藏NestedScrollView

您可以在此处找到NestedScrollView的行为 FixedScrollingViewBehavior


1
Everyone is giving the code, "Talk is cheap, show me the code" right. 
I prefer not showing the code this time. 
I will talk. I do not recommand such a complicated activity.

DrawerLayout和TabLayout是Android中两种主要的导航方式。在同一个活动中展示DrawerLayout和TabLayout违反了Android开发指南。如果这样做,您的应用程序将非常难以使用。

想象一下,如果用户进行右向左滑动,您应该滑动pagerview还是drawerlayout?我认为当用户从活动的右侧边缘滑动时,您可以将滑动手势应用于drawerlayout,否则您可以将手势应用于pagerview,但是您如何确保用户知道这个规则?

即使用户知道如何滑动您的活动(现在的用户可能会很明智),您的应用程序看起来仍然非常复杂。

我的建议是,

 if you have less than 5 main menus, just use tablayout + actionbar, 
 if you have more than 5 main menus, use drawerlayout + actionbar. 
 you don't have to use both navigation in a row, you can still place the important actions to the actionbar right.

0

移除在选项卡活动中使用的协调布局。使用LinearLayout


1
请将此答案放在评论部分。 - bigbounty

0

要随时隐藏工具栏,您可以使用:

getSupportActionBar().hide();

如需更多解释,请查看url1url2


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接