5

情報:の中に2ペインのレイアウト(2つの子Fragments)があります。ParentFragmentもちろん、これはの中にありFragmentActivityます。私はに持っsetRetainInstance(true)ていParentFragmentます。向きを変更しても、左側の子フラグメント破棄されonCreate()ません(呼び出されません)。これは正常です(親がインスタンスを保持しているため)。

問題:向きを変えると、右側のフラグメント破壊されます(onCreate()呼び出されます)。なぜ地獄は右の断片が破壊され、左の断片は破壊されないのですか?

編集:を削除するsetRetainInstance(true)と、左側のフラグメントが2回onCreate()呼び出され(lol wtf)、右側のフラグメントが1回呼び出されます。だからこれも良くない...onCreate()

ParentFragmentの以下のコード:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    this.setRetainInstance(true);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.fragment_schedule, container, false);
    setHasOptionsMenu(true);


    if (getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left) == null || 
            !getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout())
    {
        if (mPresentationsListFragment == null)
            mPresentationsListFragment = PresentationsListFragment.newInstance(PresentationsListFragment.TYPE_SCHEDULE, mScheduleDate);
        getChildFragmentManager().beginTransaction()
                                     .replace(R.id.fragment_schedule_framelayout_left, mPresentationsListFragment)
                                     .commit();
    }
    mPresentationsListFragment.setOnPresentationClickListener(this);


    return view;
}


@Override
    public void onPresentationClick(int id)
    {
        if (Application.isDeviceTablet(getActivity()))
        {
            if (getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_right) == null)
            {
                if (mPresentationDetailFragment == null)
                    mPresentationDetailFragment = PresentationDetailFragment.newInstance(id);
                else
                    mPresentationDetailFragment.loadPresentation(id);
                getChildFragmentManager().beginTransaction()
                                           .replace(R.id.fragment_schedule_framelayout_right, mPresentationDetailFragment)
                                           .commit();
            }
            else
                mPresentationDetailFragment.loadPresentation(id);
        }
        else
        {
            Intent presentationDetailIntent = new Intent(getActivity(), PresentationDetailActivity.class);
            presentationDetailIntent.putExtra(PresentationDetailActivity.KEY_PRESENTATION_ID, id);
            startActivity(presentationDetailIntent);
        }
    }

LEソリューション:antonytに 感謝します、答えは以下のとおりです。実行するために必要な唯一の変更は、親のonCreateView()内にありますFragment

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState)
{
    View view = inflater.inflate(R.layout.fragment_schedule, container, false);
    setHasOptionsMenu(true);


    if (getChildFragmentManager().findFragmentById(R.id.fragment_presentations_framelayout_left) == null)
    {
        mPresentationsListFragment = PresentationsListFragment.newInstance();
        mPresentationsListFragment.setOnPresentationClickListener(this);
        getChildFragmentManager().beginTransaction()
                .add(R.id.fragment_presentations_framelayout_left, mPresentationsListFragment)
                .commit();
    }


    return view;
}
4

1 に答える 1

5

私が理解していることからsetRetainInstance(true)、上記のコードで親フラグメントを使用している場合は、向きを変更するときに、左側のフラグメントを再作成する必要がありますが、右側のフラグメントは再作成しないでください。これはあなたが上で書いたものとは逆ですが、とにかくこれが事実である理由を説明します。setRetainInstance(false)親フラグメントがある場合は、左側のフラグメントが2回作成され、右側のフラグメントが1回作成されていることを確認してください。

ケース1:setRetainInstance(true)

親フラグメントはローテーション時に破棄されません。ただし、それでも毎回ビューが再作成されます(onDestroyViewこのonCreateView順序で呼び出されます)。onCreateView特定の条件下で左側のフラグメントを追加するコードがあります。getChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left)以前にフラグメントがそのコンテナに追加されたため、null以外である必要があります。XMLを介して追加されたフラグメントのみがtrueを返すgetChildFragmentManager().findFragmentById(R.id.fragment_schedule_framelayout_left).isInLayout() ため、falseにする必要があります。全体的な条件が真であるため、左側のフラグメントの新しいインスタンスが作成され、古いインスタンスが置き換えられます。右のフラグメントはクリックイベント中にのみインスタンス化されるため、特別な動作は発生しません。

概要:親フラグメントが残り、新しい左フラグメントが作成され、右フラグメントが残ります。

ケース2:setRetainInstance(false)

親フラグメントが破棄され、左右のフラグメントも破棄されます。3つのフラグメントはすべて、Androidによって自動的に再作成されます。次に、親フラグメントはそのビューを作成する機会を得て、上記の説明に従って、左側のフラグメントの新しいインスタンスを作成します。作成されたばかりの左側のフラグメントは、この新しいインスタンスに置き換えられます。左側のフラグメントが破棄され、別の左側のフラグメントが作成されることがわかります。適切なフラグメントに対して特別な動作は発生しません。

概要:新しい親フラグメントが作成され、2つの新しい左フラグメントが作成され、新しい右フラグメントが作成されます。

setRetainInstance(true)この場合、左側のフラグメントではなく右側のフラグメントが破棄されていることが確実な場合は、サンプルプロジェクトをgithub/etcに投稿してください。これを示しています。

FragmentTransaction.replace()更新:左側のフラグメントで使用すると、右側のフラグメントが削除される理由

内部条件のため、コードは同じコンテナ上で左側のフラグメントをそれ自体に置き換えようとします。

置換を処理するAndroid4.1ソースコードのコードスニペットは次のとおりです。

...
case OP_REPLACE: {
    Fragment f = op.fragment;
    if (mManager.mAdded != null) {
        for (int i=0; i<mManager.mAdded.size(); i++) {
            Fragment old = mManager.mAdded.get(i);
            if (FragmentManagerImpl.DEBUG) Log.v(TAG,
                    "OP_REPLACE: adding=" + f + " old=" + old);
            if (f == null || old.mContainerId == f.mContainerId) {
                if (old == f) {
                    op.fragment = f = null;
                } else {
                    if (op.removed == null) {
                        op.removed = new ArrayList<Fragment>();
                    }
                    op.removed.add(old);
                    old.mNextAnim = op.exitAnim;
                    if (mAddToBackStack) {
                        old.mBackStackNesting += 1;
                        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "Bump nesting of "
                                + old + " to " + old.mBackStackNesting);
                    }
                    mManager.removeFragment(old, mTransition, mTransitionStyle);
                }
            }
        }
    }
    if (f != null) {
        f.mNextAnim = op.enterAnim;
        mManager.addFragment(f, false);
    }
} break;
...

同じフラグメントをそれ自体で置き換えようとすると、この操作を無視しようとするコードがいくつかあります。

if (old == f) {
    op.fragment = f = null;
}

はnullであり、フラグメントの反復処理を継続しているためf、これには、FragmentManagerから後続のすべてのフラグメントを削除するという副作用があるようです。これは意図的なものではないと思いますが、少なくとも、正しいフラグメントが破壊されている理由を説明しています。replaceを使用しない/同じフラグメントをそれ自体で置き換えないことで問題を解決できます。

興味深いことに、これは最近の変更であり、以前のバージョンのAndroidには存在しませんでした。 https://github.com/android/platform_frameworks_support/commit/5506618c80a292ac275d8b0c1046b446c7f58836

バグレポート:https ://code.google.com/p/android/issues/detail?id = 43265

于 2013-07-25T15:05:42.773 に答える