Android Fragment创建了两次方向更改

时间:2015-03-21 00:44:08

标签: android android-activity 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 in活动检查null savedInstanceState,并且只有null将添加片段,这样我才能看出为什么片段应该被创建两次?在if条件中设置断点告诉我它只被调用一次,因此活动不应该多次添加片段。但是,每次方向更改时,片段的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 个答案:

答案 0 :(得分:4)

在Android中,当手机的方向改变时,活动将被销毁并重新创建。现在,我相信要修复你的问题,你可以使用片段管理器来检查片段是否已经存在于后台堆栈中,如果它不存在则创建它。

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

P.S。我没有测试过这个,但是一旦你在findFragmentById方法中提供正确的片段id,它就应该可以工作。

答案 1 :(得分:3)

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

答案 2 :(得分: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.
    }

}

答案 3 :(得分:0)

我知道回答有点迟,但使用The Code Pimp回答你可以做下一件事:

如果片段存在于backstack中,我们会弹出并删除它以将其添加回来(如果在不删除它的情况下将其添加回来,则会抛出异常,说它已经存在)。

fragment变量是一个类成员变量。

此方法将在Activity的onCreate方法中调用:

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

下一个代码将在片段本身中调用。

这是您可以在片段中实现的代码的一个小示例,以了解它的工作原理。 dummyTV是片段中心的简单文本视图,它根据方向接收文本(为此我们需要一个计数器)。

<p>compu lack, Your styling is based on URL string</p>