Переключение между изображениями Android Navigation Drawer и Up caret при использовании fragmentов

При использовании навигационного ящика разработчики Android рекомендуют, чтобы в ActionBar «только те экраны, которые представлены в навигационном ящике, должны иметь изображение навигационного ящика» и что «все остальные экраны имеют традиционный карат».

Подробнее см. Здесь: http://youtu.be/F5COhlbpIbY

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

При создании fragmentов более низкого уровня я могу вызвать ActionBarDrawerToggle setDrawerIndicatorEnabled(false) чтобы скрыть изображение навигационного ящика и отобразить Up upt

 LowerLevelFragment lowFrag = new LowerLevelFragment(); //disable the toggle menu and show up carat theDrawerToggle.setDrawerIndicatorEnabled(false); getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, lowFrag, "lowerFrag").addToBackStack(null).commit(); 

Проблема, с которой я сталкиваюсь, – это когда я перехожу назад к fragmentам верхнего уровня, которые по-прежнему показывает вверх карат, вместо оригинального изображения ящика. Любые предложения о том, как «обновить» ActionBar на fragmentах верхнего уровня, чтобы повторно отобразить изображение навигационного ящика?


Решение

Предложение Тома работало для меня. Вот что я сделал:

Основная деятельность

Это действие контролирует все fragmentы в приложении.

При создании новых fragmentов для замены других я устанавливаю DrawerToggle setDrawerIndicatorEnabled(false) следующим образом:

 LowerLevelFragment lowFrag = new LowerLevelFragment(); //disable the toggle menu and show up carat theDrawerToggle.setDrawerIndicatorEnabled(false); getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, lowFrag).addToBackStack(null).commit(); 

