Android Fragment在屏幕旋转时被创建两次

6

尽管Activity只将Fragment添加一次,但我的Fragment被创建了两次。当我旋转屏幕时会发生这种情况。此外,每次调用Fragment的onCreateView时,它都会丢失所有变量状态。

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) { // Checking for recreation
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new AppPanelFragment())
                    .commit();
        }
    }

}

在活动中的onCreate()方法检查是否有null savedInstanceState,仅当为null时才添加Fragment,因此我看不出为什么Fragment应该被创建两次。在该if条件中设置断点告诉我它只被调用一次,因此活动不应多次添加Fragment。然而,每次方向改变时,Fragment的onCreateView()方法仍将被调用。

        public class AppPanelFragment extends Fragment {

            private TextView appNameText;

            @Override
            public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                     Bundle savedInstanceState) {
// This method called several times
                View rootView = inflater.inflate(R.layout.fragment_app_panel, container, false);

// 2nd call on this method, appNameText is null, why?
appNameText = (TextView) rootView.findViewById(R.id.app_name);
appNameText.text = "My new App";

    return view;

    }

我通过使用setRetainInstance(true)来使变量状态得以保持,但这是真正的解决方案吗?我期望在屏幕方向改变时,不会创建新的碎片。

4个回答

5

在安卓系统中,当手机的方向改变时,活动会被销毁并重新创建。现在,我相信为了解决你的问题,你可以使用片段管理器来检查片段是否已经存在于后退栈中,如果不存在,则创建它。

public void onCreated(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
            mFragmentManager = getSupportFragmentManager();
            AppPanelFragment fragment  = (AppPanelFragment)mFragmentManager.findFragmentById(R.id.fagment_id);
            if(fragment == null) {
                //do your fragment creation
            } 
        }
    }

顺便说一句,我没有试过这个方法,但是只要在findFragmentById方法中提供正确的片段id,它应该可以正常工作。


4

Fragment 生命周期与 Activity 非常相似。默认情况下,它们会像 Activity 一样在配置更改期间被重新创建。这是预期的行为。即使使用了 setRetainInstance(true)(如果包含 UI,则建议谨慎使用),您的 View 将被销毁并重新创建,但在这种情况下,仅销毁 ViewFragment 实例不会被销毁。


谢谢您的回复。也许我的实际问题是我没有正确地对片段进行建模。我有一些数据在这个片段中,我希望在方向改变时保留它们。听起来我做不到这一点。那么我应该将数据与片段类分离吗?这会以哪种方式发生 - 我确实有PresentationModel,可以将模型附加到其中。 - monokh
1
如果您的数据可以放入一个包中,请使用 onSaveInstanceState() 将数据放入该包中,并在 onCreate() 中将其取回,如果 savedInstanceState 包不为空。 - Kevin Coppock
1
有道理,是的,数据可以很容易地保存在bundle上。那么是否就不需要使用setRetainInstance了呢? - monokh

1
我知道回答有点晚了,但是使用The Code Pimp的答案,你可以做以下事情:
如果片段存在于后退栈中,我们将其弹出并删除,然后再添加回去(如果不先删除就添加回去,会抛出异常,说它已经存在)。
fragment变量是一个类成员变量。
这个方法将在Activity的onCreate方法中被调用:
    if (savedInstanceState == null) {
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        if (fragmentManager.findFragmentById(getFragmentActivityLayoutContainerId()) == null) {
            fragment = getNewFragmentInstance();
        } else {
            fragment = fragmentManager.findFragmentById(getFragmentActivityLayoutContainerId());
            fragmentTransaction.remove(fragment);
            fragmentManager.popBackStack();
            fragmentTransaction.commit();
            fragmentTransaction = fragmentManager.beginTransaction();
        }
        fragmentTransaction.add(getFragmentActivityLayoutContainerId(), fragment);
        fragmentTransaction.commit();
    }

下一个代码将在片段本身中调用。
这是一个小例子,您可以在片段中实现此代码以了解其工作原理。 dummyTV 是片段中心的简单文本视图,根据方向接收文本(为此我们需要一个计数器)。
private TextView dummyTV;
private static int counter = 0;

@Override
protected int getFragmentLayoutId() {
    return R.layout.fragment_alerts_view;
}

@Override
protected void saveReferences(View view) {
    dummyTV = (TextView) view.findViewById(R.id.fragment_alerts_view_dummy_tv);
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {

    if (savedInstanceState != null) {
        dummyTV.setText(savedInstanceState.getString("dummy_string"));
    } else {
        dummyTV.setText("flip me!");
    }

    dummyTV.append(" | " + String.valueOf(counter));
}

@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString("dummy_string", counter++ % 2 == 0 ? "landscape" : "portrait");
}

0
如上所述,在方向更改时,活动将被销毁并重新创建。此外,系统会重新创建片段(任何)。
为确保您的应用程序恢复到先前的状态,在销毁活动之前调用onSaveInstanceState()。
因此,您可以在活动的onSaveInstanceState()方法中存储一些信息,然后在方向更改时将应用程序恢复到相同的状态。
注意:您无需在方向更改时创建片段,因为片段会被重新创建。
示例来自http://www.mynewsfeed.x10.mx/articles/index.php?id=15
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if ( savedInstanceState == null ){
          //Initialize fragments
          Fragment example_fragment = new ExampleFragment();
          FragmentManager manager = getFragmentManager();
          FragmentTransaction transaction = manager.beginTransaction();
          transaction.add(R.id.container, example_fragment, "Example");
    } else{
       //control comes to this block only on orientation change.

       int postion = savedInstanceState.getInt("position"); //You can retrieve any piece of data you've saved through onSaveInstanceState()

      //finding fragments on orientation change 
      Fragment example_fragment = manager.findFragmentByTag("Example");

     //update the fragment so that the application retains its state
     example_fragment.setPosition(position); //This method is just an example

    }
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("position", position); //add any information you'd like to save and restore on orientation change.
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接