pegue o último fragmento no backstack


104

Como posso obter a última instância do fragmento adicionada ao backstack (se não souber a tag e a id do fragmento)?

FragmentManager fragManager = activity.getSupportFragmentManager();
FragmentTransaction fragTransacion = fragMgr.beginTransaction();

/****After add , replace fragments 
  (some of the fragments are add to backstack , some are not)***/

//HERE, How can I get the latest added fragment from backstack ??

Respostas:


156

Você pode usar o getName()método FragmentManager.BackStackEntryque foi introduzido na API de nível 14. Este método retornará uma tag que foi usada quando adicionou o Fragment à pilha de backstack addTobackStack(tag).

int index = getActivity().getFragmentManager().getBackStackEntryCount() - 1
FragmentManager.BackStackEntry backEntry = getFragmentManager().getBackStackEntryAt(index);
String tag = backEntry.getName();
Fragment fragment = getFragmentManager().findFragmentByTag(tag);

Você precisa se certificar de que adicionou o fragmento à pilha de apoio assim:

fragmentTransaction.addToBackStack(tag);

24
para localizar fragmento por tag, ele deve ser adicionado / substituído pela mesma tag. FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)ou FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib

3
O problema com isso é se você tiver um fragmento na pilha de retorno duas vezes, não poderá recuperar um fragmento específico quando tudo o que você tem é o índice ou a entidade de backstackentry.
Justin

14
De que adianta chamá-lo de stackse não consigo acessar o fragmento por meio de um método pop?
Kenny Worden


1
Não sei por que, mas findFragmentByTag está retornando null, mesmo quando o depurador mostra claramente que a tag está ok.
htafoya de

49
FragmentManager.findFragmentById(fragmentsContainerId) 

função retorna link para o topo Fragmentna pilha de retorno. Exemplo de uso:

    fragmentManager.addOnBackStackChangedListener(new OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fr = fragmentManager.findFragmentById(R.id.fragmentsContainer);
            if(fr!=null){
                Log.e("fragment=", fr.getClass().getSimpleName());
            }
        }
    });

2
Essa resposta funciona bem em meu projeto, pois adiciono todos os fragmentos, exceto o fragmento raiz, à pilha posterior. Mas acho que essa resposta não funcionará se o fragmento adicionado mais recente não for adicionado à pilha de retorno.
Arne Evertsson de

40

Eu tentei pessoalmente muitas dessas soluções e acabei com esta solução de trabalho:

Adicione este método de utilitário que será usado várias vezes abaixo para obter o número de fragmentos em sua pilha de retorno:

protected int getFragmentCount() {
    return getSupportFragmentManager().getBackStackEntryCount();
}

Então, quando você adicionar / substituir seu fragmento usando o método FragmentTransaction, gere uma tag única para o seu fragmento (por exemplo: usando o número de fragmentos em sua pilha):

getSupportFragmentManager().beginTransaction().add(yourContainerId, yourFragment, Integer.toString(getFragmentCount()));

Finalmente, você pode encontrar qualquer um dos seus fragmentos em sua pilha de retorno com este método:

private Fragment getFragmentAt(int index) {
    return getFragmentCount() > 0 ? getSupportFragmentManager().findFragmentByTag(Integer.toString(index)) : null;
}

Portanto, buscar o fragmento superior em sua pilha pode ser facilmente alcançado chamando:

protected Fragment getCurrentFragment() {
    return getFragmentAt(getFragmentCount() - 1);
}

Espero que isto ajude!


10

este método auxiliar obtém o fragmento do topo da pilha:

public Fragment getTopFragment() {
    if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }
    String fragmentTag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
    return getSupportFragmentManager().findFragmentByTag(fragmentTag);
}

1
Obrigado. A resposta mais elegante. Adicionado aqui para os amantes de Kotlin stackoverflow.com/a/47336504/1761406
Shirane85


6

Há uma lista de fragmentos no fragmentMananger. Esteja ciente de que remover um fragmento não diminui o tamanho da lista (a entrada do fragmento apenas se torna nula). Portanto, uma solução válida seria:

public Fragment getTopFragment() {
 List<Fragment> fragentList = fragmentManager.getFragments();
 Fragment top = null;
  for (int i = fragentList.size() -1; i>=0 ; i--) {
   top = (Fragment) fragentList.get(i);
     if (top != null) {
       return top;
     }
   }
 return top;
}

