Панель действий вверх навигации с фрагментами
У меня есть вкладка Actionbar/ макет viewpager с тремя вкладками, скажем, A, B и C. На вкладке C (фрагмент) я добавляю еще один фрагмент, скажем, фрагмент D. с
DFragment f= new DFragment();
ft.add(android.R.id.content, f, "");
ft.remove(CFragment.this);
ft.addToBackStack(null);
ft.commit();
Я изменяю панель действий в onFesume DFragment, чтобы добавить кнопку добавления:
ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);
Теперь в DFragment, когда я нажимаю аппаратную (телефонную) кнопку "Назад", я возвращаюсь к исходному макету с вкладками (ABC) с выбранным CFragment. Как я могу достичь этой функциональности с помощью кнопки вверх на панели действий?
10 ответов
Воплощать в жизнь OnBackStackChangedListener
и добавьте этот код в свою активность фрагмента.
@Override
public void onCreate(Bundle savedInstanceState) {
//Listen for changes in the back stack
getSupportFragmentManager().addOnBackStackChangedListener(this);
//Handle when activity is recreated like on orientation Change
shouldDisplayHomeUp();
}
@Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}
@Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
Я понял. просто переопределите onOptionsItemSelected в хостинге и откройте backstack, например
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
}
return true;
default:
return super.onOptionsItemSelected(item);;
}
}
Вызов getActionBar().setDisplayHomeAsUpEnabled(boolean);
а также getActionBar().setHomeButtonEnabled(boolean);
в onBackStackChanged()
как объяснено в ответе ниже.
Если у вас есть одно родительское действие и вы хотите, чтобы эта кнопка "вверх" работала как кнопка "Назад", вы можете использовать этот код:
добавьте это к onCreate в вашем основном классе деятельности
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
}
}
});
и затем добавьте onOptionsItemSelected следующим образом:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
....
}
Я обычно использую это все время и кажется вполне законным
Вы можете вернуться с помощью кнопки вверх, как кнопка возврата;
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
Я знаю, что этот вопрос старый, но, возможно, кому-то (как и мне) он тоже нужен.
Если ваша активность расширяет AppCompatActivity, вы можете использовать более простое (двухэтапное) решение:
1 - Каждый раз, когда вы добавляете не домашний фрагмент, просто показывайте кнопку вверх, сразу после совершения транзакции фрагмента. Как это:
// ... add a fragment
// Commit the transaction
transaction.commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
2 - Затем, когда нажата кнопка ВВЕРХ, вы прячете ее.
@Override
public boolean onSupportNavigateUp() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
return true;
}
Вот и все.
Я использовал комбинацию ответов Роджера Гарсона Ньето и Сохаилазиза. Мое приложение имеет один MainActivity и фрагменты A, B, C, которые загружены в него. Мой "домашний" фрагмент (A) реализует OnBackStackChangedListener и проверяет размер backStack; если он меньше единицы, он скрывает кнопку ВВЕРХ. Фрагменты B и C всегда загружают кнопку возврата (в моем дизайне B запускается из A, а C запускается из B). Само MainActivity просто вставляет backstack при нажатии кнопки UP и имеет методы, чтобы показать / скрыть кнопку, которую вызывают фрагменты:
Основная деятельность:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
}
return super.onOptionsItemSelected(item);
}
public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }
фрагмент A (реализует FragmentManager.OnBackStackChangedListener):
public void onCreate(Bundle savedinstanceSate) {
// listen to backstack changes
getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);
// other fragment init stuff
...
}
public void onBackStackChanged() {
// enable Up button only if there are entries on the backstack
if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
((MainActivity)getActivity()).hideUpButton();
}
}
фрагмент B, фрагмент C:
public void onCreate(Bundle savedinstanceSate) {
// show the UP button
((MainActivity)getActivity()).showUpButton();
// other fragment init stuff
...
}
Котлин:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
setupHomeAsUp()
}
private fun setupHomeAsUp() {
val shouldShow = 0 < supportFragmentManager.backStackEntryCount
supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
}
override fun onSupportNavigateUp(): Boolean =
supportFragmentManager.popBackStack().run { true }
...
}
Это сработало для меня. Например, переопределить onSupportNavigateUp и onBackPressed (код на Kotlin);
override fun onBackPressed() {
val count = supportFragmentManager.backStackEntryCount
if (count == 0) {
super.onBackPressed()
} else {
supportFragmentManager.popBackStack()
}
}
override fun onSupportNavigateUp(): Boolean {
super.onSupportNavigateUp()
onBackPressed()
return true
}
Теперь во фрагменте, если вы отображаете стрелку вверх
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
Нажав на нее, вы вернетесь к предыдущему действию.
Это очень хорошее и надежное решение: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/
Парень создал абстрактный фрагмент, который обрабатывает поведение backPress и переключается между активными фрагментами, используя шаблон стратегии.
Для некоторых из вас может быть небольшой недостаток в абстрактном классе...
Вкратце, решение по ссылке выглядит так:
// Abstract Fragment handling the back presses
public abstract class BackHandledFragment extends Fragment {
protected BackHandlerInterface backHandlerInterface;
public abstract String getTagText();
public abstract boolean onBackPressed();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(getActivity() instanceof BackHandlerInterface)) {
throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
} else {
backHandlerInterface = (BackHandlerInterface) getActivity();
}
}
@Override
public void onStart() {
super.onStart();
// Mark this fragment as the selected Fragment.
backHandlerInterface.setSelectedFragment(this);
}
public interface BackHandlerInterface {
public void setSelectedFragment(BackHandledFragment backHandledFragment);
}
}
И использование в своей деятельности:
// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment
public class TheActivity extends FragmentActivity implements BackHandlerInterface {
private BackHandledFragment selectedFragment;
@Override
public void onBackPressed() {
if(selectedFragment == null || !selectedFragment.onBackPressed()) {
// Selected fragment did not consume the back press event.
super.onBackPressed();
}
}
@Override
public void setSelectedFragment(BackHandledFragment selectedFragment) {
this.selectedFragment = selectedFragment;
}
}
Если вы хотите вернуться к предыдущей активности, если эта активность имеет пустой стек фрагментов:
Это может быть полезно, если у вас есть MainActivity и вы переходите, например, к SettingsActivity с вложенными экранами предпочтений. NavigateUp будет выталкивать фрагменты до тех пор, пока вы не завершите SettingsActivity, чтобы вернуться к parentActivity/root.
/**
* On actionbar up-button popping fragments from stack until it is empty.
* @return true if fragment popped or returned to parent activity successfully.
*/
@Override
public boolean onSupportNavigateUp() {
//Pop back stack if the up button is pressed.
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
if (canGoBack) {
getSupportFragmentManager().popBackStack();
} else {
finish();
return super.onSupportNavigateUp();
}
return true;
}
Примечание:
setDisplayHomeAsUpEnabled(true);
в фрагментарной деятельности
onCreate()