При использовании навигационного ящика разработчики Android рекомендуют, чтобы в ActionBar «только те экраны, которые представлены в навигационном ящике, должны иметь изображение навигационного ящика» и что «все остальные экраны имеют традиционный карат».
Подробнее см. Здесь: http://youtu.be/F5COhlbpIbY
Я использую одно действие для управления несколькими уровнями фрагментов и может отображать изображение навигационного ящика для отображения и функционирования на всех уровнях.
При создании фрагментов более низкого уровня я могу вызвать ActionBarDrawerToggle
setDrawerIndicatorEnabled(false)
чтобы скрыть изображение навигационного ящика и отобразить Up-up
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();
Проблема, с которой я столкнулась, – это вернуться к фрагментам верхнего уровня, которые по-прежнему показывает вверх карат, а не исходное изображение навигационного ящика. Любые предложения о том, как «обновить» ActionBar на фрагментах верхнего уровня, чтобы повторно отобразить изображение навигационного ящика?
Предложение Тома работало для меня. Вот что я сделал:
Это действие контролирует все фрагменты в приложении.
При создании новых фрагментов для замены других я устанавливаю 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) }
В фрагментах я изменил 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; … }
Вы написали, что для реализации фрагментов нижнего уровня вы заменяете существующий фрагмент, в отличие от реализации фрагмента нижнего уровня в новом действии.
Я бы подумал, что тогда вам придется реализовать функциональность назад вручную: когда пользователь нажал назад, у вас есть код, который выталкивает стек (например, в Activity::onBackPressed
override). Итак, где бы вы ни делали это, вы можете изменить setDrawerIndicatorEnabled
.
Это легко, как 1-2-3.
Если вы хотите достичь:
1) Индикатор ящика – при отсутствии фрагментов в Back Stack или ящике
2) Стрелка – когда некоторые фрагменты находятся в 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-разработчиках в других советах о поведении индикатора 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. Таким образом, вам не нужно добавлять одинаковый код ко всем фрагментам.
@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, является опрятным, но при использовании фрагментов оно может быть расширено, чтобы автоматически обрабатывать настройку индикатора выдвижного ящика, когда стопка заднего фона пуста.
@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
для 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
которые в моем случае были необходимы, или иногда я иногда executePendingTransactions
странное поведение выше каретки.
Я создал интерфейс для хостинговой деятельности, чтобы обновить состояние представления меню гамбургера. Для фрагментов верхнего уровня я устанавливаю toggle в true
и для фрагментов, для которых я хочу отобразить 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); } }
Логика понятна. Показывать кнопку «Назад», если ядро фрагмента обратно. Покажите анимацию анимации материала в гамбургере, если фрагмент фрагмента не ясен.
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 содержит подфрагменты.
Субфрагменты будут перечислены следующим образом:
FirstFragment == position 0 —> у этого будут субфрагменты -> фрагмент
В firstFragment у вас есть другой фрагмент.
Вызовите это на DrawerActivity
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if (getFragmentManager().getBackStackEntryCount() > 0) { mDrawerToggle.setDrawerIndicatorEnabled(false); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } } });
И в фрагменте
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
ИМО, используя onNavigateUp () (как показано здесь ) в решении riwnodennyk или Tom's, является более чистым и, похоже, работает лучше. Просто замените код onOptionsItemSelected следующим:
@Override public boolean onSupportNavigateUp() { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { // handle up navigation return true; } else { return super.onSupportNavigateUp(); } }
Вы можете посмотреть на этот маленький пример! https://github.com/oskarko/NavDrawerExample