【问题标题】:Display fragment viewpager within a fragment在片段中显示片段查看器
【发布时间】:2011-10-08 22:40:49
【问题描述】:

我有一个包含 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);
    }
}

【问题讨论】:

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


【解决方案1】:

使用 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);
        }
}

【讨论】:

  • 好的-不是那么完美。我有一个问题,当重新创建视图时,viewpager 中的视图是黑色的——因为里面的片段被重用了。我通过在片段的 onDestroyView 中有这段代码来解决它:
  • int 位置 = 0; for (TabInfo info : mTab​​sAdapter.mTabs) { String tag = FragmentPagerAdapter.makeFragmentName(mViewPager.getId(), position);片断片断; FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if ((fragment = getFragmentManager().findFragmentByTag(tag)) != null) { ft.remove(fragment); } ft.commit();位置++; } ...这太可怕了:O
  • 您的解决方案工作正常,但是当我按下后退按钮然后再次附加片段时,只有前两个片段可见,第三个片段的内容不可见。请帮忙
  • 请使用getChildFragmentManger() 这是更好的解决方案
  • 另外:每次将其中一个片段替换为子片段时,应执行以下操作: FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); for (int i = 0; i
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    我当时明白,上面的答案是为了绕过Fragments不能生孩子Fragments的限制。但是,既然最新的支持库支持Fragment 嵌套,您不再需要为ViewPager 设置适配器,只需传递getChildFragmentManager()。到目前为止,这对我来说效果很好。

    @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);
    
        mAdapter = new ViewPagerAdapter(getChildFragmentManager());
        mViewPager.setAdapter(mAdapter);
    
        return mView;
    }
    

    【讨论】:

    • 这应该是最新 Android 版本的公认答案。
    • getChildFragmentManager 用于片段 我该如何解决活动内部的问题?
    • @Milad 如果Activity 正在管理FragmentsViewPager,只需将ActivityFragmentManager (getFragmentManager()) 传递给Adapter
    • 我很难弄清楚,因为我在另一个片段托管的 view pager 中有一堆子片段。
    【解决方案4】:

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

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

    【讨论】:

    • 那个 mHandler 是从哪里来的?
    • 应该是成员,可以在onCreate()中实例化:mHandler = new Handler();
    • 这种方法是否会导致在 Fragment 之间切换时出现任何问题?如果我转到另一个片段然后再次返回包含 ViewPager 的片段,我目前遇到问题。它没有显示内容,也没有正确滚动。
    • @inazaruk 你能看看这个link
    【解决方案5】:

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

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

    【讨论】:

      【解决方案6】:

      在加载父片段时,我遇到了一个 IllegalStateException 消息:java.lang.IllegalStateException: Recursive entry to 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

      【讨论】:

        【解决方案7】:

        使用 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);
        

        【讨论】:

          【解决方案8】:

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

          如果你想管理嵌套片段,你必须使用getChildFragmentManager 而不是getFragmentManager

          【讨论】:

            【解决方案9】:

            无需在 AsyncTask 或任何 Handler 中设置适配器,只需在 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());
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-05-13
              • 1970-01-01
              • 2022-01-10
              相关资源
              最近更新 更多