2
Não confiável. Três razões: 1) Esta é uma lista de todos os fragmentos que o gerenciador de fragmentos conhece, não apenas aqueles na pilha. 2) Não há garantia de que o gerenciador de fragmentos continuará adicionando novos no final da lista. Claro, ele fará isso em testes simples, mas e se um fragmento for removido, deixando um nulo, e então, em algumas circunstâncias, o gerenciador de fragmentos decidir reutilizar esse slot vazio? Não estou dizendo que sim, mas não há garantia de que nunca o fará, em nenhuma circunstância. 3) Se você ou algum futuro programador começar a usar anexar / desanexar para gerenciar fragmentos, isso não corresponderá à pilha.
Toolmaker Steve

Olá, obrigado pela sua solução, mas não é bonita e fácil
Muhammad Ali

Duvido que seja uma solução de trabalho. getFragments()retorna uma coleção reduzida de fragmentos. Talvez o último esteja visível, mas não muitos outros.
CoolMind

5

A resposta dada por deepak goel não funciona para mim porque sempre fico nulo com entry.getName() ;

O que faço é definir uma tag para o fragmento desta forma:

ft.add(R.id.fragment_container, fragmentIn, FRAGMENT_TAG);

Onde ft é minha transação de fragmento e FRAGMENT_TAGé a tag. Então eu uso este código para obter o fragmento:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);

Isso só funcionará se você der a mesma tag a todos os fragmentos, o que não é uma boa ideia se você quiser encontrar um fragmento específico posteriormente.
Shirane85

4

Peguei a resposta @roghayeh hosseini (correta) e fiz em Kotlin para quem está aqui em 2017 :)

fun getTopFragment(): Fragment? {
    supportFragmentManager.run {
        return when (backStackEntryCount) {
            0 -> null
            else -> findFragmentByTag(getBackStackEntryAt(backStackEntryCount - 1).name)
        }
    }
}

* Deve ser chamado de dentro de uma Activity.

Aproveitar :)


3

você pode usar getBackStackEntryAt () . Para saber quantas entradas a atividade contém na pilha de retorno, você pode usar getBackStackEntryCount ()

int lastFragmentCount = getBackStackEntryCount() - 1;

11
Mas como faço para colocar o último fragmento no backstack ?? O popBackStackEntryAt () retorna apenas uma instância BackStackEntry, NÃO um fragmento
Leem.fin

sim, você está certo, mas cada BackStackEntry contém e id que você pode recuperar com getId (). você pode usar este Id para recuperar o fragmento
Blackbelt

1
Para obter o último fragmento: getBackStackEntryCount () - 1
An-droid de

3
Esta resposta está errada, estou vendo que as entradas da pilha de retorno têm um id de 0, portanto, não é possível recuperar o fragmento por id.
Justin

1
Isso é antigo, mas para qualquer um que vagueia por aqui: back stack não contém fragmentos, mas transações de fragmentos, é por isso que essa resposta está errada
maciekjanusz

3

Acrescentarei algo à resposta de Deepak Goel, já que muitas pessoas, inclusive eu, estavam obtendo um valor nulo usando seu método. Aparentemente, para fazer a tag funcionar quando você adiciona um fragmento à pilha de apoio, você deve fazer assim:

getSupportFragmentManager.beginTransaction().replace(R.id.container_id,FragmentName,TAG_NAME).addToBackStack(TAG_NAME).commit();

Você precisa adicionar a mesma tag duas vezes.

Eu teria comentado, mas não tenho 50 reputação.


você tem 50 agora :)
davidbilla

2

Manter sua própria pilha de volta: myBackStack. Ao adicionar Addum fragmento ao FragmentManager, adicione- o também myBackStack. No onBackStackChanged()pop, a partir de myBackStackquando seu comprimento é maior que getBackStackEntryCount.


2

Parece que algo mudou para melhor, porque o código abaixo funciona perfeitamente para mim, mas não o encontrei nas respostas já fornecidas.

Kotlin:

supportFragmentManager.fragments[supportFragmentManager.fragments.size - 1]

Java:

getSupportFragmentManager().getFragments()
.get(getSupportFragmentManager().getFragments().size() - 1)

2

Se você usar addToBackStack(), você pode usar o seguinte código.

List<Fragment> fragments = fragmentManager.getFragments(); activeFragment = fragments.get(fragments.size() - 1);


1

Na verdade, não há fragmento mais recente adicionado à pilha porque você pode adicionar vários ou fragmentos à pilha em uma única transação ou simplesmente remover fragmentos sem adicionar um novo.

Se você realmente deseja ter uma pilha de fragmentos e ser capaz de acessar um fragmento por seu índice na pilha, é melhor ter uma camada de abstração sobre o FragmentManagere seu backstack. Veja como você pode fazer isso:

public class FragmentStackManager {
  private final FragmentManager fragmentManager;
  private final int containerId;

