当方向更改时,使用选项卡重新加载活动时,片段会初始化两次

Ger*_*och 18 tabs android device-orientation android-fragments

当我更改设备的方向时,我在使用制表符和片段重新加载活动时遇到问题.

情况如下:

我有一个活动,在操作栏中有3个标签.每个选项卡FrameLayout在主视图中加载不同的片段.如果我不改变设备的方向,一切正常.但是,当我这样做时,Android会尝试将当前选定的片段初始化两次,从而产生以下错误:

E/AndroidRuntime(2022): Caused by: android.view.InflateException: Binary XML file line #39: Error inflating class fragment
Run Code Online (Sandbox Code Playgroud)

以下是产生错误的步骤序列:

  1. 我加载活动,选择选项卡nr 2.并更改设备的方向.
  2. Android会破坏tab nr 2加载的片段的活动和实例(从现在开始,'片段2').然后继续创建活动和片段的新实例.
  3. 在里面Activity.onCreate()我将第一个标签添加到操作栏.当我这样做时,会自动选择此选项卡.它可能代表未来的问题,但我现在不介意.onTabSelected调用并创建并加载第一个片段的新实例(参见下面的代码).
  4. 我添加所有其他选项卡而没有触发任何事件,这很好.
  5. 我打电话ActionBar.selectTab(myTab)选择Tab nr 2.
  6. onTabUnselected()调用第一个选项卡,然后调用onTabSelected()第二个选项卡.此序列替换Fragment 2实例的当前片段(请参阅下面的代码).
  7. 接下来,Fragment.onCreateView()在Fragment 2实例上调用,片段布局变得膨胀.
  8. 这是问题所在.Android调用onCreate()然后调用onCreateView()片段实例ONCE AGAIN,当我尝试(第二次)对布局进行充气时会产生异常.

显然问题是Android正在初始化片段两次,但我不知道为什么.

当我自动加载活动时,我尝试不选择第二个标签,但第二个片段无论如何都被初始化并且没有显示(因为我没有选择它的标签).

我发现了这个问题:Android片段在方向更改时重新创建

用户问我的基本相同,但我不喜欢所选择的答案(它只是一个workaroud).必须有一些方法让这个工作没有android:configChanges诀窍.

如果不清楚,我想知道如何阻止片段的重新创建或避免它的双重初始化.很高兴知道为什么会发生这种情况.:P

这是相关代码:

public class MyActivity extends Activity  implements ActionBar.TabListener {

    private static final String TAG_FRAGMENT_1 = "frag1";
    private static final String TAG_FRAGMENT_2 = "frag2";
    private static final String TAG_FRAGMENT_3 = "frag3";

    Fragment frag1;
    Fragment frag2;
    Fragment frag3;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // my_layout contains a FragmentLayout inside
        setContentView(R.layout.my_layout); 

        // Get a reference to the fragments created automatically by Android
        // when reloading the activity
        FragmentManager fm = getFragmentManager();
        this.frag1 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_1);
        this.frag2 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_2);
        this.frag3 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_3)


        ActionBar actionBar = getActionBar();

        // snip...

        // This triggers onTabSelected for the first tab
        actionBar.addTab(actionBar.newTab()
                .setText("Tab1").setTabListener(this)
                .setTag(MyActivity.TAG_FRAGMENT_1));

        actionBar.addTab(actionBar.newTab()
                .setText("Tab2").setTabListener(this)
                .setTag(MyActivity.TAG_FRAGMENT_2));
        actionBar.addTab(actionBar.newTab()
                .setText("Tab3").setTabListener(this)
                .setTag(MyActivity.TAG_FRAGMENT_3));

        Tab t = null;
        // here I get a reference to the tab that must be selected
        // snip...

        // This triggers onTabUnselected/onTabSelected
        ab.selectTab(t);

    }

    @Override
    protected void onDestroy() {
        // Not sure if this is necessary
        this.frag1 = null;
        this.frag2 = null;
        this.frag3 = null;
        super.onDestroy();
    }

    @Override  
    public void onTabSelected(Tab tab, FragmentTransaction ft) {  

        Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString());
        if (curFrag == null) {
            curFrag = createFragmentInstanceForTag(tab.getTag().toString());
            if(curFrag == null) { 
                // snip... 
                return;
            }
        }
        ft.replace(R.id.fragment_container, curFrag, tab.getTag().toString());
    }

    @Override  
    public void onTabUnselected(Tab tab, FragmentTransaction ft) 
    {  
        Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString());
        if (curFrag == null) {
            // snip... 
            return;
        }

        ft.remove(curFrag);
    }

    private Fragment getFragmentInstanceForTag(String tag) 
    {
        // Returns this.frag1, this.frag2 or this.frag3
        // depending on which tag was passed as parameter
    }

    private Fragment createFragmentInstanceForTag(String tag) 
    {
        // Returns a new instance of the fragment requested by tag
        // and assigns it to this.frag1, this.frag2 or this.frag3
    }
}
Run Code Online (Sandbox Code Playgroud)

Fragment的代码是无关紧要的,它只返回onCreateView()方法覆盖的膨胀视图.

Mar*_*son 1

看起来,当屏幕旋转并且应用程序重新启动时,它通过调用 Fragment 类的默认构造函数来重新创建每个 Fragment。