用片段向上导航

我有一个选项卡式的 Actionbar/viewpager 布局,有三个选项卡,分别是 ABC。在选项卡 C选项卡(片段)中,我添加了另一个片段,即片段 D。和

 DFragment f= new DFragment();
ft.add(android.R.id.content, f, "");
ft.remove(CFragment.this);
ft.addToBackStack(null);
ft.commit();

我修改了 DFragment 的 onResume 中的操作栏来添加按钮:

ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);

现在在片段,当我按硬件(电话)后退按钮,我返回到原来的选项卡式(ABC)布局与 CFragment 选中。我如何实现这个功能与动作栏向上按钮?

105164 次浏览

I got it. just override onOptionsItemSelected in hosting activity and popup the backstack, e.g.

public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}
break;
}
}
return super.onOptionsItemSelected(item);
}

Call getActionBar().setDisplayHomeAsUpEnabled(boolean); and getActionBar().setHomeButtonEnabled(boolean); in onBackStackChanged() as explained in an answer below.

Implement OnBackStackChangedListener and add this code to your Fragment Activity.

@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;
}

If you have one parent activity and want this up button to work as a back button, you can use this code:

add this to the onCreate in your main activity class

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);
}
}


});

and then add onOptionsItemSelected like so:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
....
}

I generally use this all the time and seems pretty legit

This is a very good and reliable solution: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

The guy has made an abstract fragment that handles the backPress behaviour and is switching between the active fragments using the strategy pattern.

For some of you there maybe a little drawback in the abstract class...

Shortly, the solution from the link goes like this:

// 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);
}
}

And usage in the activity:

// 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;
}
}

you can go back with up button like back button ;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}

I used a combination of Roger Garzon Nieto's and sohailaziz's answers. My app has a single MainActivity, and fragments A, B, C that are loaded into it. My "home" fragment (A) implements OnBackStackChangedListener, and checks the size of the backStack; if it's less than one, then it hides the UP button. Fragments B and C always load the back button (in my design, B is launched from A, and C is launched from B). The MainActivity itself just pops the backstack on UP button tap, and has methods to show/hide the button, which the fragments call:

MainActivity:

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); }

fragmentA (implements 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();
}
}

fragmentB, fragmentC:

public void onCreate(Bundle savedinstanceSate) {
// show the UP button
((MainActivity)getActivity()).showUpButton();


// other fragment init stuff
...
}

I know this question is old, but may be someone (like me) also needs it.

If your Activity extends AppCompatActivity, you can use a simpler (two-step) solution:

1 - Whenever you add a non-home fragment just show the up button, right after commiting the fragment transaction. Like this:

    // ... add a fragment
// Commit the transaction
transaction.commit();


getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Then when UP button is pressed, you hide it.

@Override
public boolean onSupportNavigateUp() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
return true;
}

That's it.

This worked for me. Override onSupportNavigateUp and onBackPressed, for example (code in 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
}

Now in the fragment, if you display the up arrow

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Clicking on it takes you back the previous activity.

Kotlin:

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 }


...
}

If you want to go back to your previous activity if this activity has an empty stack of fragments:

This could be useful if you have a MainActivity and you are navigating to e.g. a SettingsActivity with nested prefernceScreens. NavigateUp will pop fragments until you can finish the SettingsActivity to go back to 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;
}

Note: setDisplayHomeAsUpEnabled(true); in fragment activities onCreate()