android - FragmentPagerAdapter : IllegalStateException Can't change container ID of fragment

标签 android android-fragments android-viewpager

首先:对于文本/代码墙感到抱歉,但我认为理解问题需要其中大部分内容。

我正在使用 ViewPagerTabHost 中的 Fragments 创建一个应用程序。 ViewPager 有一个自定义的 FragmentPagerAdapter,它将提供 ViewPager 中的各种页面。我遇到了自定义 FragmentPagerAdapter 开始将各种 Fragments 添加到 BackStack 的问题,但它在检查时失败了容器 ID(在本例中为 ViewPager 的 ID)与要添加的 Fragments 的 ID。这些是不同的,因此程序失败。我对使用 Fragments 还很陌生,所以我不确定我的代码是否遵循了最佳实践。以下可能是什么错误?

扩展主要 XML 布局的 Activity 。

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 的 fragment :

<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>

请注意,onCreateView 会为每个自定义 Fragment 调用,它们会被膨胀并返回每个的根 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 是 2131296319Fragment 的 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;
}

您必须返回您的 Fragment 的新实例,而不是现有的实例,它已经有一个父级,因此,一个容器 assinged。删除您在布局中声明的 Fragment,并更改您的 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;
}

关于android - FragmentPagerAdapter : IllegalStateException Can't change container ID of fragment,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30694156/

相关文章:

android - 如何为 JSON 对象中的 Firebase 云消息传递发送 "event_time"?

android - 从 Activity 完成异步后在 Fragment 上填充 ListView

java - 当 fragment 页面完全加载时旋转器或加载器

android - fragment 抽屉导航 View 页面指示器之间的变化

android - java.lang.IllegalStateException : Activity has been destroyed Exception 错误

android - 几个月后 Google Ads 突然停止工作 [Android][iOS][Flutter]

android - ContactsContract API 的简单示例

android - 服务在被杀死后不会重新启动,即使在 Return_STICKY 之后

android - 如何在 PagerAdapter 的 instantiateItem 方法中使用 AsyncTask

android - fragment 在重新附加时创建/恢复重复 View