Jak wznowić Fragment z Backstacka, jeśli istnieje

Uczę się używać fragmentów. Mam trzy instancje Fragment, które są inicjowane na górze klasy. Dodaję ten fragment do takiej aktywności:

Deklarowanie i inicjowanie:

Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();

Zastępowanie / Dodawanie:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();

Te fragmenty działają poprawnie. Każdy fragment jest dołączony do działania i jest zapisywany na tylnym stosie bez żadnego problemu.

Więc kiedy odpalę A, C, i wtedy B, stos wygląda jak to:

| |
|B|
|C|
|A|
___

I kiedy nacisnę przycisk "Wstecz", {[11] } zostaje zniszczony i C zostaje wznowiony.

Ale kiedy uruchamiam fragment A po raz drugi, zamiast wznawiać ze stosu, jest on dodawany u góry stosu

| |
|A|
|C|
|A|
___

Ale chcę wznowić {[9] } i zniszczyć wszystkie fragmenty na nim (jeśli istnieją). Właściwie, po prostu lubię domyślne zachowanie stosu wstecznego.

Jak to osiągnąć?

Oczekiwane: (A powinno być wznowione i do góry fragmenty powinny być zniszczone)

| |
| |
| |
|A|
___

Edit: (suggested by A -- C)

To jest mój kod próbny:

private void selectItem(int position) {
        Fragment problemSearch = null, problemStatistics = null;
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        String backStateName = null;
        Fragment fragmentName = null;
        boolean fragmentPopped = false;
        switch (position) {
        case 0:
            fragmentName = profile;
            break;
        case 1:
            fragmentName = submissionStatistics;
            break;
        case 2:
            fragmentName = solvedProblemLevel;
            break;
        case 3:
            fragmentName = latestSubmissions;
            break;
        case 4:
            fragmentName = CPExercise;
            break;
        case 5:
            Bundle bundle = new Bundle();
            bundle.putInt("problem_no", problemNo);
            problemSearch = new ProblemWebView();
            problemSearch.setArguments(bundle);
            fragmentName = problemSearch;
            break;
        case 6:
            fragmentName = rankList;
            break;
        case 7:
            fragmentName = liveSubmissions;
            break;
        case 8:
            Bundle bundles = new Bundle();
            bundles.putInt("problem_no", problemNo);
            problemStatistics = new ProblemStatistics();
            problemStatistics.setArguments(bundles);
            fragmentName = problemStatistics;
        default:
            break;
        }
        backStateName = fragmentName.getClass().getName();
        fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
        if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(backStateName);
        ft.commit();

        // I am using drawer layout
        mDrawerList.setItemChecked(position, true);
        setTitle(title[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

Problem polega na tym, że po uruchomieniu A, a następnie B, naciśnij "back", B jest usuwany i A jest wznawiany. i naciśnięcie "wstecz" po raz drugi powinien zamknąć aplikację. Ale pokazuje puste okno i muszę nacisnąć ponownie trzeci raz, aby je zamknąć.

Również, gdy uruchamiam A, następnie B, następnie C, następnie B jeszcze raz...

Oczekiwano:

| |
| |
|B|
|A|
___

Rzeczywista:

| |
|B|
|B|
|A|
___

Czy powinienem nadpisać onBackPressed() jakimś dostosowaniem, czy coś mi umyka?

Author: Braden Best, 2013-08-19

4 answers

Czytając dokumentację , istnieje sposób, aby otworzyć tylny stos na podstawie nazwy transakcji lub id podanego przez commit. Używanie nazwy Może być łatwiejsze, ponieważ nie powinno wymagać śledzenia liczby, która może zmienić i wzmocnić logikę "unique back stack entry".

Ponieważ chcesz tylko jeden wpis na stos tylny Fragment, Utwórz nazwę stanu tylnego fragmentu jako nazwę klasy (poprzez getClass().getName()). Następnie podczas zastępowania Fragment, użyj popBackStackImmediate() metoda. Jeśli zwraca true, oznacza to, że w tylnym stosie znajduje się instancja fragmentu. Jeśli nie, wykonaj logikę wymiany fragmentu.

private void replaceFragment (Fragment fragment){
  String backStateName = fragment.getClass().getName();

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment);
    ft.addToBackStack(backStateName);
    ft.commit();
  }
}

EDIT

Problem polega na tym, że kiedy uruchamiam A, a potem B, wciskam przycisk back, B jest usuwany i A jest wznawiany. i naciśnięcie ponownie przycisku Wstecz powinno zamknij aplikację. Ale to pokazuje puste okno i trzeba jeszcze nacisnąć żeby go zamknąć.

To dlatego, że FragmentTransaction jest dodano do tylnego stosu, aby upewnić się, że możemy pop fragmenty na górze później. Szybkie rozwiązanie tego problemu polega na nadpisaniu onBackPressed() i zakończeniu działania, jeśli tylny stos zawiera tylko 1 Fragment

@Override
public void onBackPressed(){
  if (getSupportFragmentManager().getBackStackEntryCount() == 1){
    finish();
  }
  else {
    super.onBackPressed();
  }
}

Jeśli chodzi o zduplikowane wpisy tylnego stosu, twoja Instrukcja warunkowa, która zastępuje fragment, jeśli nie został wyrzucony, jest wyraźnie Inna niż mój oryginalny fragment kodu. pękła.

Coś takiego powinno być bliżej tego, czego chcesz:

private void replaceFragment (Fragment fragment){
  String backStateName =  fragment.getClass().getName();
  String fragmentTag = backStateName;

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment, fragmentTag);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
  } 
}

