使用 AppBar

应用栏位于应用窗口的顶部,提供了一个一致的位置来显示当前屏幕的信息和操作。 顶部应用栏

an example top app bar
图 1. 顶部应用栏示例。

应用栏的所有权根据应用的需求而有所不同。 使用片段时,应用栏可以实现为由主机活动拥有的 ActionBar 或片段布局中的工具栏。

如果所有屏幕都使用相同的应用栏,并且该应用栏始终位于顶部并跨越屏幕宽度,请使用活动托管的主题提供的操作栏。 使用主题应用栏有助于保持一致的外观,并提供一个位置来托管选项菜单和“向上”按钮。

如果要跨多个屏幕更精确地控制应用栏的大小、位置和动画,请使用片段托管的工具栏。 例如,您可能需要一个折叠应用栏或一个仅跨越屏幕一半宽度且垂直居中的应用栏。

不同的情况需要不同的方法来处理诸如加载菜单和响应用户交互等操作。 了解不同的方法并为您的应用采用最佳方法可以节省您的时间,并有助于确保您的应用正常运行。

本主题中的示例引用了一个包含可编辑配置文件的 ExampleFragment。 该片段在其应用栏中加载了以下 XML 定义的菜单

<!-- sample_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/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

该菜单包含两个选项:一个用于导航到配置文件屏幕,另一个用于保存所做的任何配置文件更改。

活动拥有的应用栏

应用栏最常由主机活动拥有。 当应用栏由活动拥有时,片段可以通过覆盖在片段创建期间调用的框架方法来与应用栏进行交互。

向活动注册

您必须通知系统您的应用栏片段参与了选项菜单的填充。 为此,请在片段的 onCreate(Bundle) 方法中调用 setHasOptionsMenu(true),如下例所示

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

public class ExampleFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

setHasOptionsMenu(true) 告诉系统您的片段希望接收与菜单相关的回调。当发生与菜单相关的事件(例如点击)时,事件处理方法会先在 Activity 上调用,然后再在片段上调用。

但是,不要在您的应用程序逻辑中依赖此顺序。如果同一个 Activity 托管多个片段,则每个片段都可以提供菜单选项,在这种情况下,回调顺序取决于片段添加的顺序。

加载菜单

要将您的菜单合并到应用栏的选项菜单中,请覆盖片段中的 onCreateOptionsMenu()。此方法接收当前应用栏菜单和 MenuInflater 作为参数。使用菜单加载器创建片段菜单的实例,然后将其合并到当前菜单中,如下例所示

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

图 2 显示了更新后的菜单。

the options menu now contains your menu fragment
图 2. 选项菜单现在包含您的菜单片段。

处理点击事件

参与选项菜单的每个 Activity 和片段都可以响应触摸。片段的 onOptionsItemSelected() 将选定的菜单项作为参数接收,并返回一个布尔值以指示是否消耗了触摸。一旦 Activity 或片段从 onOptionsItemSelected() 返回 true,则其他参与的片段将不再接收回调。

在您对 onOptionsItemSelected() 的实现中,对菜单项的 itemId 使用 switch 语句。如果选定的项目是您的,请适当地处理触摸并返回 true 以指示已处理点击事件。如果选定的项目不是您的,请调用 super 实现。默认情况下,super 实现返回 false 以允许菜单处理继续。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // Navigate to settings screen.
                true
            }
            R.id.action_done -> {
                // Save profile changes.
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // Navigate to settings screen.
                return true;
            }
            case R.id.action_done: {
                // Save profile changes.
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

动态修改菜单

将隐藏或显示按钮或更改图标的逻辑放在 onPrepareOptionsMenu() 中。此方法在显示菜单之前调用。

继续前面的示例,保存按钮在用户开始编辑之前应不可见,并在用户保存后消失。将此逻辑添加到 onPrepareOptionsMenu() 中可使菜单正确显示

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

当您需要更新菜单时(例如,当用户按下编辑按钮以编辑个人资料信息时),请在主机 Activity 上调用 invalidateOptionsMenu() 以请求系统调用 onCreateOptionsMenu()。无效后,您可以在 onCreateOptionsMenu() 中进行更新。菜单加载后,系统将调用 onPrepareOptionsMenu() 并更新菜单以反映片段的当前状态。

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

片段拥有的应用栏

如果您的应用中的大多数屏幕不需要应用栏,或者如果一个屏幕需要与其他屏幕不同的应用栏,则可以将 Toolbar 添加到您的片段布局中。虽然您可以在片段的视图层次结构中的任何位置添加 Toolbar,但通常将其保留在屏幕顶部。要在片段中使用 Toolbar,请提供一个 ID 并获取对它的引用,就像对任何其他视图一样。您还可以考虑使用 CoordinatorLayout 行为为工具栏设置动画。

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

当使用片段拥有的应用栏时,Google 建议直接使用 Toolbar API。不要使用 setSupportActionBar()Fragment 菜单 API,这些 API 仅适用于 Activity 拥有的应用栏。

加载菜单

Toolbar 的便捷方法 inflateMenu(int) 将菜单资源的 ID 作为参数。要将 XML 菜单资源加载到工具栏中,请将 resId 传递给此方法,如下例所示

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

要加载另一个 XML 菜单资源,请使用新菜单的 resId 再次调用该方法。新的菜单项将添加到菜单中,现有菜单项不会被修改或删除。

如果要替换现有的菜单集,请在使用新菜单 ID 调用 inflateMenu(int) 之前清除菜单,如下例所示

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

处理点击事件

您可以使用 OnMenuItemClickListener 通过 setOnMenuItemClickListener() 方法直接传递给工具栏。当用户从工具栏末尾或关联的溢出菜单中显示的操作按钮中选择菜单项时,将调用此侦听器。选定的 MenuItem 将传递到侦听器的 onMenuItemClick() 方法,并可用于使用操作,如下例所示

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen.
                    true
                }
                R.id.action_done -> {
                    // Save profile changes.
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen.
                    return true;
                case R.id.action_done:
                    // Save profile changes.
                    return true;
                default:
                    return false;
            }
        });
    }
}

动态修改菜单

当您的片段拥有应用栏时,您可以像任何其他视图一样在运行时修改 Toolbar

继续前面的示例,保存菜单选项在用户开始编辑之前应不可见,并在点击时再次消失

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

如果存在,导航按钮将显示在工具栏的开头。在工具栏上设置导航图标使其可见。您还可以设置一个特定于导航的 onClickListener(),每当用户点击导航按钮时都会调用它,如下例所示

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere.
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere.
            }
        });
    }
}