FragmentPagerAdapter:IllegalStateException 无法更改片段的容器 ID
FragmentPagerAdapter: IllegalStateException Can't change container ID of fragment
首先:抱歉翻墙了text/code,不过我觉得大部分都需要看懂问题了
我正在 ViewPager
和 TabHost
中使用 Fragments
创建一个应用程序。 ViewPager
有一个自定义 FragmentPagerAdapter
,它将提供 ViewPager
中的各个页面。我 运行 遇到一个问题,自定义 FragmentPagerAdapter
开始将各种 Fragments
添加到 BackStack
,但它在检查容器 ID(在在本例中,ViewPager
) 的 ID 与要添加的 Fragments
的 ID 相对应。这些是不同的,因此程序失败。我对使用 Fragments
还很陌生,所以我不确定我的代码是否遵循最佳实践。以下可能是什么错误?
Activity,使主要 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>
请注意,为每个自定义 Fragment
调用 onCreateView
,它们被膨胀并返回每个自定义的根 View
。这是一个例子,FilteredRecipesFragment
。其他习俗Fragments
类似。
public class FilteredRecipesFragment extends Fragment {
private FilteredRecipesListFragment mFilteredRecipesListFragment;
private Button showRecipeFilterButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.filtered_recipes_fragment, container, false);
mFilteredRecipesListFragment = (FilteredRecipesListFragment)
getFragmentManager().findFragmentById(R.id.filtered_recipes_list_fragment);
showRecipeFilterButton = (Button) rootView.findViewById(R.id.show_recipe_filter_dialog_button);
showRecipeFilterButton.setOnClickListener(new RecipeFilterButtonListener());
return rootView;
}
}
最后,自定义 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:
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);
}
}
一切 似乎 工作正常,但在每个自定义 Fragment
膨胀后,自定义 ViewPager
也开始添加它们。这是 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>
在 BackStackRecord.doAppOp
中失败,因为容器 ID(即 MyViewPager
的 ID 与 Fragment
ID 不同。这是该方法的代码:
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
// IT FAILS HERE!
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
我知道容器 ID 是自定义 ViewPager
的 ID,因为在 instantiateItem(ViewGroup, int)
调用中传递的是它的 ID。在我的例子中,MyViewPager
实例的 ID 是 2131296319 而 Fragment
的 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;
}
您必须 return 您的 Fragment
的新实例,而不是现有实例,它已经有一个父实例,因此,一个容器被分配。删除您在布局中声明的 Fragment
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();
}
首先:抱歉翻墙了text/code,不过我觉得大部分都需要看懂问题了
我正在 ViewPager
和 TabHost
中使用 Fragments
创建一个应用程序。 ViewPager
有一个自定义 FragmentPagerAdapter
,它将提供 ViewPager
中的各个页面。我 运行 遇到一个问题,自定义 FragmentPagerAdapter
开始将各种 Fragments
添加到 BackStack
,但它在检查容器 ID(在在本例中,ViewPager
) 的 ID 与要添加的 Fragments
的 ID 相对应。这些是不同的,因此程序失败。我对使用 Fragments
还很陌生,所以我不确定我的代码是否遵循最佳实践。以下可能是什么错误?
Activity,使主要 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>
请注意,为每个自定义 Fragment
调用 onCreateView
,它们被膨胀并返回每个自定义的根 View
。这是一个例子,FilteredRecipesFragment
。其他习俗Fragments
类似。
public class FilteredRecipesFragment extends Fragment {
private FilteredRecipesListFragment mFilteredRecipesListFragment;
private Button showRecipeFilterButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.filtered_recipes_fragment, container, false);
mFilteredRecipesListFragment = (FilteredRecipesListFragment)
getFragmentManager().findFragmentById(R.id.filtered_recipes_list_fragment);
showRecipeFilterButton = (Button) rootView.findViewById(R.id.show_recipe_filter_dialog_button);
showRecipeFilterButton.setOnClickListener(new RecipeFilterButtonListener());
return rootView;
}
}
最后,自定义 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:
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);
}
}
一切 似乎 工作正常,但在每个自定义 Fragment
膨胀后,自定义 ViewPager
也开始添加它们。这是 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>
在 BackStackRecord.doAppOp
中失败,因为容器 ID(即 MyViewPager
的 ID 与 Fragment
ID 不同。这是该方法的代码:
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
if (tag != null) {
if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
throw new IllegalStateException("Can't change tag of fragment "
+ fragment + ": was " + fragment.mTag
+ " now " + tag);
}
fragment.mTag = tag;
}
if (containerViewId != 0) {
if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
// IT FAILS HERE!
throw new IllegalStateException("Can't change container ID of fragment "
+ fragment + ": was " + fragment.mFragmentId
+ " now " + containerViewId);
}
fragment.mContainerId = fragment.mFragmentId = containerViewId;
}
Op op = new Op();
op.cmd = opcmd;
op.fragment = fragment;
addOp(op);
}
我知道容器 ID 是自定义 ViewPager
的 ID,因为在 instantiateItem(ViewGroup, int)
调用中传递的是它的 ID。在我的例子中,MyViewPager
实例的 ID 是 2131296319 而 Fragment
的 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;
}
您必须 return 您的 Fragment
的新实例,而不是现有实例,它已经有一个父实例,因此,一个容器被分配。删除您在布局中声明的 Fragment
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();
}