在片段中显示片段viewpager

时间:2011-10-08 22:40:49

标签: android android-fragments android-viewpager android-lifecycle android-nested-fragment

我有一个包含ViewPager的片段。 ViewPager与包含一组片段的适配器相关联。

加载父片段后,我遇到一条IllegalStateException,其中包含以下消息:java.lang.IllegalStateException: Recursive entry to executePendingTransactions

一些研究使我得出的结论是系统无法在另一个片段中显示片段,但是似乎有一些迹象表明可以通过使用ViewPager(A bug in ViewPager using it with other fragment)完成此操作。

事实上,如果我在按下时在我的ViewPager上调用mPager.setAdapter(mAdapter)的父片段添加一个按钮,ViewPager会成功加载。这不太理想。

然后问题必须与片段生命周期有关。因此,我的问题是: 有没有其他人找到解决这个问题的方法,如果有,怎么办?

在片段事务处理之前,是否有某种方法可以延迟ViewPager上的适配器设置?

这是我的父片段代码:

    @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    mView = inflater.inflate(R.layout.team_card_master, container, false);
    mViewPager = (ViewPager)mView.findViewById(R.id.team_card_master_view_pager);

    final Button button = (Button)mView.findViewById(R.id.load_viewpager_button);
    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            mViewPager.setAdapter(mAdapter);
            button.setVisibility(View.GONE);
        }
    });

    mAdapter = new ViewPagerAdapter(getFragmentManager());
 //     mViewPager.setAdapter(mAdapter);

    return mView;
}

适配器:

public class ViewPagerAdapter extends FragmentPagerAdapter {
    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        if (mCursor == null) return 0;
        else return mCursor.getCount();
    }

    @Override
    public Fragment getItem(int position) {
        Bundle b = createBundle(position, mCursor);
        return TeamCardFragment.newInstance(b);
    }
}

9 个答案:

答案 0 :(得分:60)

使用AsyncTask为viewPager设置适配器。这个对我有用。 asyncTask是使原始片段完成它的转换。然后我们继续使用viewPager片段,基本上是为了避免递归。

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    mView = inflater.inflate(R.layout.team_card_master, container, false);
    mViewPager = (ViewPager)mView.findViewById(R.id.team_card_master_view_pager);

    final Button button = (Button)mView.findViewById(R.id.load_viewpager_button);
    button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            mViewPager.setAdapter(mAdapter);
            button.setVisibility(View.GONE);
        }
    });

    mAdapter = new ViewPagerAdapter(getFragmentManager());
    new setAdapterTask().execute();

    return mView;
}

private class setAdapterTask extends AsyncTask<Void,Void,Void>{
      protected Void doInBackground(Void... params) {
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
                   mViewPager.setAdapter(mAdapter);
        }
}

答案 1 :(得分:37)

您可以使用FragmentStatePagerAdapter代替FragmentPageAdapter。它会为你删除碎片。

答案 2 :(得分:24)

我刚遇到同样的问题。我有一个Fragment需要托管ViewPager Fragments。当我在Fragment之上堆叠另一个ViewPagerFragment,然后回击时,我会得到一个IllegalStateException。检查完日志后(堆叠新的Fragment时),我发现我的ViewPagerFragment将通过其生命周期方法停止并销毁,但Fragments中的子ViewPager将留在onResume。我意识到,Fragments应该为FragmentManager管理ViewPagerFragment,而不是FragmentManager的{​​{1}}。

我当时理解,上述答案是为了解决Activity无法生育孩子Fragments的限制。但是,现在最新的支持库支持Fragments嵌套,您不再需要为Fragment设置适配器,并且只需要传递ViewPager即可。到目前为止,这对我来说非常合适。

getChildFragmentManager()

答案 3 :(得分:19)

我使用Handler.post()在原始片段事务之外设置适配器:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mViewPager.setAdapter(mAdapter);     
        }
    });        
}

答案 4 :(得分:0)

尝试在ViewCreated上的TabIndicator上初始化viewPager适配器时,我遇到了这个问题。在网上阅读之后,我意识到这可能只是因为片段尚未添加到活动中。所以我将初始化代码移动到片段的onStart(),这应该可以解决你的问题。但不适合我,我仍然会再次遇到同样的问题。

但那是因为在我的代码中,我试图通过显式调用executePendingTransactions()来阻止正常的异步执行挂起任务,阻塞后一切运行良好

答案 5 :(得分:0)

  

