旋转屏幕时保留Fragment对象

48

我开发了一个基于Honeycomb的应用程序,并使用了片段(Fragments)
这是我的应用程序:

  • 我有一个Activity(称为A1),其中包含一个片段(fragment)
  • 最初,该片段保存了一个片段对象,命名为F1
  • 接着,根据用户的操作,它可能会更改为其他对象,如F2、F3等

我的问题是

当用户旋转设备时,活动将被重新创建,这会使F1成为片段对象,即使在旋转之前它不是
有什么方法可以在旋转设备时保留片段对象吗?
我已经尝试使用 setRetainInstance(true); 但它对我没起作用
我在我的onCreate函数中添加了片段的代码,就像这样

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);

   FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();

   Fragment homeFragment = new Home();
   fragmentTransaction.add(R.id.mainFragement, homeFragment);
   fragmentTransaction.commit();
}
6个回答

70
默认情况下,Android会保留片段对象。在您的代码中,您正在在onCreate函数中设置homeFragment。这就是为什么它总是一些homeFragmentfl,无论您在onCreate中设置什么。
因为每当您旋转时,onCreate将执行并将片段对象设置为第一个对象。
因此,您的简单解决方案是检查savedInstanceState bundle是否为空,然后设置片段对象。
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if(null == savedInstanceState) {
        // set you initial fragment object 
    }
 }

1
如果我使用一个处理片段的ViewPager活动,我应该怎么做? - android developer
@androiddeveloper 祈祷。不开玩笑,我做的是:super.onCreate(null); - Warpzit
30
默认情况下,Android 不会保留碎片对象。当屏幕方向改变时,Android 会创建新的对象,除非调用了 setRetainInstance(true)。不要将此与 Android 自动重新附加片段混淆,它们并不是同一件事。请参考S.D. 在这个答案中报告的跟踪信息。 - Rafa Viotti

41

您需要为碎片(Fragment)设置一个唯一的标记(tag),并检查该碎片是否已经添加到您的 Activity 中。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    String tag = "my_fragment";
    FragmentManager fragmentManager = getFragmentManager();
    if(fragmentManager.findFragmentByTag(tag) == null) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        Fragment homeFragment = new Home();
        fragmentTransaction.add(R.id.mainFragement, homeFragment, tag);
        fragmentTransaction.commit();
    }
}

检查savedInstanceState是否为空并不是一种安全的方法来检查您的碎片是否已设置 - 在大多数情况下它可以工作,但在某些情况下(例如设备内存较低时),Android 可能会销毁您的 Activity,这可能会破坏您的应用程序。

要查看此操作,请在设备的开发选项中勾选“不保留活动”(该设置在 Android 4.0+ 中可用,早期版本不确定)。当您打开新活动时,您的第一个活动将被销毁。当您返回它(通过按下返回键)时,它将再次创建,并且 savedInstanceState 不为 null。但是,您的碎片不再位于活动中,因此您必须重新添加它。

编辑 - 使用 SupportFragmentManager 显示原始原则

public class ActivityAwesome extends AppCompatActivity
{
    private final String TAG = getClass().getSimpleName();
    private FragmentHome mHomeFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_layout);

        FragmentManager fragmentManager = getSupportFragmentManager();
        Fragment fragment = fragmentManager.findFragmentByTag(TAG);
        if(fragment == null)
        {
            // Create the detail fragment and add it to the activity using a fragment transaction.
            mHomeFragment = new FragmentHome();
            fragmentManager.beginTransaction()
                    .add(R.id.fragment_container, mHomeFragment, TAG)
                    .commit();
        }
        else
        {
            // get our old fragment back !
            mHomeFragment = (FragmentHome)fragment;
        }
    }
}

如果您想在旋转设备后操纵片段(在本例中为mHomeFragment),则这非常有用。


虽然我之前使用了@labeeb提供的答案,但我认为检查唯一的“tag”是最好的方法。 - Craig Russell
有趣!那么看来谷歌需要更新他们的模板代码。 - Someone Somewhere
我认为他们应该改变这个生命周期的东西,任何在字典中找到的单词都是Android的生命周期方法。 - Farid
这个标签方法解决了我的问题!每次我想说“new HomeFragment”时,我首先检查是否可以通过findByTag找到非空的home fragment,如果找到了一个,我就使用它,否则我就创建一个新的 :) - Matheos

3

使用onAttachFragment()方法在您的Activity中重新分配对象:

@Override
public void onAttachFragment(Fragment fragment) {
    if (fragment instanceof MyFragment)
        this.myFragment = (MyFragment) fragment;
}

0

只是重新调整了@Ralf的答案,使其更具动态性,无需指定要保留的特定片段,但如果您想指定,也是可以的:

 public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //set Home/Main/default fragment
        changeFragmentTo(HomeFragment.newInstance(), FRAGMENT_TAG_HOME_FRAGMENT);


        if (getCurrentFragment() != null) {
            //if screen rotated retain Fragment
            changeFragmentTo(getCurrentFragment(), getCurrentFragment().getTag());

        }

    }


    private Fragment getCurrentFragment() {
//fl_main_container is FarmeLayout where I load my Fragments
        return getSupportFragmentManager().findFragmentById(R.id
                .fl_main_container);
    }



    /**
     * changeFragmentTo(Fragment fragmentToLoad, String fragmentTag)
     *
     * @param fragmentToLoad : dataType > v4.app.Fragment :: the object of the fragment you want to load in form of MyFragment() or MyFragment().newInstance()
     * @param fragmentTag    :  dataType > String :: a String which identify the "tag" of the fragment in form of "FRAGMENT_TAG_MY_FRAGMENT", Value must be stored in {@link models.MyConstants}
     */


    public void changeFragmentTo(Fragment fragmentToLoad, String fragmentTag) {

        if (getSupportFragmentManager().findFragmentByTag(fragmentTag) == null) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fl_main_container, fragmentToLoad, fragmentTag)
                    .setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                    .addToBackStack(fragmentTag)
                    .commit();

        } else {
            getSupportFragmentManager()
                    .beginTransaction()
                    .replace(R.id.fl_main_container, fragmentToLoad, fragmentTag)
                    .setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
                    .commit();
        }
    }



}

0

我在Activity的布局中定义了一个Fragment,在Fragment的onSaveInstanceState确实被调用了,但是在Fragment的onCreateView方法中,savedInstanceState Bundle却为null。

原因是我的Fragment在XML中没有ID:

android:id="@+id/compass_fragment" ...

-1
您可以在片段类的 OnCreate 中简单地设置 RetainInstance 属性。
public override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);
    RetainInstance = true;
}

旋转时保留Fragment对象


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