Android示例代码'FragmentTabs'导致设备方向异常

时间:2012-05-25 11:41:02

标签: android android-fragments android-tabhost

我需要在我的应用程序中使用Tabs,因此我的代码基于Android FragmentTabs示例here.。但是我在代码中更改了几行以符合我自己的目的,如下所示。

在给定的android代码中,每次用户更改选项卡时,Fragment的视图外部都会被破坏,并且会创建进入视图的Fragment。出于我的目的,这是不可取的。虽然我的片段可以彼此独立,但它们会对通过网络连接传输的实时数据做出反应并对其进行操作。仅仅因为Fragment不在视觉范围内并不意味着我希望它停止在后台工作(这似乎是Android示例中的思路)。因此,我将代码更改为“隐藏”和“显示”标签,而不是创建和销毁它们。

为实现这一目标,在示例代码中,我在两个位置更改了onTabChanged(),如下所示:

public void onTabChanged(String tabId) {
        TabInfo newTab = mTabs.get(tabId);
        if (mLastTab != newTab) {
            FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
            if (mLastTab != null) {
                if (mLastTab.fragment != null) {
                    ft.hide(mLastTab.fragment);  //***** 1st Alteration
                    //ft.detach(mLastTab.fragment);
                }
            }
            if (newTab != null) {
                if (newTab.fragment == null) {
                    newTab.fragment = Fragment.instantiate(mActivity,
                            newTab.clss.getName(), newTab.args);
                    ft.add(mContainerId, newTab.fragment, newTab.tag);
                } else {
                    ft.show(newTab.fragment);   //************ 2nd Alteration
                    //ft.attach(newTab.fragment);
                }
            }

            mLastTab = newTab;
            ft.commit();
            mActivity.getSupportFragmentManager().executePendingTransactions();
        }
    }

这现在可以按照我的要求运行。当设备重新定向时,问题就出现了。在第一方向改变时,未示出先前选择的标签内容,即片段。这与我“隐藏”和“显示”标签的事实有关。然而更严重的是,在设备的第二次重定向时抛出异常:

  

E / AndroidRuntime(371):引起:java.lang.NullPointerException   E / AndroidRuntime(371):在android.support.v4.app.FragmentManagerImpl.findFragmentByTag(FragmentManager.java:1211)   E / AndroidRuntime(371):$ TabManager.addTab(ClassName.java:121)

addTab方法在这里:

public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args) {
        tabSpec.setContent(new DummyTabFactory(mActivity));
        String tag = tabSpec.getTag();

        TabInfo info = new TabInfo(tag, clss, args);

        // Check to see if we already have a fragment for this tab, probably
        // from a previously saved state.  If so, deactivate it, because our
        // initial state is that a tab isn't shown.
        FragmentManager fm = mActivity.getSupportFragmentManager();
        Log.i("Class Name","addTab() FragmentManager = " + fm.toString());
        Log.i("Class_Name","addTab() tag = " + tag);
        info.fragment = fm.findFragmentByTag(tag); // **** This causes the exception

        /*
         * Commented the following as Fragments are not removed but hidden/shown on tab change.
         */
        if (info.fragment != null && !info.fragment.isDetached()) {
            FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
            ft.detach(info.fragment);
            ft.commit();
        }

        mTabs.put(tag, info);
        mTabHost.addTab(tabSpec);
    }

更具体地说,异常是在info.fragment = fm.findFragmentByTag(tag);抛出的。在代码中可以看出我试图准确地隔离什么对象是null,即它是FragmentManager还是标签但是这些都没有记录为null

我不完全理解Android示例,如果我附加和分离碎片而不是隐藏和显示它们,它可以正常工作。任何人都可以解释问题是什么吗?

1 个答案:

答案 0 :(得分:1)

根据the android docs on runtime changes,当方向发生变化时,您的活动会重新启动。这意味着FragmentManager将其状态保存到一个包中然后重新加载它。

添加了所有已查看的选项卡,即使它们不可见,因此它们会在FragmentManager的添加片段列表中被隐藏起来。 TabManager不存储任何东西,但它只是重新创建,我怀疑新TabManager和恢复的FragmentManager之间的这种不匹配正在向你发送。

我建议您仔细阅读that doco以及有关保存/恢复标签的一些非常谨慎的选择。

剩下的就是纯粹的猜测。看起来Fragment.instantiate和ft.add看起来都没有检查新片段是否与旧片段重复。这可能会导致TabManager保存对一个片段的引用,而FragmentManager包含该片段的两个实例。

从onCreate中当前不可见的选项卡中清除FragmentManager或在创建新实例之前检查片段是否存在就足够了。