Работа с AppBar

Верхняя панель приложения обеспечивает постоянное место в верхней части окна приложения для отображения информации и действий с текущего экрана.

пример верхней панели приложения
Рисунок 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>

Меню содержит две опции: одну для перехода к экрану профиля и одну для сохранения любых внесенных изменений профиля.

Панель приложения, принадлежащая активности

Панель приложения чаще всего принадлежит хосту. Если панель приложения принадлежит действию, фрагменты могут взаимодействовать с панелью приложения, переопределяя методы платформы, которые вызываются во время создания фрагмента.

Зарегистрироваться с активностью

Вы должны сообщить системе, что ваш фрагмент панели приложения участвует в заполнении меню опций. Для этого вызовите setHasOptionsMenu(true) в методе onCreate(Bundle) вашего фрагмента, как показано в следующем примере:

Котлин

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

Ява

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

setHasOptionsMenu(true) сообщает системе, что ваш фрагмент хочет получать обратные вызовы, связанные с меню. Когда происходит событие, связанное с меню, например щелчок, метод обработки событий сначала вызывается для действия, а затем для фрагмента.

Однако не полагайтесь на этот порядок в логике вашего приложения. Если одно и то же действие содержит несколько фрагментов, каждый фрагмент может предоставлять параметры меню, и в этом случае порядок обратного вызова зависит от порядка добавления фрагментов.

Раздуть меню

Чтобы объединить меню с меню параметров панели приложения, переопределите onCreateOptionsMenu() в своем фрагменте. Этот метод получает в качестве параметров текущее меню панели приложения и MenuInflater . Используйте средство раздувания меню, чтобы создать экземпляр меню вашего фрагмента, а затем объединить его с текущим меню, как показано в следующем примере:

Котлин

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

Ява

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

На рис. 2 показано обновленное меню.

меню опций теперь содержит фрагмент вашего меню
Рисунок 2. Меню опций теперь содержит фрагмент вашего меню.

Обработка событий кликов

Каждое действие и фрагмент, участвующие в меню опций, могут реагировать на прикосновения. Функция onOptionsItemSelected() фрагмента получает выбранный пункт меню в качестве параметра и возвращает логическое значение, указывающее, используется ли касание. Как только действие или фрагмент возвращает true из onOptionsItemSelected() , никакие другие участвующие фрагменты не получат обратный вызов.

В вашей реализации onOptionsItemSelected() используйте оператор switch для itemId пункта меню. Если выбранный элемент принадлежит вам, обработайте касание соответствующим образом и верните true , чтобы указать, что событие щелчка обработано. Если выбранный элемент не ваш, вызовите super . По умолчанию super возвращает false , чтобы продолжить обработку меню.

Котлин

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

Ява

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() обеспечивает правильное отображение меню:

Котлин

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

Ява

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() и обновляет меню, чтобы отразить текущее состояние фрагмента.

Котлин

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

Ява

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

Панель приложения, принадлежащая фрагменту

Если большинству экранов вашего приложения не требуется панель приложения или если для одного экрана требуется панель приложения, отличная от других, вы можете добавить Toolbar в макет фрагмента. Хотя вы можете добавить Toolbar в любое место в иерархии представлений вашего фрагмента, обычно вы держите ее в верхней части экрана. Чтобы использовать Toolbar в своем фрагменте, укажите идентификатор и получите ссылку на него в своем фрагменте, как и в любом другом представлении. Вы также можете рассмотреть возможность анимации панели инструментов с помощью поведения CoordinatorLayout .

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

При использовании панели приложения, принадлежащей фрагменту, Google рекомендует напрямую использовать API- Toolbar . Не используйте setSupportActionBar() и API-интерфейсы меню Fragment , которые подходят только для панелей приложений, принадлежащих действиям.

Раздуть меню

Удобный метод Toolbar inflateMenu(int) принимает идентификатор ресурса меню в качестве параметра. Чтобы разместить ресурс меню XML на панели инструментов, передайте resId этому методу, как показано в следующем примере:

Котлин

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

Ява

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

}

Чтобы расширить другой ресурс XML-меню, снова вызовите метод с resId нового меню. Новые пункты меню добавляются в меню, а существующие пункты меню не изменяются и не удаляются.

Если вы хотите заменить существующий набор меню, очистите меню перед вызовом inflateMenu(int) с новым идентификатором меню, как показано в следующем примере:

Котлин

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

Ява

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

        viewBinding.myToolbar.getMenu().clear()

    }

}

Обработка событий кликов

Вы можете передать OnMenuItemClickListener непосредственно на панель инструментов, используя метод setOnMenuItemClickListener() . Этот прослушиватель вызывается, когда пользователь выбирает пункт меню из кнопок действий, представленных в конце панели инструментов или связанного с ним переполнения. Выбранный MenuItem передается методу onMenuItemClick() прослушивателя и может использоваться для использования действия, как показано в следующем примере:

Котлин

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
            }
        }
    }
}

Ява

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 во время выполнения точно так же, как и любое другое представление.

Продолжая предыдущий пример, пункт меню «Сохранить» должен быть невидимым до тех пор, пока пользователь не начнет редактирование, и он должен снова исчезнуть при нажатии:

Котлин

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

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

    }
}

Ява

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

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

}

Если она присутствует, кнопка навигации отображается в начале панели инструментов. Установка значка навигации на панели инструментов делает его видимым. Вы также можете установить onClickListener() специфичную для навигации, которая вызывается всякий раз, когда пользователь нажимает кнопку навигации, как показано в следующем примере:

Котлин

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

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

Ява

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.
            }
        });
    }
}