  private final List<Fragment> fragments = new ArrayList<>();

  public FragmentStackManager(final FragmentManager fragmentManager,
      final int containerId) {
    this.fragmentManager = fragmentManager;
    this.containerId = containerId;
  }

  public Parcelable saveState() {
    final Bundle state = new Bundle(fragments.size());
    for (int i = 0, count = fragments.size(); i < count; ++i) {
      fragmentManager.putFragment(state, Integer.toString(i), fragments.get(i));
    }
    return state;
  }

  public void restoreState(final Parcelable state) {
    if (state instanceof Bundle) {
      final Bundle bundle = (Bundle) state;
      int index = 0;
      while (true) {
        final Fragment fragment =
            fragmentManager.getFragment(bundle, Integer.toString(index));
        if (fragment == null) {
          break;
        }

        fragments.add(fragment);
        index += 1;
      }
    }
  }

  public void replace(final Fragment fragment) {
    fragmentManager.popBackStackImmediate(
        null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    fragmentManager.beginTransaction()
        .replace(containerId, fragment)
        .addToBackStack(null)
        .commit();
    fragmentManager.executePendingTransactions();

    fragments.clear();
    fragments.add(fragment);
  }

  public void push(final Fragment fragment) {
    fragmentManager
        .beginTransaction()
        .replace(containerId, fragment)
        .addToBackStack(null)
        .commit();
    fragmentManager.executePendingTransactions();

    fragments.add(fragment);
  }

  public boolean pop() {
    if (isEmpty()) {
      return false;
    }

    fragmentManager.popBackStackImmediate();

    fragments.remove(fragments.size() - 1);
    return true;
  }

  public boolean isEmpty() {
    return fragments.isEmpty();
  }

  public int size() {
    return fragments.size();
  }

  public Fragment getFragment(final int index) {
    return fragments.get(index);
  }
}

Agora, em vez de adicionar e remover fragmentos chamando FragmentManagerdiretamente, você deve usar push(), replace()e pop()métodos de FragmentStackManager. E você poderá acessar o fragmento superior apenas chamandostack.get(stack.size() - 1) .

Mas se você gosta de hacks, tenho outras maneiras de fazer coisas semelhantes. A única coisa que devo mencionar é que esses hacks funcionarão apenas com fragmentos de suporte.

O primeiro hack é apenas adicionar todos os fragmentos ativos ao gerenciador de fragmentos. Se você apenas substituir os fragmentos um por um e retirá-los da pilha, este método retornará o fragmento superior:

public class BackStackHelper {
  public static List<Fragment> getTopFragments(
      final FragmentManager fragmentManager) {
    final List<Fragment> fragments = fragmentManager.getFragments();
    final List<Fragment> topFragments = new ArrayList<>();

    for (final Fragment fragment : fragments) {
      if (fragment != null && fragment.isResumed()) {
        topFragments.add(fragment);
      }
    }

    return topFragments;
  }
}

A segunda abordagem é o evento mais hacky e permite que você obtenha todos os fragmentos adicionados na última transação para a qual addToBackStackfoi chamado:

package android.support.v4.app;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BackStackHelper {
  public static List<Fragment> getTopFragments(
      final FragmentManager fragmentManager) {
    if (fragmentManager.getBackStackEntryCount() == 0) {
      return Collections.emptyList();
    }

    final List<Fragment> fragments = new ArrayList<>();

    final int count = fragmentManager.getBackStackEntryCount();
    final BackStackRecord record =
        (BackStackRecord) fragmentManager.getBackStackEntryAt(count - 1);
    BackStackRecord.Op op = record.mHead;
    while (op != null) {
      switch (op.cmd) {
        case BackStackRecord.OP_ADD:
        case BackStackRecord.OP_REPLACE:
        case BackStackRecord.OP_SHOW:
        case BackStackRecord.OP_ATTACH:
          fragments.add(op.fragment);
      }
      op = op.next;
    }

    return fragments;
  }
}

Observe que, neste caso, você deve colocar esta classe no android.support.v4.apppacote.


0

Ou você pode apenas adicionar uma tag ao adicionar fragmentos correspondentes ao seu conteúdo e usar o campo String estático simples (você também pode salvá-lo no pacote de instância de atividade no método onSaveInstanceState (Bundle)) para manter a última tag de fragmento adicionada e obter este fragmento porTag () a qualquer hora que você precisar ...


0

A resposta mais alta (Deepak Goel) não funcionou bem para mim. De alguma forma, a tag não foi adicionada corretamente.

Acabei apenas enviando o ID do fragmento através do fluxo (usando intents) e recuperando-o diretamente do gerenciador de fragmentos.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.