我正在尝试学习Android Fragment,并且我对Fragment管理有一个非常具体的问题,因为屏幕方向会搞砸我的实现。编辑:已经解决了我的问题,请参阅下面的“更新”。精简版:使用静态片段,如果更改屏幕方向,则会丢失对R.id.fragment的引用,并且Activity重新创建Fragment会引起问题,因为布局中仍然存在另一个Fragment(因为它们是在XML)。内容:我有一个使用默认Eclipse模板的Master / Detail工作流,并且ItemList上的每个选项卡都有不同的Fragment类型。理想情况下,我想做的是在片段之间切换,但是我想不使用BackStack来保持它们的当前状态,因为我想使用ItemList导航,并使用“后退”按钮关闭应用程序。对于该特定问题,我找不到任何解决方案,因此尝试了许多不同的方法。现在,我正在使用在主布局中定义的静态片段:<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/item_detail_container"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".ItemDetailActivity"tools:ignore="MergeRootFrame" ><fragment android:name="com.example.pintproject.DevicesFragment" android:id="@+id/devices" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /><fragment android:name="com.example.pintproject.ItemDetailFragment" android:id="@+id/detail" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> </FrameLayout>在ItemListActivity onCreate()中,我查找布局中的Fragment,如果尚未创建它们,请添加它们,并且保留对当前活动Detail Fragment的引用,以便隐藏/显示我切换到的片段。我使用隐藏/显示而不是替换,因为替换会破坏Fragment:@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_item_list); if (findViewById(R.id.item_detail_container) != null) { // The detail container view will be present only in the // large-screen layouts (res/values-large and // res/values-sw600dp). If this view is present, then the // activity should be in two-pane mode. mTwoPane = true; // In two-pane mode, list items should be given the // 'activated' state when touched. ((ItemListFragment) getSupportFragmentManager().findFragmentById( R.id.item_list)).setActivateOnItemClick(true); } df = (DevicesFragment) getSupportFragmentManager().findFragmentById(R.id.devices); if (df==null){ df = new DevicesFragment(); getSupportFragmentManager().beginTransaction().add(R.id.item_detail_container,df).commit(); getSupportFragmentManager().beginTransaction().hide(df).commit(); } idf = (ItemDetailFragment) getSupportFragmentManager().findFragmentById(R.id.detail); if (idf==null){ idf = new ItemDetailFragment(); getSupportFragmentManager().beginTransaction().add(R.id.item_detail_container,idf).commit(); getSupportFragmentManager().beginTransaction().hide(idf).commit(); } mContent = df;@Overridepublic void onItemSelected(String id) { if (mTwoPane) { // In two-pane mode, show the detail view in this activity by // adding or replacing the detail fragment using a // fragment transaction. switch (Integer.valueOf(id)){ case 1:{ if (idf!=null){ getSupportFragmentManager().beginTransaction().hide(mContent).commit(); getSupportFragmentManager().beginTransaction().show(idf).commit(); mContent = idf; } }break; case 2:{ if (df!=null){ getSupportFragmentManager().beginTransaction().hide(mContent).commit(); getSupportFragmentManager().beginTransaction().show(df).commit(); mContent = df; } }break; } } else { // In single-pane mode, simply start the detail activity // for the selected item ID. Intent detailIntent = new Intent(this, ItemDetailActivity.class); detailIntent.putExtra(ItemDetailFragment.ARG_ITEM_ID, id); startActivity(detailIntent); }}问题:通过这种方法,Fragment可以毫无问题地隐藏/显示并保持状态,但是如果我进行方向更改,它们将被销毁并重新创建。我知道它们被破坏了,因为我没有使用setRetainInstance(),但是问题是当我改变方向时,Activity失去了对Fragment的引用,并且df = (DevicesFragment) getSupportFragmentManager().findFragmentById(R.id.devices);为null,因此程序将创建另一个Fragment。如果我再次改变方向,不仅程序会重新创建两个新的Fragments,而且还会以某种方式将另外两个Fragment添加到布局中,甚至没有隐藏它们,它们在另一个之上显示。如果我使用setRetainInstance(),则在更改方向后,Fragment会保持状态,但是,对Fragment的活动引用为null,并在现有的Fragment上方创建一个新的Fragment,每个都有两个。例:我在横向方向上创建了Fragment A和FragmentB。两者都可以正常工作,我可以在它们之间切换。我将方向更改为“纵向”,Fragment A和Fragment B被破坏,并创建了新的Fragment A'和Fragment B',但它们仍然可以正常工作。我再次将方向更改为横向,Fragment A'和Fragment B'被销毁,创建了新的Fragment A''和Fragment B'',但是屏幕上显示了另一个Fragment A和 B,它们都同时(一个在另一个之上,我们称它们为残差),并且这些新的A''和B''工作正常,但显示在残差A和B上方。从现在开始,每次更改方向时,都会将2个新的Fragment添加到先前的Fragment中,但是它们甚至都不会保持先前的状态。我希望这个例子足够清楚。我认为问题在于Activity在更改方向时不保存视图引用,再次创建了它们,但我真的不知道该如何解决。更新:我通过使用findFragmentByTag而不是findFragmentById解决了我的问题。由于现在可以检索已经创建的Fragment,因此必须将它们添加到容器中,并添加要搜索的特定标签。所以我的测试代码如下:@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_item_list); if (findViewById(R.id.item_detail_container) != null) { // The detail container view will be present only in the // large-screen layouts (res/values-large and // res/values-sw600dp). If this view is present, then the // activity should be in two-pane mode. mTwoPane = true; // In two-pane mode, list items should be given the // 'activated' state when touched. ((ItemListFragment) getSupportFragmentManager().findFragmentById( R.id.item_list)).setActivateOnItemClick(true); } df = (DevicesFragment) getSupportFragmentManager().findFragmentByTag("df"); idf = (ItemDetailFragment) getSupportFragmentManager().findFragmentByTag("idf"); if (savedInstanceState==null){ if (df==null){ df = new DevicesFragment(); getSupportFragmentManager().beginTransaction().add(R.id.item_detail_container,df, "df").commit(); getSupportFragmentManager().beginTransaction().hide(df).commit(); } if (idf==null){ idf = new ItemDetailFragment(); getSupportFragmentManager().beginTransaction().add(R.id.item_detail_container,idf,"idf").commit(); getSupportFragmentManager().beginTransaction().hide(idf).commit(); } } else { Log.i("OUT","INSTANCE NOT NULL"); } mContent = df;}这是完全可用的功能,每个setRetainInstance(true)都必须具有Fragment,并且无论我们改变方向多少次,它们都保持其当前状态。 (adsbygoogle = window.adsbygoogle || []).push({}); 最佳答案 您绝不能持有对该片段的引用。代替。每当您需要它时,都可以在短时间内检索参考。public ItemListFragment getItemListFragment() { return ((ItemListFragment) getSupportFragmentManager().findFragmentById( R.id.item_list));}然后,每当您需要从中获取数据时,请使用final ItemListFragment listFragment = getItemListFragment();if (listFragment != null) { // do something}并避免致电二传手。您可以定义设置器,但是更好的做法是在创建Fragment时传递参数,或者通过Fragment本身的getActivity()检索数据,如下所述。这样做是因为Fragment生命周期并不总是与Activity 1匹配。如果必须从Activity调用setter,请不要忘记将值保存在Fragment的onSaveInstanceState()中(如果需要)。所以不要打电话setActivateOnItemClick(true);在活动中,从片段中执行。@Overridepublic void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final YourActivity activity = (Activtity) getYourActivity(); setActivateOnItemClick(activity.isMultiPane());}这样,当在Activity onCreate()之后重新创建Fragment(仅在其中处理值设置的情况下)时,它将始终可以访问该值然后从Activity定义isMultiPane方法public boolean isMultiPane() { return mTwoPane;} (adsbygoogle = window.adsbygoogle || []).push({});