Переключение между изображениями Android Navigation Drawer и Up caret при использовании фрагментов
При использовании навигационного ящика разработчики Android рекомендуют, чтобы в ActionBar "только те экраны, которые представлены в навигационном ящике, должны иметь изображение навигационного ящика" и что "все остальные экраны имеют традиционный карат".
Подробнее см. здесь: http://youtu.be/F5COhlbpIbY
Я использую одно действие для управления несколькими уровнями фрагментов и могу получить изображение навигационного ящика для отображения и работы на всех уровнях.
При создании фрагментов более низкого уровня я могу вызвать ActionBarDrawerToggle
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, "lowerFrag").addToBackStack(null).commit();
Проблема, с которой я сталкиваюсь, - это когда я перехожу назад к фрагментам верхнего уровня, которые показывается вверх по карат, вместо исходного изображения навигационного ящика. Любые предложения о том, как "обновить" ActionBar на фрагментах верхнего уровня, чтобы повторно отобразить изображение навигационного ящика?
Решение
Том предложил мне работать. Вот что я сделал:
MainActivity
Эта активность управляет всеми фрагментами в приложении.
При создании новых фрагментов для замены других я устанавливаю 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
В фрагментах я изменил 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
при нажатии < он вызывает 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;
…
}
Ответы
Ответ 1
Вы написали, что для реализации фрагментов нижнего уровня вы заменяете существующий фрагмент, в отличие от реализации фрагмента нижнего уровня в новом действии.
Я бы подумал, что тогда вам придется реализовать функциональные возможности назад вручную: когда пользователь нажал назад, у вас есть код, который выдает стек (например, в Activity::onBackPressed
override). Итак, где бы вы ни делали это, вы можете изменить setDrawerIndicatorEnabled
.
Ответ 2
Это легко, как 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);
}
}
P.S. См. Создание навигационного ящика на Android-разработчиках в других советах о поведении индикаторов из 3 строк.
Ответ 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);
}
}
});
Ответ 4
Если ваша кнопка на панели действий не работает, не забудьте добавить слушателя:
// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
У меня есть некоторые проблемы с реализацией навигации на ящике с помощью кнопки "домой", все работает, кроме действия "Бутон".
Ответ 5
Попробуйте выполнить выбор элемента 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);
}
}
Ответ 6
FOLLOW UP
Решение, данное @dzeikei, является опрятным, но при использовании фрагментов оно может быть расширено, чтобы автоматически обрабатывать настройку возврата индикатора ящика, когда стопка backstack пуста.
@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
.
В одном из моих приложений мне также нужно было обработать поведение курсора, когда была нажата кнопка "Назад". Это можно обрабатывать, переопределяя 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
, которые в моем случае были необходимы, или иногда у меня было странное поведение выше каретки.
Ответ 7
Я создал интерфейс для хостинговой деятельности, чтобы обновить состояние представления меню гамбургера. Для фрагментов верхнего уровня я устанавливаю toggle на true
и для фрагментов, для которых я хочу отображать up < arrow Я установил переключатель в 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);
}
}
Ответ 8
Этот ответ работал, но с ним была небольшая проблема.
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);
}
}
Ответ 9
Логика понятна.
Показывать кнопку "Назад", если четкий фрагмент фрагмента прозрачен.
Покажите анимацию анимации материала на гамбургере, если фрагмент не ясен.
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;
}
Ответ 10
Если вы посмотрите на приложение GMAIL и придете сюда, чтобы найти значок carret/vacance.
Я прошу вас сделать это, ни один из приведенных выше ответов не был ясен. я смог изменить принятый ответ.
-
NavigationDrawer → Listview содержит подфрагменты.
-
субфрагменты будут перечислены следующим образом
-
firstFragment == position 0 --- > у этого будут субфрагменты → фрагмент
- secondFragment
- Третий фрагмент и т.д.
В 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
Ответ 11
Вы можете посмотреть на этот маленький пример!
https://github.com/oskarko/NavDrawerExample
Ответ 12
IMO, используя onNavigateUp() (как показано здесь) в riwnodennyk или Tom решение чище и, кажется, работает лучше. Просто замените код onOptionsItemSelected следующим:
@Override
public boolean onSupportNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
// handle up navigation
return true;
} else {
return super.onSupportNavigateUp();
}
}