Переключение между изображением ящика навигации Android и кареткой при использовании фрагментов
при использовании навигационного ящика разработчики Android рекомендуют, чтобы в ActionBar "только те экраны, которые представлены в навигационном ящике, фактически имели изображение навигационного ящика" и что "все остальные экраны имеют традиционный up carat."
см. здесь для деталей: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();
проблема, с которой я сталкиваюсь, когда я возвращаюсь к фрагментам верхнего уровня, которые все еще показывает Up carat вместо исходного изображения навигационного ящика. Любые предложения о том, как" обновить " панель действий на фрагментах верхнего уровня, чтобы повторно отобразить изображение навигационного ящика?
решение
предложение Тома сработало для меня. Вот что я сделал:--18-->
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)
}
In в LowerLevelFragments
в фрагментах, которые я изменил onCreate
и onOptionsItemSelected
такой:
на onCreate
добавил setHasOptionsMenu(true)
включить настройки меню опции. Также установить setDisplayHomeAsUpEnabled(true)
включить в actionbar:
@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;
…
}
12 ответов
вы написали, что для реализации фрагментов нижнего уровня вы заменяете существующий фрагмент, в отличие от реализации фрагмента нижнего уровня в новом действии.
Я бы подумал, что вам придется реализовать обратную функциональность вручную: когда пользователь нажал назад, у вас есть код, который всплывает стек (например, в Activity::onBackPressed
переопределить). Итак, где бы вы это ни делали, вы можете отменить setDrawerIndicatorEnabled
.
Это просто, как 1-2-3.
Если вы хотите достичь:
1) Индикатор Ящика - когда никакие части в задней стоге или ящике не раскрыты
2) стрелка - когда некоторые фрагменты находятся в задней стеке
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);
}
}
П. С. Вижу создание навигационного ящика для разработчиков 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();
}
});
У меня возникли проблемы с реализацией навигации по ящику с помощью кнопки "Домой", все работало, кроме действия buton.
попробуйте обработать выбор домашнего элемента в 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, аккуратно, но его можно расширить при использовании фрагментов, чтобы автоматически обрабатывать настройку индикатора ящика, когда 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
что в моем случае было необходимо, иначе у меня иногда было странное поведение каретки.
Я создал интерфейс для хостинга, чтобы обновить состояние просмотра меню гамбургера. Для фрагментов верхнего уровня я установил переключатель в true
и для фрагментов, для которых я хочу отобразить стрелку вверх , я установил переключатель в 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);
}
}
этой ответ работал, но с ним была небольшая проблема.
The getSupportActionBar().setDisplayHomeAsUpEnabled(false)
не вызывался явно, и это заставляло значок ящика быть скрытым, даже когда в backstack не было элементов, поэтому изменение 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/affordance..
Я бы попросил вас сделать это, ни один из вышеперечисленных ответ был ясен. я смог изменить принятый ответ.
-
NavigationDrawer --> Listview содержит вложенные фрагменты.
subfragments будет указан такой
firstFragment == позиция 0 ---> это будет subfragments --> фрагмент
- secondFragment
- thirdFragment и так далее....
в 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 метод активности ящика установите переключатель ящика в true, чтобы снова включить значок списка навигации.
спасибо, Pusp
вы можете посмотреть на этом маленьком примере! https://github.com/oskarko/NavDrawerExample
IMO, используя onNavigateUp () (как показано здесь) в решении riwnodennyk или Тома чище и, кажется, работает лучше. Просто замените код onOptionsItemSelected следующим:
@Override
public boolean onSupportNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
// handle up navigation
return true;
} else {
return super.onSupportNavigateUp();
}
}