Android FragmentPagerAdapter:IllegalStateException可以';t更改片段的容器ID
第一:很抱歉有这么多的文本/代码,但我认为大部分内容都是理解问题所必需的 我正在使用Android FragmentPagerAdapter:IllegalStateException可以';t更改片段的容器ID,android,android-fragments,android-viewpager,Android,Android Fragments,Android Viewpager,第一:很抱歉有这么多的文本/代码,但我认为大部分内容都是理解问题所必需的 我正在使用ViewPager和TabHost中的Fragments创建一个应用程序。ViewPager有一个自定义的FragmentPagerAdapter,它将为ViewPager中的各个页面提供信息。我遇到了一个问题,自定义FragmentPagerAdapter开始将各种片段添加到backbackback,但在检查容器ID(在本例中是查看页面的ID)与要添加的片段的ID时失败。这些是不同的,因此程序失败。我对使用片段
ViewPager
和TabHost
中的Fragments
创建一个应用程序。ViewPager
有一个自定义的FragmentPagerAdapter
,它将为ViewPager
中的各个页面提供信息。我遇到了一个问题,自定义FragmentPagerAdapter
开始将各种片段添加到backbackback
,但在检查容器ID(在本例中是查看页面的ID
)与要添加的片段的ID时失败。这些是不同的,因此程序失败。我对使用片段相当陌生,所以我不确定我的代码是否遵循最佳实践。下面的错误是什么
活动,它使主XML布局膨胀。
public class MyActivity extends FragmentActivity implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener{
private MyViewPager mViewPager;
private FragmentTabHost mFragmentTabHost;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
setupViewPager();
setupFragmentTabHost();
}
private void setupViewPager()
{
mViewPager = (MyViewPager) findViewById(R.id.my_pager);
}
private void setupFragmentTabHost()
{
mFragmentTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mFragmentTabHost.setOnTabChangedListener(this);
mFragmentTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
mFragmentTabHost.addTab(mFragmentTabHost.newTabSpec("tab1").setIndicator("Tab 1", null), TabFragment.class, null);
mFragmentTabHost.addTab(mFragmentTabHost.newTabSpec("tab2").setIndicator("Tab 2", null), TabFragment.class, null);
mFragmentTabHost.addTab(mFragmentTabHost.newTabSpec("tab3").setIndicator("Tab 3", null), TabFragment.class, null);
}
@Override
protected void onDestroy()
{
}
public MyViewPager getMyPager()
{
return mViewPager;
}
@Override
public void onTabChanged(String tabId) {
int position = mFragmentTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
@Override
public void onPageSelected(int position)
{
mFragmentTabHost.setCurrentTab(position);
}
@Override
public void onPageScrollStateChanged(int arg0) {}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
}
主XML文件my_activity.XML,包含ViewPager、TabHost和ViewPager的片段:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.app.FragmentTabHost
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0" />
<com.mycompany.myapp.gui.mypager.MyViewPager
android:id="@+id/my_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<TabWidget
android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.app.FragmentTabHost>
<fragment
android:name="com.mycompany.myapp.gui.mypager.FilteredRecipesFragment"
android:id="@+id/filtered_recipes_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
<fragment
android:name="com.mycompany.myapp.gui.mypager.SelectedRecipesFragment"
android:id="@+id/selected_recipes_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
<fragment
android:name="com.mycompany.myapp.gui.mypager.ShoppingListFragment"
android:id="@+id/shopping_list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
</LinearLayout>
public class MyPagerAdapter extends FragmentPagerAdapter{
private MyActivity mMyActivity;
public MyPagerAdapter(FragmentManager fragmentManager, MyActivity myActivity)
{
super(fragmentManager);
mMyActivity = myActivity;
}
@Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.filtered_recipes_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.shopping_list_fragment);
default:
return null;
}
}
@Override
public int getCount()
{
return PagerConstants.NUMBER_OF_PAGES; // 3
}
@Override
public CharSequence getPageTitle(int position)
{
return PagerConstants.PAGE_TITLES(position);
}
}
最后,自定义ViewPager
及其自定义FragmentPagerAdapter
,程序失败
public class MyViewPager extends ViewPager{
private MyActivity mMyActivity;
private MyPagerAdapter mMyPagerAdapter;
public MyViewPager(Context context, AttributeSet attrs)
{
super(context, attrs);
mMyActivity = (MyActivity) context;
mMyPagerAdapter = new MyPagerAdapter(mMyActivity.getSupportFragmentManager(), mMyActivity);
this.setAdapter(mMyPagerAdapter);
this.setOnPageChangeListener(mMyActivity);
this.setCurrentItem(PagerConstants.PAGE_SHOPPING_LIST); // Page 0
}
}
MyPagerAdapter.java:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<android.support.v4.app.FragmentTabHost
android:id="@android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0" />
<com.mycompany.myapp.gui.mypager.MyViewPager
android:id="@+id/my_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<TabWidget
android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v4.app.FragmentTabHost>
<fragment
android:name="com.mycompany.myapp.gui.mypager.FilteredRecipesFragment"
android:id="@+id/filtered_recipes_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
<fragment
android:name="com.mycompany.myapp.gui.mypager.SelectedRecipesFragment"
android:id="@+id/selected_recipes_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
<fragment
android:name="com.mycompany.myapp.gui.mypager.ShoppingListFragment"
android:id="@+id/shopping_list_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true" />
</LinearLayout>
public class MyPagerAdapter extends FragmentPagerAdapter{
private MyActivity mMyActivity;
public MyPagerAdapter(FragmentManager fragmentManager, MyActivity myActivity)
{
super(fragmentManager);
mMyActivity = myActivity;
}
@Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.filtered_recipes_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.shopping_list_fragment);
default:
return null;
}
}
@Override
public int getCount()
{
return PagerConstants.NUMBER_OF_PAGES; // 3
}
@Override
public CharSequence getPageTitle(int position)
{
return PagerConstants.PAGE_TITLES(position);
}
}
看起来一切正常,但在每个自定义片段
膨胀后,自定义查看页面
也开始添加它们。下面是Eclipse的堆栈输出:
06-07 15:37:49.815: E/AndroidRuntime(793): java.lang.IllegalStateException: Can't change container ID of fragment FilteredRecipesFragment{4605b0e0 #0 id=0x7f09003f android:switcher:2131296318:0}: was 2131296319 now 2131296318
BackStackRecord.doAddOp(int, Fragment, String, int) line: 407
BackStackRecord.add(int, Fragment, String) line: 389
MyPagerAdapter(FragmentPagerAdapter).instantiateItem(ViewGroup, int) line: 99
MyViewPager(ViewPager).addNewItem(int, int) line: 832
MyViewPager(ViewPager).populate(int) line: 982
MyViewPager(ViewPager).populate() line: 914
MyViewPager(ViewPager).onMeasure(int, int) line: 1436
MyViewPager(View).measure(int, int) line: 8171
LinearLayout(ViewGroup).measureChildWithMargins(View, int, int, int, int) line: 3132
... More calls <snipped>
我知道容器ID是自定义ViewPager
的ID,因为它是在实例化项(ViewGroup,int)
调用中传递的ID。在我的例子中,MyViewPager
实例的ID是2131296319,片段的ID是2131296318,因此它失败了
我在哪里转错弯了?在整个ViewPager
/FragmentPagerAdapter
/Fragment
概念中,我误解了什么?问题在于:
@Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.filtered_recipes_fragment);
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.selected_recipes_fragment);
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return mMyActivity.getSupportFragmentManager().findFragmentById(R.id.shopping_list_fragment);
default:
return null;
}
您必须返回片段
的新实例,而不是现有实例,该实例已经有一个父实例,因此有一个已分配的容器。删除您在布局中声明的片段
s,并更改getItem
等
@Override
public Fragment getItem(int position)
{
switch (position) {
case PagerConstants.PAGE_FILTER_RECIPES: // 0
return new FilteredRecipesFragment();
case PagerConstants.PAGE_SELECTED_RECIPES: // 1
return new SelectedRecipesFragment();
case PagerConstants.PAGE_SHOPPING_LIST: // 2
return new ShoppingListFragment()
default:
return null;
}
出现此问题的一个可能原因是,您尝试两次添加相同的片段。如下图所示
public void populateFragments() {
Fragment fragment = new Fragment();
//fragment is added for the first time
addFragment(fragment);
// fragment is added for the second time
// This call will be responsible for the issue. The
addFragment(fragment);
}
public void addFragment(Fragment fragment) {
FrameLayout frameLayout = AppView.createFrameLayout(context);
view.addView(frameLayout);
getSupportFragmentManager().beginTransaction().add(frameLayout.getId(), fragment).commit();
}
我这样做的原因首先是我需要维护每个片段中的状态。它们都有一些数组,其中包含从数据库更新的ListFragments
的数据,因此在我滑动或更改选项卡时需要保留这些数据。这方面的最佳做法是什么<代码>FragmentStatePagerAdapter
?如果我将片段
s从我的主layout.xml
移开,我应该将它们放在哪里?在他们自己的XML文件中?在您的情况下,FragmentStatePagerAdapter
或FragmentPagerAdapter
应该没有多大区别FragmentPagerAdapter
应该将片段保存在内存中,这样您就不必担心数据了。另外,如果您的数据在数据库中,并且您使用的是ContentProvider和Loader,那么重新加载也不是什么大问题。如果我将片段从main layout.xml中移出,我应该将它们放在哪里?在他们自己的XML文件中?也不只需将其实例化为运行时。ViewPager负责为您处理事务是的,您是对的,数据仍然存在。我只是对new
的意思感到困惑,好吧,创建一个新的片段。我没有意识到它在本例中重用了旧的片段。我理解对了吗?也就是说,当这样做时,片段
被重用?这是内存使用和CPU之间的折衷,对吗?是的,片段
s会一直保存在片段管理器中,只要用户可以返回页面。