Затем, переопределив onBackPressed , я вернул выше, установив DrawerToggle в setDrawerIndicatorEnabled(true) следующим образом:

 @Override public void onBackPressed() { super.onBackPressed(); // turn on the Navigation Drawer image; // this is called in the LowerLevelFragments setDrawerIndicatorEnabled(true) } 

В LowerLevelFragments

Во fragmentах я изменил onCreate и onOptionsItemSelected следующим образом:

В onCreate добавлен setHasOptionsMenu(true) чтобы включить настройку меню опций. Также установите setDisplayHomeAsUpEnabled(true) чтобы включить < в панели действий:

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // needed to indicate that the fragment would // like to add items to the Options Menu setHasOptionsMenu(true); // update the actionbar to show the up carat/affordance getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); } 

Затем в onOptionsItemSelected всякий раз, когда onOptionsItemSelected <, он вызывает onBackPressed() из действия, чтобы перейти на один уровень в иерархии и отобразить изображение навигационного ящика:

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Get item selected and deal with it switch (item.getItemId()) { case android.R.id.home: //called when the up affordance/carat in actionbar is pressed getActivity().onBackPressed(); return true; … } 

    Вы написали, что для реализации fragmentов нижнего уровня вы заменяете существующий fragment, в отличие от реализации fragmentа нижнего уровня в новом действии.

    Я бы подумал, что тогда вам придется реализовать функциональность назад вручную: когда пользователь нажал назад, у вас есть код, который выдает стек (например, в Activity::onBackPressed override). Итак, где бы вы это ни делали, вы можете отменить setDrawerIndicatorEnabled .

    Это легко, как 1-2-3.

    Если вы хотите достичь:

    1) Индикатор ящика – при отсутствии fragmentов в Back Stack или ящике

    2) Стрелка – когда некоторые fragmentы находятся в Back Stack

     private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { syncActionBarArrowState(); } }; @Override protected void onCreate(Bundle savedInstanceState) { getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mDrawerToggle = new ActionBarDrawerToggle( this, mDrawerLayout, R.drawable.ic_navigation_drawer, 0, 0 ) { public void onDrawerClosed(View view) { syncActionBarArrowState(); } public void onDrawerOpened(View drawerView) { mDrawerToggle.setDrawerIndicatorEnabled(true); } }; mDrawerLayout.setDrawerListener(mDrawerToggle); getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener); } @Override protected void onDestroy() { getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener); super.onDestroy(); } private void syncActionBarArrowState() { int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0); } 

    3) Оба индикатора действуют в соответствии с их формой

     @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } else if (item.getItemId() == android.R.id.home && getSupportFragmentManager().popBackStackImmediate()) { return true; } else { return super.onOptionsItemSelected(item); } } 

    PS См. Раздел Создание навигационного ящика на Android Developers в других советах о поведении индикаторов из 3 строк.

    Я использовал следующее:

     getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if(getSupportFragmentManager().getBackStackEntryCount() > 0){ mDrawerToggle.setDrawerIndicatorEnabled(false); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } else { getSupportActionBar().setDisplayHomeAsUpEnabled(false); mDrawerToggle.setDrawerIndicatorEnabled(true); } } }); 

    Если ваша кнопка на панели действий не работает, не забудьте добавить слушателя:

     // Navigation back icon listener mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); 

    У меня есть некоторые проблемы с реализацией навигации на ящике с помощью кнопки «домой», все работает, кроме действия.

    Попробуйте обработать выбор элемента Home в MainActivity в зависимости от состояния DrawerToggle. Таким образом, вам не нужно добавлять одинаковый код ко всем fragmentам.

     @Override public boolean onOptionsItemSelected(MenuItem item) { // Only handle with DrawerToggle if the drawer indicator is enabled. if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { // Handle home button in non-drawer mode case android.R.id.home: onBackPressed(); return true; default: return super.onOptionsItemSelected(item); } } 

    СЛЕДОВАТЬ ЗА

    Решение, данное @dzeikei, является опрятным, но при использовании fragmentов оно может быть расширено, чтобы автоматически обрабатывать настройку индикатора выдвижного ящика, когда стопка заднего фона пуста.

     @Override public boolean onOptionsItemSelected(MenuItem item) { // Only handle with DrawerToggle if the drawer indicator is enabled. if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { // Handle home button in non-drawer mode case android.R.id.home: // Use getSupportFragmentManager() to support older devices FragmentManager fragmentManager = getFragmentManager(); fragmentManager.popBackStack(); // Make sure transactions are finished before reading backstack count fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ mDrawerToggle.setDrawerIndicatorEnabled(true); } return true; default: return super.onOptionsItemSelected(item); } } 

    РЕДАКТИРОВАТЬ

    По вопросу о @JJD.

    Фрагменты хранятся / управляются в процессе. Вышеупомянутый код записывается один раз в этой операции, но обрабатывает только onOptionsItemSelected для onOptionsItemSelected .

    В одном из моих приложений мне также нужно было обработать поведение курсора, когда была нажата кнопка «Назад». Это можно обрабатывать, переопределяя onBackPressed .

     @Override public void onBackPressed() { // Use getSupportFragmentManager() to support older devices FragmentManager fragmentManager = getFragmentManager(); fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ super.onBackPressed(); } else { fragmentManager.executePendingTransactions(); fragmentManager.popBackStack(); fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ mDrawerToggle.setDrawerIndicatorEnabled(true); } } }; 

    Обратите внимание на дублирование кода между onOptionsItemSelected и onBackPressed чего можно избежать, создав метод и вызывая этот метод в обоих местах.

    Также обратите внимание, что я добавляю еще два раза executePendingTransactions которые в моем случае были необходимы, или иногда у меня было странное поведение выше каретки.

    Я создал интерфейс для хостинговой деятельности, чтобы обновить состояние представления меню гамбургера. Для fragmentов верхнего уровня я устанавливаю toggle в true и для fragmentов, для которых я хочу отобразить up <стрелка, я установил toggle в false .

     public class SomeFragment extends Fragment { public interface OnFragmentInteractionListener { public void showDrawerToggle(boolean showDrawerToggle); } private OnFragmentInteractionListener mListener; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { this.mListener = (OnFragmentInteractionListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener"); } } @Override public void onResume() { super.onResume(); mListener.showDrawerToggle(false); } } 

    Тогда в моей деятельности …

     public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener { private ActionBarDrawerToggle mDrawerToggle; public void showDrawerToggle(boolean showDrawerIndicator) { mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator); } } 

    Этот ответ работал, но с ним была небольшая проблема. Функция getSupportActionBar().setDisplayHomeAsUpEnabled(false) не вызывалась явно, и это вызывало скрытие значка ящика, даже если в стопке не было элементов, поэтому изменение setActionBarArrowDependingOnFragmentsBackStack() сработало для меня.

     private void setActionBarArrowDependingOnFragmentsBackStack() { int backStackEntryCount = getSupportFragmentManager() .getBackStackEntryCount(); // If there are no items in the back stack if (backStackEntryCount == 0) { // Please make sure that UP CARAT is Hidden otherwise Drawer icon // wont display getSupportActionBar().setDisplayHomeAsUpEnabled(false); // Display the Drawer Icon mDrawerToggle.setDrawerIndicatorEnabled(true); } else { // Show the Up carat getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Hide the Drawer Icon mDrawerToggle.setDrawerIndicatorEnabled(false); } } 

    Логика понятна. Показывать кнопку «Назад», если четкий fragment fragmentа прозрачен. Покажите анимацию анимации гамбургера, если fragment fragmentа не ясен.

     getSupportFragmentManager().addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { syncActionBarArrowState(); } } ); private void syncActionBarArrowState() { int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0); } //add these in Your NavigationDrawer fragment class public void setDrawerIndicatorEnabled(boolean flag){ ActionBar actionBar = getActionBar(); if (!flag) { mDrawerToggle.setDrawerIndicatorEnabled(false); actionBar.setDisplayHomeAsUpEnabled(true); mDrawerToggle.setHomeAsUpIndicator(getColoredArrow()); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } mDrawerToggle.syncState(); getActivity().supportInvalidateOptionsMenu(); } //download back button from this(https://www.google.com/design/icons/) website and add to your project private Drawable getColoredArrow() { Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp); Drawable wrapped = DrawableCompat.wrap(arrowDrawable); if (arrowDrawable != null && wrapped != null) { // This should avoid tinting all the arrows arrowDrawable.mutate(); DrawableCompat.setTint(wrapped, Color.GRAY); } return wrapped; } 

    Если вы посмотрите на приложение GMAIL и придете сюда, чтобы найти значок carret / vacance.

    Я прошу вас сделать это, ни один из вышеприведенных ответов не был ясен. я смог изменить принятый ответ.

    • NavigationDrawer -> Listview содержит подfragmentы.


    • субfragmentы будут перечислены следующим образом:

    • firstFragment == position 0 —> у этого будут субfragmentы -> fragment

    • secondFragment
    • третий fragment и т. д ….

    В FirstFragment у вас есть другой fragment.

    Назовите это на DrawerActivity

     getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if (getFragmentManager().getBackStackEntryCount() > 0) { mDrawerToggle.setDrawerIndicatorEnabled(false); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } } }); 

    и в fragmentе

      setHasOptionsMenu(true); @Override public boolean onOptionsItemSelected(MenuItem item) { // Get item selected and deal with it switch (item.getItemId()) { case android.R.id.home: //called when the up affordance/carat in actionbar is pressed activity.onBackPressed(); return true; } return false; } 

    В методе активности OnBackPressed Drawer установите для переключателя ящика значение true, чтобы снова включить значок списка навигации.

    Спасибо, Pusp

    Вы можете посмотреть на этот маленький пример! https://github.com/oskarko/NavDrawerExample

    ИМО, используя onNavigateUp () (как показано здесь ) в решении riwnodennyk или Tom’s, является более чистым и, похоже, работает лучше. Просто замените код onOptionsItemSelected следующим:

     @Override public boolean onSupportNavigateUp() { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { // handle up navigation return true; } else { return super.onSupportNavigateUp(); } } 
    Interesting Posts

    Выбор элементов по атрибуту

    Ограничить максимальное количество строк таблицы sqlite

    Самый быстрый способ определить, существует ли целое число между двумя целыми числами (включительно) с известными наборами значений

    Как использовать двойные или одиночные скобки, круглые скобки, фигурные скобки

    Перенаправление весной MVC

    Простой клиент Kerberos в Java?

    Как создавать элементы управления динамически в MVC 3 на основе XML-файла

    Могу ли я увеличить лимит оперативной памяти Windows XP 32bit на 3,5 ГБ с помощью / PAE?

    Не удается установить Win XP поверх Vista / Ubuntu

    Использование WebClient или WebRequest для входа на веб-сайт и доступа к данным

    UIButton Touch и Hold

    Как получить имя первого дня месяца?

    Путаница относительно модификации константной переменной с помощью указателей

    Функции вызова со специальным префиксом / суффиксом

    Неожиданное свободное дисковое пространство уменьшается?

    Давайте будем гением компьютера.