获取 backstack 中的最新片段

Posted

技术标签:

【中文标题】获取 backstack 中的最新片段【英文标题】:get the latest fragment in backstack 【发布时间】:2012-03-30 22:15:09 【问题描述】:

如何获取最新添加到 backstack 中的片段实例(如果我不知道片段标签和 id)?

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 ??

【问题讨论】:

【参考方案1】:

您可以使用 API 级别 14 中引入的 FragmentManager.BackStackEntrygetName() 方法。该方法将返回一个标签,该标签是您使用 addTobackStack(tag) 将 Fragment 添加到后台堆栈时使用的标签。

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

您需要确保将片段添加到后台堆栈,如下所示:

fragmentTransaction.addToBackStack(tag);

【讨论】:

为了按标签查找片段,必须添加/替换为相同的标签。 FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc 这样的问题是,如果你在后台堆栈中有一个片段两次,当你只有索引或 backstackentry 实体时,你无法检索特定片段。 如果我无法通过 pop 方法访问片段,那么将其称为 stack 有什么意义? RTFM:developer.android.com/reference/android/app/… 我不知道为什么,但是 findFragmentByTag 返回 null,即使调试器清楚地显示标签是好的。【参考方案2】:
FragmentManager.findFragmentById(fragmentsContainerId) 

function 将链接返回到后台堆栈中的顶部 Fragment。使用示例:

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

【讨论】:

这个答案在我的项目中效果很好,因为我将除根片段之外的每个片段都添加到了后堆栈。但我想如果最新添加的片段没有添加到后台堆栈,这个答案将不起作用。【参考方案3】:

我亲自尝试了其中的许多解决方案,最终得到了这个可行的解决方案:

添加这个将在下面多次使用的实用方法来获取你的 backstack 中的片段数:

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

然后,当您使用 FragmentTransaction 方法添加/替换您的片段时,为您的片段生成一个唯一标签(例如:通过使用堆栈中的片段数):

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

最后,您可以使用此方法在 backstack 中找到您的任何片段:

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

因此,可以很容易地通过调用来获取 backstack 中的顶部片段:

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

希望这会有所帮助!

【讨论】:

【参考方案4】:

科特林

// In activities
activity.supportFragmentManager.fragments.lastOrNull()

// In fragments
fragment.childFragmentManager.fragments.lastOrNull()

【讨论】:

如果fragments 为空,您可能需要使用.lastOrNull()【参考方案5】:

这个帮助方法从栈顶获取片段:

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

【讨论】:

谢谢。最优雅的答案。在此处为 Kotlin 爱好者添加它***.com/a/47336504/1761406【参考方案6】:

fragmentMananger 中有一个片段列表。请注意,删除片段不会使列表大小减小(片段条目只是变为空)。因此,一个有效的解决方案是:

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;

【讨论】:

不可靠。三个原因: 1) 这是片段管理器知道的所有片段的列表,而不仅仅是堆栈上的片段。 2) 不能保证片段管理器会在列表末尾继续添加新片段。当然,它会在简单的测试中这样做,但是如果一个片段被删除,留下一个空值,然后在某些情况下片段管理器决定重用那个空槽怎么办?不是说会,但不能保证在任何情况下都不会。 3) 如果你或一些未来的程序员开始使用附加/分离来管理片段,这将与堆栈不匹配。 您好,感谢您的解决方案,但它既不美观也不简单 我怀疑这是一个有效的解决方案。 getFragments() 返回一个缩短的片段集合。也许最后一个是可见的,但其他的不多。【参考方案7】:

deepak goel 给出的答案对我不起作用,因为我总是从entry.getName() 得到 null;

我所做的是以这种方式为片段设置标签:

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

其中 ft 是我的片段交易,FRAGMENT_TAG 是标签。然后我使用这段代码来获取片段:

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);

【讨论】:

这只有在你给所有片段都赋予相同标签时才有效,如果你想稍后找到特定片段,这不是一个好主意。【参考方案8】:

刚刚接受了@roghayeh hosseini(正确)的答案,并在 Kotlin 中为 2017 年的人提供了答案:)

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

*这应该从 Activity 内部调用。

享受:)

【讨论】:

【参考方案9】:

看起来有些事情已经变得更好了,因为下面的代码对我来说非常适合,但我没有在已经提供的答案中找到它。

科特林:

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

Java:

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

【讨论】:

【参考方案10】:

您可以使用getBackStackEntryAt()。为了知道活动在后台堆栈中有多少条目,您可以使用getBackStackEntryCount()

int lastFragmentCount = getBackStackEntryCount() - 1;

【讨论】:

但是我怎样才能得到 backstack 中的最后一个片段呢? popBackStackEntryAt() 只返回一个 BackStackEntry 实例,而不是片段 是的,你是对的,但是每个 BackStackEntry 都持有和你可以使用 getId() 检索的 id。您可以使用此 ID 来检索片段 获取最后一个片段:getBackStackEntryCount() - 1 这个答案是错误的,我看到 backstack 条目的 id 为 0,所以无法通过 id 检索片段。 这是旧的,但对于任何在这里徘徊的人:回栈不保存碎片,而是碎片交易,这就是为什么这个答案是错误的【参考方案11】:

我将在Deepak Goel's answer 中添加一些内容,因为包括我在内的很多人都使用他的方法得到了一个空值。显然,当您将片段添加到后台堆栈时,要使标签工作,您应该这样做:

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

您需要添加两次相同的标签。

我会发表评论,但我没有 50 声望。

【讨论】:

你现在有 50 个 :)【参考方案12】:

保留您自己的后台堆栈:myBackStack。当您将Add 片段添加到FragmentManager 时,也将其添加到myBackStack。在onBackStackChanged() 中,当其长度大于getBackStackEntryCount 时从myBackStack 弹出。

【讨论】:

【参考方案13】:

如果你使用addToBackStack(),你可以使用以下代码。

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

【讨论】:

【参考方案14】:

实际上,堆栈中没有添加最新的片段,因为您可以在单个事务中向堆栈添加多个或片段,或者只是删除片段而不添加新片段。

如果你真的想拥有一堆片段并且能够通过它在堆栈中的索引来访问一个片段,你最好在FragmentManager 和它的backstack 之上有一个抽象层。你可以这样做:

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

现在,您应该使用FragmentStackManagerpush()replace()pop() 方法,而不是直接调用FragmentManager 来添加和删除片段。您只需调用stack.get(stack.size() - 1) 即可访问最顶层的片段。

但如果你喜欢 hacks,我必须以其他方式来做类似的事情。我唯一要提的是,这些 hack 仅适用于支持片段。

第一个技巧只是将所有活动片段添加到片段管理器中。如果您只是一个接一个地替换片段并从堆栈中弹出,此方法将返回最顶层的片段:

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;
  

第二种方法是事件更 hacky,允许您获取在最后一个事务中添加的所有片段,其中 addToBackStack 已被调用:

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;
  

请注意,在这种情况下,您必须将此类放入 android.support.v4.app 包中。

【讨论】:

【参考方案15】:

或者您可以在添加与其内容对应的片段时添加一个标签,并使用简单的静态字符串字段(也可以将其保存在 onSaveInstanceState(Bundle) 方法中的活动实例包中)来保存最后添加的片段标签并获取此片段byTag() 在您需要的任何时候...

【讨论】:

【参考方案16】:

最高(Deepak Goel)答案对我来说效果不佳。不知何故,标签没有正确添加。

我最终只是通过流(使用意图)发送片段的 ID 并直接从片段管理器中检索它。

【讨论】:

【参考方案17】:

Kotlin 开发者可以使用它来获取当前片段:

        supportFragmentManager.addOnBackStackChangedListener 
        val myFragment = supportFragmentManager.fragments.last()

        if (null != myFragment && myFragment is HomeFragment) 
            //HomeFragment is visible or currently loaded
         else 
            //your code
        
    

【讨论】:

以上是关于获取 backstack 中的最新片段的主要内容,如果未能解决你的问题,请参考以下文章

在 Android Navigation 组件中使用 backstack 打开不同层次结构中的片段

Android:片段 backStack

具有多个 backstack 的片段

当我们在android中使用backstack返回上一个片段时,上一个片段正在重新启动

如何在 BackStack 上反转片段动画?

片段行为:FragmentTransaction::replace() 和反向 backStack 操作