6

有一些类似的答案,但不是针对这种情况。


我的情况很简单。

我有一个具有两种不同布局的活动,一种是纵向的,另一种是横向的。

Portrait中,我使用并动态<FrameLayout>添加Fragment它。

Landscape中,我使用<fragment>Fragment静态的。(其实这无关紧要)

它首先从Portrait开始,然后我Fragment简单地添加了:

ListFrag listFrag = new ListFrag();
getSupportFragmentManager().beginTransaction()
        .replace(R.id.FragmentContainer, listFrag).commit();

哪里ListFrag extends ListFragment

然后我做一个屏幕旋转。我发现 listFrag 正在横向模式下重新创建。 (在其中我注意到该onCreate()方法再次使用非空包调用)

我试着setRetainInstance(false)像@NPike 在这篇文章中所说的那样使用。但是默认情况下getRetainInstance()已经false是。正如文档所说,它没有达到我的预期。谁能解释一下?


我正在处理的片段是 a ListFragment,它setListAdapter()onCreate(). 所以这里不能使用该if (container == null) return null;方法。(或者我不知道如何申请)。

我从这篇文章中得到了一些提示。我应该if (bundle != null) setListAdapter(null); else setListAdapter(new ...);在我的ListFragment? 但是,有没有更好的方法在片段被销毁/分离时确实删除/删除片段,而不是在创建时执行它?(if (container == null) return null;方法如此)


编辑:

我发现的唯一整洁的方法是getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit();onSaveInstanceState(). 但这会引发另一个问题。

  1. 当屏幕被部分遮挡时,例如弹出 WhatsApp 或 TXT 对话框,该片段也会消失。(这是相对较小的,只是视觉问题)

  2. 当屏幕旋转时,Activity 被完全销毁并重新创建。onCreate(Bundle)所以我可以在or中重新添加片段onRestoreInstanceState(Bundle)。但是在(1)的情况下,以及切换活动,当用户返回我的活动时,既onCreate(Bundle)不会也不会被调用。onRestoreInstanceState(Bundle)我无处可重新创建 Activity(并从 Bundle 中检索数据)。

对不起,我没有说清楚,我已经有了一个决策,该getSupportFragmentManager()...replace(...).commit();行仅在纵向模式下运行。


示例代码

我已经提取了简单的代码,以便更好地说明情况:)

MainActivity.java

package com.example.fragremovetrial;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {

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

        if (findViewById(R.id.FragmentContainer) != null) {
            System.out.println("-- Portrait --");       // Portrait
            ListFrag listFrag = new ListFrag();
            getSupportFragmentManager().beginTransaction()
                    .replace(R.id.FragmentContainer, listFrag).commit();
        } else {
            System.out.println("-- Landscape --");      // Landscape
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (findViewById(R.id.FragmentContainer) != null) {
            System.out.println("getRetainInstance = " +
                    getSupportFragmentManager().findFragmentById(R.id.FragmentContainer).getRetainInstance());
        }
    }
}

布局/activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/FragmentContainer"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" />

layout-land/activity_main.xml (没关系)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
</LinearLayout>

ListFrag.java

package com.example.fragremovetrial;

import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.widget.ArrayAdapter;

public class ListFrag extends ListFragment {
    private String[] MenuItems = { "Content A", "Contnet B" };

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

        System.out.println("ListFrag.onCreate(): " + (savedInstanceState == null ? null : savedInstanceState));

        setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, MenuItems));
    }
}

注意我得到了以下

调试消息

-- Portrait --
ListFrag.onCreate(): null
getRetainInstance = false

(rotate Port -> Land)

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@4052dd28}]
-- Landscape --
以前焦点视图在保存期间报告 id 16908298,但在恢复期间找不到。

(rotate Land -> Port)

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@405166c8}]
-- Portrait --
ListFrag.onCreate(): null
getRetainInstance = false

(rotate Port -> Land )

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@4050fb40}]
-- 风景 --
以前聚焦的视图在保存期间报告了 id 16908298,但在恢复期间找不到。

(rotate Land -> Port)

ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray@40528c60}]
-- Portrait --
ListFrag.onCreate(): null
getRetainInstance = false

随着屏幕不断旋转,创建的片段数量不会无限增加,我无法解释。(请帮忙)

4

4 回答 4

3

处理此问题的正确方法是将以下内容放入您的 onCreate()

if (savedInstanceState == null) {
     // Do fragment transaction.
}
于 2014-07-21T17:46:36.833 回答
2

我终于想出了一个解决方案,以防止在重新创建 Activity 时重新创建不需要的 Fragment。就像我在问题中提到的那样:

我发现的唯一整洁的方法是getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit();onSaveInstanceState(). 但它会引发另一个问题......

...有一些改进。

onSaveInstanceState()

@Override
protected void onSaveInstanceState(Bundle outState) {
    if (isPortrait2Landscape()) {
        remove_fragments();
    }
    super.onSaveInstanceState(outState);
}

