【问题标题】:Two different menus for Top App Bar and Bottom App bar with Navigation Components带有导航组件的顶部应用栏和底部应用栏的两个不同菜单
【发布时间】:2019-02-28 05:40:36
【问题描述】:

我正在试用 Android Navigation Architecture Component,并且还在研究 Material design 指南。我真的受到了下面的design 的启发:

对于顶部工具栏,我可以通过setSupportActionBar(toolbar) 设置它,然后在MainActivity 中设置:

public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);
    return super.onCreateOptionsMenu(menu);
}

但在尝试时,我无法弄清楚如何在顶部和底部应用栏上为不同的片段实现菜单,特别是对于底部应用栏。

例如,我想在DetailFragment,但在MainActivity仅在底部应用栏显示一个收藏图标,它应该消失了。

我当前的代码:

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        NavController navController = Navigation.findNavController(this, R.id.nav_host);
        NavigationUI.setupActionBarWithNavController(this, navController);


        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show());


    }

    @Override
    public boolean onSupportNavigateUp() {
        return Navigation.findNavController(this, R.id.nav_host).navigateUp();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return super.onCreateOptionsMenu(menu);
    }
}

主片段

public class MainFragment extends Fragment {

    public MainFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Button buttonOne = view.findViewById(R.id.button_one);
        buttonOne.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.detailFragment));
    }

}

DetailFragment

public class DetailFragment extends Fragment {

    public DetailFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_detail, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <fragment
        android:id="@+id/nav_host"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="top"
        android:layout_marginTop="?android:attr/actionBarSize"
        app:defaultNavHost="true"
        app:layout_anchor="@id/bottom_appbar"
        app:layout_anchorGravity="top"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        app:navGraph="@navigation/mobile_navigation" />

    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottom_appbar"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/actionBarSize"
        android:layout_gravity="bottom" />

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_anchor="@id/bottom_appbar" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

mobile_navigation.xml

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@id/mainFragment">
    <fragment
        android:id="@+id/mainFragment"
        android:name="com.example.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main" >
        <action
            android:id="@+id/toAccountFragment"
            app:destination="@id/detailFragment" />
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:name="com.example.DetailFragment"
        android:label="fragment_account"
        tools:layout="@layout/fragment_detail" />
</navigation>

menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/app_bar_settings"
        android:title="@string/action_settings"
        app:showAsAction="never" />
</menu>

bottom_appbar_menu.xml仅适用于DetialFragment

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_bottom_fav"
        android:icon="@drawable/ic_favorite"
        android:title="@string/action_favorite"
        app:showAsAction="ifRoom" />
</menu>

感谢任何帮助。


更新了可能的解决方案:

这是我能想到的,但不满意,因为我不知道这是否是写方法。我发布了一个可能的解决方案:

1- MainActivity

NavController navController = Navigation.findNavController(this, R.id.nav_host);
NavigationUI.setupWithNavController(toolbar, navController);

2- 为底部应用栏创建两个不同的菜单(我没有尝试动态添加菜单项),一个为MainFragment 提供空白菜单 xml,另一个为DetailFragment 包含一个最喜欢的图标。

为简单起见,在MainActivity 中覆盖onCreateOptionsMenu 而不是用MainFragment 覆盖它:

public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.menu, menu);
    bottomAppBar.replaceMenu(R.menu.bottom_menu_blank);
    return super.onCreateOptionsMenu(menu);
}

3- 感谢@ʍѳђઽ૯ท 让我了解底部应用栏的replaceMenu 方法。在DetailFragment 中使用setHasOptionsMenu(true) 并覆盖onCreateOptionsMenu

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    BottomAppBar bottomAppBar = requireActivity().findViewById(R.id.bottom_appbar);
    bottomAppBar.replaceMenu(R.menu.bottom_menu_fav);
}

如果有人有更好的方法,请告诉我。

【问题讨论】:

    标签: android android-layout android-jetpack androidx android-bottomappbar


    【解决方案1】:

    要拥有多个工具栏(或 BottomAppBar),您必须手动为另一个工具栏充气。当您调用 setSupportActionBar()onCreateOptionsMenu() 时,您实际上是在这样做:

    private boolean inflateBottomAppBar() {
        BottomAppBar bottomAppBar = findViewById(R.id.bottomAppBar);
        Menu bottomMenu = bottomAppBar.getMenu();
        getMenuInflater().inflate(R.menu.menu_bottom, bottomMenu);
        for (int i = 0; i < bottomMenu.size(); i++) {
            bottomMenu.getItem(i).setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
                @Override
                public boolean onMenuItemClick(MenuItem menuItem) {
                    return onOptionsItemSelected(menuItem);
                }
            });
        }
        return super.onCreateOptionsMenu(menu);
    }
    

    R.id.bottomAppBarBottomAppBar 的 id,R.menu.menu_bottom 是菜单项的 id。

    在主工具栏膨胀后,在 onCreateOptionsMenu() 中调用此方法,一切顺利。 onOptionsItemSelected() 方法会正常处理所有项目点击。

    如果您要制作两个或更多常规工具栏,这也将起作用。

    【讨论】:

      【解决方案2】:

      像往常一样使用onCreateOptionsMenu() 作为Toolbar:(Kotlin)

      override fun onCreateOptionsMenu(menu: Menu?): Boolean {
              menuInflater.inflate(R.menu.menu_first, menu)
              return super.onCreateOptionsMenu(menu)
          }
      

      然后在onCreate()内声明Toolbar并使用setSupportActionBar()

      val toolbar = findViewById<Toolbar>(R.id.myToolbar)
      setSupportActionBar(toolbar)
      

      然后,replaceMenu() 会成功:(在 onCreate() 内部)

      val bottomBar = findViewById<BottomAppBar>(R.id.bottomAppBar)
      bottomBar.replaceMenu(R.menu.menu_main)
      

      请注意,如果您想将BottomSheetFragment 用于NavigationView 开头,则需要setSupportActionBar 才能将menus 设置为BottomAppBar,但我仍然找不到方法解决这个问题。

      【讨论】:

      • 感谢您的输入,但这不是一个完整的答案,但为此 +1。完整答案?例如:我必须在MainActivity 的方法onCreateOptionsMenu 上使用BottomAppBar#replaceMenu。要在片段上显示不同的菜单,我必须再次将 BottomAppBar#replaceMenu 与不同的菜单 xml 文件一起使用。为此,我必须在片段中使用setHasOptionsMenu(true) 并覆盖onCreateOptionsMenu。但是,如果有人有我写的以外的任何内容,我正在寻找更具体的答案。但再次感谢您。
      • 实际上就是这样。您的问题标题询问我回答了什么以及与回答相关的内容,但您似乎需要在 Fragment 中添加菜单。嗯,是的。在Fragment 中使用setHasOptionsMenu(true)onCreateOptionsMenu,它将显示您要查找的内容。不过,我添加了一条注释,您可能会考虑阅读它。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-27
      • 1970-01-01
      • 2020-07-08
      • 2018-07-06
      • 2019-11-03
      • 1970-01-01
      • 2018-12-27
      相关资源
      最近更新 更多