gpt4 book ai didi

android - 嵌套保留的 FragmentTabHost 不会将选项卡附加到新 Activity

转载 作者:行者123 更新时间:2023-11-30 03:03:22 25 4
gpt4 key购买 nike

我一直在使用 Android v4 支持库中的 FragmentTabHost 一段时间,遇到了一个我无法解决的严重问题。我的目标要求如下。

1) Fragment 将包括也从 Fragments 构建的选项卡,其中还包括嵌套的 Fragments,具有如下层次结构:

  • android.support.v4.app.Fragment (HostFragment)
    • android.support.v4.app.FragmentTabHost (TabHost)
      • android.support.v4.app.Fragment (Tab1Fragment)
        • android.support.v4.app.Fragment (Tab1Fragment1)
        • android.support.v4.app.Fragment (Tab1Fragment2)
        • android.support.v4.app.Fragment (Tab1Fragment3)
      • android.support.v4.app.Fragment (Tab2Fragment)
        • android.support.v4.app.Fragment (Tab2Fragment1)
        • android.support.v4.app.Fragment (Tab2Fragment2)

HostFragment + TabHost 设置是根据文档中的说明完成的 here .

2) 在屏幕旋转时保留 HostFragment 的状态,因为重新创建此设置是一项耗费资源的操作,除了布局顺序之外,此屏幕上没有任何变化,因此不需要额外的工作来显示屏幕.只需在 Tab1Tab2onCreateView 回调中返回不同的 layout 并将现有 fragment 重新附加到相同的 id。

要实现这一点,人们会认为只需设置 HostFragment.setRetainInstance(true) 即可完成工作,并且部分地确实如此。没有重新创建任何内容,选项卡按应有的方式保留,一切正常。现在让我们进入正题。

问题

大问题稍后出现。 Tab1Fragment1(以及所有其他 TabXFragmentY)似乎没有附加到旋转时创建的新 Activity。它们仅在第一次运行 Activity 时附加,然后当用户旋转屏幕时什么都不做。

后果

这给我带来了两个大问题(可能还有更多):

  1. 初始 Activity 被泄露,因为 TabHost 中的所有 Fragments 都无缘无故地附加到它。
  2. 当您在任何 TabXFragmentY 上调用 getActivity() 时,您将获得 Activity 和可怕的“在 onSavedInstance 之后无法执行操作”异常。

当您想要从父 Activity 中获取某些内容时,这会导致严重的问题。此外,在设置 HostFragment.setRetainInstance(false) 时不会发生这种情况,所有内容都会重新创建并且工作正常。

问题

这是我只看到的问题吗?我找不到关于该主题的任何内容,而且我已经检查了我的代码一百次。

我还尝试从 v4 示例中更改 com.example.android.supportv4.app.FragmentTabsFragmentSupport,我确实将其设置为保留它的实例并在其嵌套 fragment 之一中添加了日志信息在每个 onAttach 调用上进行验证,我可以看到仍然存在相同的问题。我很困惑。帮助。

最佳答案

最近我一直在使用 Fragment 开发一个 TabHost 并发现了同样的问题。基本上,您需要控制附加/分离哪些 Fragment,在我的例子中,我在 onTabChanged() 事件中执行此操作。

我有一个 TabInfo 类,我在其中存储每个 Tab 的以下信息

  • Fragment 的名称(用于识别目的)。
  • 要附加的 fragment
  • TabHost.TabSpec 规范,就像您要删除某些选项卡一样,这里有重新创建其余选项卡的信息,因为 TabHost 对于删除选项卡有点棘手.
  • 关联的Bundle(基本上是配置更改之前保存的实例)。

此外,我需要跟踪 lastTabnewTab 打开,因为 TabHost 没有本地方式知道哪个TabHost 刚刚关闭,所以我在类范围内声明一个变量。这就是我现在的处理方式,我会尝试添加尽可能多的评论:

@Override
public void onTabChanged(final String tag) {
// I get the info for the Tab just triggered using its tag
final TabInfo newTab = mTabInfo.get(tag);

// If there's actually been a tab change...
if (lastTab != newTab) {
// You'll have to make a transaction for replacing the Fragment
final FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();

// If the last tab actually has a Fragment associated to it
if ((lastTab != null) && (lastTab.getFragment() != null)) {
// In my case I've an additional level of complexion, as I have a nested Fragment
// inside my content Fragment. So I have to remove it first prior to detaching
// the parent Fragment. This is not needed if you have just one Fragment as content.
final Fragment loginFrag = (Fragment) lastTab.getFragment().getActivity().getSupportFragmentManager().findFragmentById(lastTab.getLoginFragId());
ft.remove(loginFrag);

// And this is what does the trick: I initially was calling detach() instead of remove()
// but seems that with some versions there's a problem that makes not apply it,
// calling remove will actually remove this Fragment
ft.remove(lastTab.getFragment());
}

// You've detached the old Fragment, you have now to attach the new one
if (newTab != null) {
if (newTab.getFragment() == null) {
// Inflate the new content if it's the first time the tab has been fired
final TabFragmentInflater tabInf = new TabFragmentInflater();
newTab.setFragment(Fragment.instantiate(this, tabInf.getClass().getName(), newTab.getArgs()));
ft.add(R.id.realtabcontent, newTab.getFragment(), newTab.getTag());
}
else {
// if not, just attach its fragment
ft.attach(newTab.getFragment());
}
}

ft.commit();
this.getSupportFragmentManager().executePendingTransactions();

lastTab = newTab;
}
}

----编辑----

回答您的问题:

  • 我确实在 TabHost 上调用了 newTabSpec(),它在一个单独的方法中,我没有包含因为我只是包含了 onTabChanged( ) 回调。您必须正常创建选项卡,此onTabChanged() 方法只会在您单击选项卡时触发。对于创作,我做了这样的事情:

    private void initTabHost(final Bundle args) {
    final TabHost th = (TabHost) findViewById(android.R.id.tabhost);
    th.setup();

    // I have a HashMap called fragMap where as the key I define the tab's name
    // And as the value, I have an Integer which is a unique identifier
    // to know what to inflate when I call the TabFragmentInflater (I will
    // add the code below). You can perfectly add it as an id or a tag also.
    for (final String tablabel : fragMap.keySet()) {
    final TabHost.TabSpec tabSpec = th.get().newTabSpec(tabname).setIndicator(tabname);
    // Here I initialize a TabInfo object for this tab, which will include additional
    // Handling info: Name of tab, Tab Spec, The unique ID I explained above,
    // the forth argument is irrelevant in your example, and args (saved instance)
    final TabInfo tabInfo = new TabInfo(tabname, tabSpec, fragMap.get(tablabel), R.id.someLayout, args);

    // This is not actually the TabHost's `addTab()` method, I'll call it inside
    // this method (see below)
    MyClass.addTab(this, th, tabSpec, tabInfo);

    // I have to be able to keep tracking of that info
    mTabInfo.put(tabInfo.getTag(), tabInfo);
    }

    // For the tab creation, I force it to start on the first tab
    this.onTabChanged(firstTabNameTag);
    th.setOnTabChangedListener(this);
    }

addTab() 方法非常简单,它会膨胀对象并在需要时将其分离。

private static void addTab(final MyClass activity, final TabHost tabHost, final TabHost.TabSpec tabSpec, final TabInfo tabInfo) {
tabSpec.setContent(new TabFactory(activity)); // This is just an empty View
final String tag = tabSpec.getTag();

// Here I check if there's already a Fragment for that tab, probably in a
// previously saved state. If this happens, we deactivate it, because our
// former state for that tab is "not shown".
tabInfo.setFragment(activity.getSupportFragmentManager().findFragmentByTag(tag));
if ((tabInfo.getFragment() != null) && (!tabInfo.getFragment().isDetached())) {
final FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
ft.detach(tabInfo.getFragment());
ft.commit();

activity.getSupportFragmentManager().executePendingTransactions();
}

// Actually, there's where I call the "official" `addTab()` from `TabHost`
tabHost.addTab(tabSpec);
}
  • 所以只剩下 TabFragmentInflater。它只是一个 Fragment,它根据我上面提到的唯一 ID 来膨胀相应的布局,所以它是这样的:

    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
    fragId = getArguments().getInt("fragid");

    if (view != null) {
    ViewGroup parent = (ViewGroup) view.getParent();
    if (parent != null)
    parent.removeView(view);
    }

    try {
    switch (fragId) {
    case 1: // My first tab...
    final LinearLayout fragLayout = (LinearLayout) inflater.inflate(R.layout.myfirsttab_fragment_layout, container, false);

    ...
    return fragLayout;

    case 2: // My second tab
    fragLayout = (LinearLayout) inflater.inflate(R.layout.mysecondtab_fragment_layout, container, false);

    ...
    return fragLayout;

    ...
    }
    }
    catch (final InflateException e) { return view; }

    return null;
    }

关于android - 嵌套保留的 FragmentTabHost 不会将选项卡附加到新 Activity,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22219414/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com