Tryb warunkowy został nieco zmieniony, ponieważ wybranie tego samego fragmentu, gdy był widoczny, również spowodowało zduplikowanie wpisów.

Realizacja:

Sugeruję, aby nie rozdzielać zaktualizowanej metody replaceFragment(), Jak to zrobiłeś w swoim kodzie. Cała logika jest zawarta w tej metodzie i poruszanie się części może powodować problemy.

Oznacza to, że należy skopiować zaktualizowany replaceFragment() method into your class then change

backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();

Więc jest to po prostu

replaceFragment (fragmentName);

Edytuj #2

Aby zaktualizować szufladę, gdy zmieni się tylny stos, stwórz metodę, która akceptuje we fragmencie i porównuje nazwy klas. Jeśli coś pasuje, Zmień tytuł i wybór. Dodaj również OnBackStackChangedListener i niech wywoła Twoją metodę aktualizacji, jeśli istnieje prawidłowy Fragment.

Na przykład w aktywności onCreate() dodaj

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {

  @Override
  public void onBackStackChanged() {
    Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
    if (f != null){
      updateTitleAndDrawer (f);
    }

  }
});

I inne "metoda": {]}

private void updateTitleAndDrawer (Fragment fragment){
  String fragClassName = fragment.getClass().getName();

  if (fragClassName.equals(A.class.getName())){
    setTitle ("A");
    //set selected item position, etc
  }
  else if (fragClassName.equals(B.class.getName())){
    setTitle ("B");
    //set selected item position, etc
  }
  else if (fragClassName.equals(C.class.getName())){
    setTitle ("C");
    //set selected item position, etc
  }
}

Teraz, gdy zmieni się tylny stos, tytuł i zaznaczona pozycja będą odzwierciedlać widoczne Fragment.

 240
Author: A--C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-19 16:22:33

Myślę, że ta metoda rozwiąże twój problem:

public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) {


    FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager ();
    manager.findFragmentByTag ( tag );
    FragmentTransaction ft = manager.beginTransaction ();

    if (manager.findFragmentByTag ( tag ) == null) { // No fragment in backStack with same tag..
        ft.add ( fragmentHolderLayoutId, fragment, tag );
        ft.addToBackStack ( tag );
        ft.commit ();
    }
    else {
        ft.show ( manager.findFragmentByTag ( tag ) ).commit ();
    }
}

Który został pierwotnie opublikowany w To Pytanie

 4
Author: erluxman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:02

Krok 1: zaimplementuj interfejs z klasą aktywności

public class AuthenticatedMainActivity extends Activity implements FragmentManager.OnBackStackChangedListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        .............
        FragmentManager fragmentManager = getFragmentManager();           
        fragmentManager.beginTransaction().add(R.id.frame_container,fragment, "First").addToBackStack(null).commit();
    }

    private void switchFragment(Fragment fragment){            
      FragmentManager fragmentManager = getFragmentManager();
      fragmentManager.beginTransaction()
        .replace(R.id.frame_container, fragment).addToBackStack("Tag").commit();
    }

    @Override
    public void onBackStackChanged() {
    FragmentManager fragmentManager = getFragmentManager();

    System.out.println("@Class: SummaryUser : onBackStackChanged " 
            + fragmentManager.getBackStackEntryCount());

    int count = fragmentManager.getBackStackEntryCount();

    // when a fragment come from another the status will be zero
    if(count == 0){

        System.out.println("again loading user data");

        // reload the page if user saved the profile data

        if(!objPublicDelegate.checkNetworkStatus()){

            objPublicDelegate.showAlertDialog("Warning"
                    , "Please check your internet connection");

        }else {

            objLoadingDialog.show("Refreshing data..."); 

            mNetworkMaster.runUserSummaryAsync();
        }

        // IMPORTANT: remove the current fragment from stack to avoid new instance
        fragmentManager.removeOnBackStackChangedListener(this);

    }// end if
   }       
}

Krok 2: Po wywołaniu innego fragmentu dodaj tę metodę:

String backStateName = this.getClass().getName();

FragmentManager fragmentManager = getFragmentManager();
fragmentManager.addOnBackStackChangedListener(this); 

Fragment fragmentGraph = new GraphFragment();
Bundle bundle = new Bundle();
bundle.putString("graphTag",  view.getTag().toString());
fragmentGraph.setArguments(bundle);

fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragmentGraph)
.addToBackStack(backStateName)
.commit();
 3
Author: Vinod Joshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-06 18:34:19
getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {

    @Override
    public void onBackStackChanged() {

        if(getFragmentManager().getBackStackEntryCount()==0) {
            onResume();
        }    
    }
});
 0
Author: gushedaoren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-04 08:13:23