使用 AppBar

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

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)告诉系统您的片段希望接收与菜单相关的回调。当发生与菜单相关的事件(例如点击)时,事件处理方法首先在活动上调用,然后再在片段上调用。

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

展开菜单

要将您的菜单合并到应用栏的选项菜单中,请覆盖片段中的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. 选项菜单现在包含您的菜单片段。

处理点击事件

每个参与选项菜单的活动和片段都可以响应触摸。片段的onOptionsItemSelected()接收选定的菜单项作为参数,并返回一个布尔值以指示是否消耗了触摸。一旦活动或片段从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);
    }
}

当您需要更新菜单时(例如,当用户按下**编辑**按钮以编辑个人资料信息时),请在主机活动上调用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仅适用于活动拥有的应用栏。

展开菜单

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()

    }

}

处理点击事件

您可以使用setOnMenuItemClickListener()方法直接将OnMenuItemClickListener传递到工具栏。当用户从工具栏末尾显示的操作按钮或关联的溢出菜单中选择菜单项时,将调用此监听器。选定的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.
            }
        });
    }
}