Обработка заголовка ActionBar с помощью стека фрагмента?
У меня есть Activity
, где я загружаюсь в ListFragment
, и, щелкнув, он сверлит уровень и отобразит новый тип ListFragment
, заменив исходный (используя метод showFragment
ниже). Это помещается в задний стек.
В начале активность показывает заголовок по умолчанию на панели действий (т.е. автоматически устанавливается на основе приложения android:label
).
При отображении списка для следующего уровня в иерархии имя элемента, на который нужно щелкнуть, должно стать заголовком панели действий.
Однако при нажатии Back я хотел бы восстановить исходный заголовок по умолчанию. Это не то, о чем FragmentTransaction
знает, поэтому заголовок не восстанавливается.
Я смутно читал о FragmentBreadCrumbs
, но для этого, похоже, требуется использование настраиваемого представления. Я использую ActionBarSherlock и предпочитаю, чтобы у меня не было собственного пользовательского названия.
Каков наилучший способ сделать это? Возможно ли это без загрузки кода шаблона и отслеживания названий, указанных на этом пути?
protected void showFragment(Fragment f) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.fragment_container, f);
ft.addToBackStack(null);
ft.commit();
}
Ответы
Ответ 1
В каждом фрагменте и каждом действии я меняю заголовок наподобие этого. Таким образом, активное название всегда будет правильным:
@Override
public void onResume() {
super.onResume();
// Set title
getActivity().getActionBar()
.setTitle(R.string.thetitle);
}
Есть случаи, когда onResume не вызывается внутри фрагментов. В некоторых из этих случаев мы можем использовать:
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
// Set title
getActivity().getActionBar()
.setTitle(R.string.thetitle);
}
}
Ответ 2
Поскольку исходный ответ довольно старый, это может также помочь. Как указано в документации, можно захотеть зарегистрировать listener
для прослушивания изменений заднего стека в хостинге Activity
:
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
Затем определите ситуацию в методе обратного вызова и установите правильный заголовок, не получив доступ к ActionBar
из Fragment
.
Это более элегантное решение, так как Fragment
не обязательно знать о существовании ActionBar
, а Activity
обычно является местом, которое управляет стоп-кадром, поэтому, если он обрабатывается, кажется, более подходящим, Fragment
следует всегда рассматривать только по своему содержанию, а не по окружению.
Подробнее о теме в документации.
Ответ 3
Пусть управляющая деятельность выполняет всю работу следующим образом:
Слушайте события backstack (в onCreate() активности):
// Change the title back when the fragment is changed
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment fragment = getFragment();
setTitleFromFragment(fragment);
}
});
Получить текущий фрагмент из контейнера:
/**
* Returns the currently displayed fragment.
* @return
* Fragment or null.
*/
private Fragment getFragment() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
return fragment;
}
Установите фрагмент внутри представления содержимого:
private void setFragment(Fragment fragment, boolean addToBackStack) {
// Set the activity title
setTitleFromFragment(fragment);
.
.
.
}
Ответ 4
Warpzit прав. Это также решает проблему названия при изменении ориентации устройства. Также, если вы используете поддержку v7 для панели действий, вы можете получить панель действий из фрагмента следующим образом:
@Override
public void onResume() {
super.onResume();
((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}
Ответ 5
Лучше всего позволить ОС выполнять как можно больше работы.
Предполагая, что каждый фрагмент правильно назван с использованием .addToBackStack( "title" ), тогда
вы можете переопределить onBackPressed что-то вроде этого для достижения желаемого поведения:
// this example uses the AppCompat support library
// and works for dynamic fragment titles
@Override
public void onBackPressed() {
FragmentManager fragmentManager = getSupportFragmentManager();
int count = fragmentManager.getBackStackEntryCount();
if (count <= 1) {
finish();
}
else {
String title = fragmentManager.getBackStackEntryAt(count-2).getName();
if (count == 2) {
// here I am using a NavigationDrawer and open it when transitioning to the initial fragment
// a second back-press will result in finish() being called above.
mDrawerLayout.openDrawer(mNavigationDrawerFragment.getView());
}
super.onBackPressed();
Log.v(TAG, "onBackPressed - title="+title);
getSupportActionBar().setTitle(title);
}
}
Ответ 6
Я использую аналогичное решение для подхода Ли, но вместо этого вместо метода onBackStackChanged()
.
Сначала я устанавливаю имя фрагмента при добавлении транзакции в задний стек.
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_content, fragment)
.addToBackStack(fragmentTitle)
.commit();
Затем я переопределяю метод onBackStackChanged()
, и я вызываю setTitle()
с последним именем записи Backstack.
@Override
public void onBackStackChanged() {
int lastBackStackEntryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
FragmentManager.BackStackEntry lastBackStackEntry =
getSupportFragmentManager().getBackStackEntryAt(lastBackStackEntryCount);
setTitle(lastBackStackEntry.getName());
}
Ответ 7
Использовать метод фрагментов:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
Он вызывается для каждого фрагмента, но onResume не является.
Ответ 8
Лучший подход заключается в использовании андроида, предоставляемого интерфейсом OnBackStackChangedListener метода onBackStackChanged().
Допустим, у нас есть ящик для навигации с 4 параметрами, к которым пользователь может перейти. В этом случае у нас будет 4 фрагмента. Сначала посмотрим на код, а затем я объясню, как работает.
private int mPreviousBackStackCount = 0;
private String[] title_name = {"Frag1","Frag2","Frag3","Frag4"};
Stack<String> mFragPositionTitleDisplayed;
public class MainActivity extends ActionBarActivity implements FragmentManager.OnBackStackChangedListener
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
....
....
getSupportFragmentManager().addOnBackStackChangedListener(this);
mFragPositionTitleDisplayed = new Stack<>();
}
public void displayFragment() {
Fragment fragment = null;
String title = getResources().getString(R.string.app_name);
switch (position) {
case 0:
fragment = new Fragment1();
title = title_name[position];
break;
case 1:
fragment = new Fragment2();
title = title_name[position];
break;
case 2:
fragment = new Fragment3();
title = title_name[position];
break;
case 3:
fragment = new Fragment4();
title = title_name[position];
break;
default:
break;
}
if (fragment != null) {
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container_body, fragment)
.addToBackStack(null)
.commit();
getSupportActionBar().setTitle(title);
}
}
@Override
public void onBackStackChanged() {
int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
if(mPreviousBackStackCount >= backStackEntryCount) {
mFragPositionTitleDisplayed.pop();
if (backStackEntryCount == 0)
getSupportActionBar().setTitle(R.string.app_name);
else if (backStackEntryCount > 0) {
getSupportActionBar().setTitle(mFragPositionTitleDisplayed.peek());
}
mPreviousBackStackCount--;
}
else{
mFragPositionTitleDisplayed.push(title_name[position]);
mPreviousBackStackCount++;
}
}
В показанном коде есть метод displayFragment(). Здесь я показываю фрагмент на основе опции, выбранной из навигационного ящика. Переменная позиция соответствует позиции элемента, нажатого из ListView или RecyclerView в навигационном ящике. Я установил заголовок панели действий соответственно с помощью getSupportActionBar.setTitle(название), где заголовок хранит соответствующее название названия.
Всякий раз, когда мы нажимаем элемент из навигационного ящика, фрагмент отображается в зависимости от элемента, который был нажат на пользователя. Но на задней стороне этот фрагмент добавлен в backstack, и метод onBackStachChanged() получает удар. То, что я сделал, это то, что я создал переменную mPreviousBackStackCount и инициализировал ее до 0. Я также создал дополнительный стек, который будет хранить имена названий панели действий. Всякий раз, когда я добавляю новый фрагмент в backstack, я добавляю соответствующее название названия в созданный стек. На противоположной стороне всякий раз, когда я нажимаю кнопку назад, вызывается onBackStackChanged(), и я вывожу последнее имя заголовка из своего стека и устанавливаю заголовок на имя, полученное методом peek() стека.
Пример:
Давайте скажем, что наш андроид backstack пуст:
Нажмите "Выбор 1" из навигационного ящика:
вызывается onBackStachChanged(), а фрагмент 1 добавляется к андроидному backstack, backStackEntryCount устанавливается в 1, а Frag1 переносится в мой стек, а размер mFragPositionTitleDisplayed становится равным 1.
Нажмите Выбор 2 из навигационного ящика:
вызывается onBackStachChanged(), а фрагмент 2 добавляется к backstack андроида, backStackEntryCount устанавливается в 2, а Frag2 помещается в мой стек, а размер mFragPositionTitleDisplayed становится равным 2.
Теперь у нас есть 2 элемента как в стеке android, так и в моем стеке. При нажатии кнопки onBackStackChanged() вызывается, а значение backStackEntryCount равно 1. Код входит в часть if и выдает последнюю запись из моего стека. Таким образом, андроид backstack имеет только 1 фрагмент - "Фрагмент 1", и мой стек имеет только 1 заголовок - "Frag1". Теперь я просто заглядываю() в заголовок из своего стека и устанавливаю панель действий в этот заголовок.
Помните: для установки действия bat title используйте peek(), а не pop(), иначе ваше приложение выйдет из строя, когда вы откроете более 2 фрагментов и попытаетесь вернуться назад, нажав кнопку "Назад".
Ответ 9
Вы можете решить с помощью onKeyDown!
У меня есть bool
mainisopen = true < - MainFragment Visible
другой фрагмент mainisopen = false
и вот мой код:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == false) {
mainisopen = true;
HomeFrag fragment = new HomeFrag();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragmet_cont, fragment);
fragmentTransaction.commit();
navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
navigationView.setNavigationItemSelectedListener(this);
this.setTitle("Digi - Home"); //Here set the Title back
return true;
} else {
if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == true) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Wollen sie die App schliessen!");
builder.setCancelable(true);
builder.setPositiveButton("Ja!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
System.exit(1);
}
});
builder.setNegativeButton("Nein!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "Applikation wird fortgesetzt", Toast.LENGTH_SHORT).show();
}
});
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
Ответ 10
Как описано здесь, мое решение заключается в добавлении этого кода в метод MainActivity onCreate(): и изменении заголовка панели действий
FragmentManager fragmentManager=getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment currentFragment = fragmentManager.findFragmentById(R.id.My_Container_1_ID);
currentFragment.onResume();
}
});
и изменение заголовка панели действий в методе фрагмента onResume()
@Override
public void onResume() {
super.onResume();
AppCompatActivity activity = (AppCompatActivity) getActivity();
ActionBar actionBar = activity.getSupportActionBar();
if(actionBar!=null) {
actionBar.setTitle("Fragment Title");
actionBar.setSubtitle("Subtitle");
}
}
Ответ 11
Чтобы обновить заголовок панели действий, нажмите Назад. Просто поставьте
getActivity.setTitle( "название" )
внутри метода onCreateView.