private boolean isPortrait2Landscape() {
    return isDevicePortrait() && (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}

isDevicePortrait()像:

private boolean isDevicePortrait() {
    return (findViewById(R.id.A_View_Only_In_Portrait) != null);
}

*请注意,我们无法getResources().getConfiguration().orientation确定设备当前是否为纵向。这是因为Resources对象在屏幕旋转后立即更改 - 甚至在调用之前! onSaveInstanceState()

如果我们不想使用findViewById()来测试方向(出于任何原因,而且它毕竟不是那么整洁),请保留一个全局变量并通过inprivate int current_orientation;初始化它。这看起来更整洁。但是我们应该注意不要在 Activity 生命周期的任何地方更改它。current_orientation = getResources().getConfiguration().orientation;onCreate()

*请确保我们remove_fragments() 之前 super.onSaveInstanceState()

(因为在我的情况下,我从 Layout 和 Activity 中删除了 Fragments。如果它在 之后super.onSaveInstanceState(),则 Layout 将已经保存到 Bundle 中。然后在 Activity 重新创建后, Fragments将被重新创建。 ###)

###我已经证明了这个现象。但是在 Activity 重新创建时如何确定 Fragment 恢复的原因是什么?只是我的猜测。如果您对此有任何想法,请回答我的另一个问题

于 2013-04-01T19:04:59.020 回答
0

在您的onCreate()方法中,您应该只ListFrag在您处于纵向模式时创建您的。您可以通过检查仅在纵向布局中具有的 FrameLayout 视图是否不是null.

if (findViewById(R.id.yourFrameLayout) != null) {
    // you are in portrait mode
    ListFrag listFrag = new ListFrag();
    getSupportFragmentManager().beginTransaction()
            .replace(R.id.FragmentContainer, listFrag).commit();
} else {
    // you are in landscape mode
    // get your Fragment from the xml
}

最后,如果您从纵向切换到横向布局,您不希望创建另一个 ListFrag,而是使用您在 xml 中指定的 Fragment。

于 2013-03-19T10:37:03.110 回答
0

@midnite,首先感谢您的问题,我有同样的问题,我工作了好几天。现在我为这个问题找到了一些棘手的解决方案 - 并想与社区分享。但如果有人有更好的解决方案,请写在评论中。

所以,我有同样的情况 - 两个设备方向的不同布局。在纵向中,不需要 DetailsFragment。

我只是在 OnCreate 中试图找到已经创建的片段:

    fragment = (ToDoListFragment) getFragmentManager().findFragmentByTag(LISTFRAGMENT);
    frameDetailsFragment = (FrameLayout) findViewById(R.id.detailsFragment);
    detailsFragment = (DetailsFragment) getFragmentManager().findFragmentByTag(DETAILS_FRAGMENT);
    settingsFragment = (SettingsFragment) getFragmentManager().findFragmentByTag(SETTINGS_FRAGMENT);

在那之后,我正在添加我的主要片段 -

    if (fragment == null){
        fragment = new ToDoListFragment();
        getFragmentManager().beginTransaction()
                .add(R.id.container, fragment, LISTFRAGMENT)
                .commit();
    }

并清除我的片段的任务:

  if (getFragmentManager().getBackStackEntryCount() > 0 ) {
        getFragmentManager().popBackStackImmediate();
    }

而最有趣的就在这里——

  destroyTmpFragments();

这是方法本身:

  private void destroyTmpFragments(){
    if (detailsFragment != null && !detailsFragment.isVisible()) {
        Log.d("ANT", "detailsFragment != null, Destroying");
        getFragmentManager().beginTransaction()
                .remove(detailsFragment)
                .commit();

        detailsFragment = null;
    }

    if (settingsFragment != null && !settingsFragment.isVisible()) {
        Log.d("ANT", "settingsFragment != null, Destroying");
        getFragmentManager().beginTransaction()
                .remove(settingsFragment)
                .commit();

        settingsFragment = null;
    }
}

如您所见,我手动清理了所有 FragmentManager 为我轻轻保存的片段(非常感谢他)。在日志中,我有下一个生命周期调用:

 04-24 23:03:27.164 3204    3204    D   ANT MainActivity onCreate()
 04-24 23:03:27.184 3204    3204    I   ANT DetailsFragment :: onCreateView
 04-24 23:03:27.204 3204    3204    I   ANT DetailsFragment :: onActivityCreated
 04-24 23:03:27.204 3204    3204    I   ANT DetailsFragment :: onDestroy
 04-24 23:03:27.208 3204    3204    I   ANT DetailsFragment :: onDetach

因此,在 onActivityCreated 中,让您的视图检查 null - 以防片段不可见。稍后将被删除(可能因为片段管理器的删除是异步的)之后,在活动的代码中,我们可以创建全新的片段实例,具有适当的布局(fargmnet 的占位符),片段将能够找到它的视图,例如,并且不会产生 NullPointerException

但是,在后台堆栈中的片段被删除得非常快,无需调用 onActivityCreated (在上面代码的帮助下:

 if (getFragmentManager().getBackStackEntryCount() > 0 ) {
        getFragmentManager().popBackStackImmediate();
    }

最后,如果我处于陆地方向,最后我会添加片段 -

 if (frameDetailsFragment != null){
        Log.i("ANT", "frameDetailsFragment != null");

        if (EntryPool.getPool().getEntries().size() > 0) {
            if (detailsFragment == null) {
                detailsFragment = DetailsFragment.newInstance(EntryPool.getPool().getEntries().get(0), 0);
            }

            getFragmentManager().beginTransaction()
                    .replace(R.id.detailsFragment, detailsFragment, DETAILS_FRAGMENT)
                    .commit();
        }
    }
于 2015-04-24T23:15:08.627 回答