获取Backstack中的最新片段


104

如何获得最新的片段实例添加到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 ??

Answers:


156

您可以使用API级别14中引入的getName()方法FragmentManager.BackStackEntry。该方法将返回一个标记,该标记是您使用将Fragment添加到Backstack时使用的标记addTobackStack(tag)

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

您需要确保像这样将片段添加到后台:

fragmentTransaction.addToBackStack(tag);

24
为了按标签查找片段,必须将其添加/替换为相同的标签。FragmentTransaction.add(int containerViewId, Fragment fragment, String tag)or FragmentTransaction.replace(int containerViewId, Fragment fragment, String tag) doc
Saqib

3
问题是如果您在后堆栈中有两次片段,那么当您拥有的只是索引或backstackentry实体时,您将无法检索特定片段。
贾斯汀

14
什么是称这是一个点stack,如果我无法通过pop方法访问片段?
肯尼·沃登


1
我不知道为什么,但是即使调试器清楚地表明该标记是可以的,但findFragmentByTag仍返回null。
htafoya

49
FragmentManager.findFragmentById(fragmentsContainerId) 

函数将链接返回到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());
            }
        }
    });

2
这个答案在我的项目中效果很好,因为我将除根片段以外的所有片段都添加到了后堆栈中。但是我想如果没有将最新添加的片段未添加到后台,则此答案将无效。
Arne Evertsson 2014年

40

我个人尝试了许多解决方案,最后得到了这个可行的解决方案:

添加此实用程序方法,该方法将在下面多次使用,以获取后堆栈中的碎片数:

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

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

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

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

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

因此,可以通过调用以下命令轻松实现在堆栈中获取顶部片段:

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

希望这可以帮助!


10

此帮助程序方法从堆栈顶部获取片段:

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

1
谢谢。最优雅的答案。在此处为Kotlin爱好者添加了stackoverflow.com/a/47336504/1761406
Shirane85 '17


6

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

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
不可靠的。三个原因:1)这是片段管理器知道的所有片段的列表,而不仅仅是堆栈中的那些片段。2)无法保证片段管理器将继续在列表末尾添加新的片段。当然,它会在简单的测试中执行此操作,但是如果删除了一个片段,留下一个空值,然后在某些情况下,片段管理器决定重用该空插槽怎么办?并不是说这样做,但是在任何情况下都不能保证它永远不会。3)如果您或将来的程序员开始使用附加/分离来管理片段,则这将与堆栈不匹配。
ToolmakerSteve

嗨,谢谢您的解决方案,但它并不美观且简单
穆罕默德·阿里

我怀疑这是一个有效的解决方案。getFragments()返回缩短的片段集合。也许最后一个是可见的,但其他的并不多。
CoolMind

5

Deepak Goel给出的答案对我不起作用,因为我总是从中得到空值entry.getName()

我要做的是通过以下方式将Tag设置为片段:

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

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

Fragment prev_fragment = fragmentManager.findFragmentByTag(FRAGMENT_TAG);

仅当您为所有片段赋予相同的标签时,此方法才有效,如果您以后要查找特定片段,则不是一个好主意。
Shirane85

4

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

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

*应从活动内部调用。

请享用 :)


3

您可以使用getBackStackEntryAt()。为了知道活动在后退堆栈中保存的条目数量,可以使用getBackStackEntryCount()

int lastFragmentCount = getBackStackEntryCount() - 1;

11
但是,如何获得后堆栈中的最后一个片段呢?popBackStackEntryAt()仅返回BackStackEntry实例,而不返回片段
Leem.fin 2012年

是的,您是对的,但是每个BackStackEntry都有一个ID和您可以使用getId()检索的ID。您可以使用此ID来检索片段
Blackbelt

1
要获取最后一个片段:getBackStackEntryCount()-1
An-droid

3
这个答案是错误的,我看到Backstack条目的ID为0,因此无法按ID检索片段。
贾斯汀

1
这很老了,但是对于任何在这里徘徊的人来说:后堆栈不保存碎片,而是碎片交易,这就是为什么这个答案是错误的
maciekjanusz

3

我将在Deepak Goel的答案中添加一些内容,因为包括我在内的许多人都使用他的方法获得了空值。显然,要在将片段添加到Backstack时使标记起作用,您应该这样做:

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

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

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


您现在有50 :)
大卫

2

保持自己的后排堆栈:myBackStack。当您Add为片段时FragmentManager,也将其添加到中myBackStack。当onBackStackChanged()流行音乐myBackStack的长度大于时getBackStackEntryCount


2

看起来情况已经有所好转,因为下面的代码对我来说很完美,但是我没有在已经提供的答案中找到它。

科特林:

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

Java:

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


1

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

如果您确实想拥有一堆片段,并希望能够通过其在堆栈中的索引访问片段,则最好在片段及其后堆栈上都具有一个抽象层FragmentManager。方法如下:

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

现在,而不是增加,并通过调用除去碎片FragmentManager直接,你应该使用push()replace()pop()方法FragmentStackManager。通过调用,您将可以访问最上面的片段stack.get(stack.size() - 1)

但是,如果您喜欢黑客,我必须采用其他方式来做类似的事情。我唯一要提及的是,这些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;
  }
}

第二种方法是使事件更加棘手,并允许您获取上一次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包中。


0

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


0

最高(Deepak Goel)答案对我而言效果不佳。标签以某种方式未正确添加。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.