加载父片段后,遇到IllegalStateException并带有消息:java.lang.IllegalStateException:executePendingTransactions的递归入口。
  
  一些研究使我得出结论,系统无法在另一个片段中显示片段,但是似乎有一些迹象表明可以通过使用ViewPager完成此操作(ViewPager中的一个错误与其他片段一起使用) )。

显式设置ViewPager的ID。

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
      mView = inflater.inflate(R.layout.team_card_master, container, false);
      mViewPager = (ViewPager)mView.findViewById(R.id.team_card_master_view_pager);
      mViewPager.setId(100);

      final Button button = (Button)mView.findViewById(R.id.load_viewpager_button);
      button.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
          mViewPager.setAdapter(mAdapter);
          button.setVisibility(View.GONE);
        }
      });
      mAdapter = new ViewPagerAdapter(getFragmentManager());
      mViewPager.setAdapter(mAdapter);

      return mView;
    }

您可以在xml资源中使用id而不是幻数100。

<resources>
  <item name="Id_For_ViewPager" type="id"/>
</resources>

和setId(R.Id_For_ViewPager)。

有关更多详细信息,请参阅FragmentManager.java源。

https://github.com/android/platform_frameworks_support/blob/master/v4/java/android/support/v4/app/FragmentManager.java#L900

或者看这个页面(日文)。最初的想法来自她,实际上,不是我。 http://y-anz-m.blogspot.jp/2012/04/androidfragment-viewpager-id.html

答案 6 :(得分:0)

使用Thomas Amssler撰写的这篇精彩文章:http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

我创建了一个可用于创建所有类型片段的适配器。 适配器本身如下所示:

public abstract class EmbeddedAdapter extends FragmentStatePagerAdapter {

private SparseArray<WeakReference<Fragment>>    mPageReferenceMap   = new SparseArray<WeakReference<Fragment>>();
private ArrayList<String> mTabTitles;

public abstract Fragment initFragment(int position);

public EmbeddedAdapter(FragmentManager fm, ArrayList<String> tabTitles) {
    super(fm);
    mTabTitles = tabTitles;
}

@Override
public Object instantiateItem(ViewGroup viewGroup, int position) {
    mPageReferenceMap.put(Integer.valueOf(position), new WeakReference<Fragment>(initFragment(position)));
    return super.instantiateItem(viewGroup, position);
}

@Override
public int getCount() {
    return mTabTitles.size();
}

@Override
public CharSequence getPageTitle(int position) {
    return mTabTitles.get(position);
}

@Override
public Fragment getItem(int pos) {
    return getFragment(pos);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferenceMap.remove(Integer.valueOf(position));
}

public Fragment getFragment(int key) {

    WeakReference<Fragment> weakReference = mPageReferenceMap.get(key);

    if (null != weakReference) {

        return (Fragment) weakReference.get();

    } else {
        return null;
    }
}

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
}

}

要使用适配器,您必须执行以下操作:

private void setAdapter() {

    if(mAdapter == null) mAdapter = new EmbeddedAdapter(getActivity().getSupportFragmentManager(), mTabTitles) {

        @Override
        public Fragment initFragment(int position) {

            Fragment fragment;

            if(position == 0){

                // A start fragment perhaps?
                fragment = StartFragment.newInstance(mBlocks);

            }else{

                // Some other fragment
                fragment = PageFragment.newInstance(mCategories.get((position)));
            }
            return fragment;
        }
    };

    // Have to set the adapter in a handler
    Handler handler = new Handler();
    handler.post(new Runnable() {

        @Override
        public void run() {

            mViewPager.setVisibility(View.VISIBLE);
            mPagerTabStrip.setVisibility(View.VISIBLE);
            mAdapter.notifyDataSetChanged();
        }
    });
}

请注意,所有片段都使用:

setRetainInstance(true);

答案 7 :(得分:0)

根据文件:https://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager()

如果您想管理嵌套片段,则必须使用getChildFragmentManager代替getFragmentManager

答案 8 :(得分:0)

代替在AsyncTask或任何处理程序中设置适配器,只需在 onResume()部分中编写该部分代码

这样做,您将:

  • 让主要片段的过渡完成。
  • 不使用任何后台任务即可显示子片段。

onResume()的代码如下:

@Override
public void onResume() {
    super.onResume();
    if(mViewPager.getAdapter()==null||mViewPager.getAdapter().getCount()==0)
    mViewPager.setAdapter(mAdapter);
}

也在 onCreateView 中 代替

mAdapter = new ViewPagerAdapter(getFragmentManager());

替换为

mAdapter = new ViewPagerAdapter(getChildFragmentManager());