Отобразить фрагмент просмотра в фрагменте - PullRequest
54 голосов
/ 09 октября 2011

У меня есть фрагмент, который содержит ViewPager.ViewPager связан с адаптером, который содержит набор фрагментов.

При загрузке родительского фрагмента меня встречает IllegalStateException с сообщением: java.lang.IllegalStateException: Recursive entry to executePendingTransactions.

Некоторые исследованияпривел меня к выводу, что система не может отображать фрагменты внутри другого фрагмента, ОДНАКО, кажется, есть некоторые признаки того, что это можно сделать именно с помощью ViewPager ( Ошибка в ViewPager при использовании его с другимифрагмент ).

Фактически, если я добавлю кнопку к своему родительскому фрагменту, которая при нажатии вызывает на моем 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 ]

60 голосов
/ 09 октября 2011

используйте 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);
        }
}
38 голосов
/ 31 января 2012

Вы можете использовать FragmentStatePagerAdapter вместо FragmentPageAdapter.Он удалит фрагменты для вас.

24 голосов
/ 24 января 2013

Я только что столкнулся с этой же проблемой.У меня был Fragment, необходимый для размещения ViewPager из Fragments.Когда я сложил еще один Fragment поверх моего ViewPagerFragment, а затем нанес удар, я получил IllegalStateException.После проверки журналов (при укладке нового Fragment) я обнаружил, что мой ViewPagerFragment будет проходить через все методы его жизненного цикла для остановки и уничтожения, однако его потомки Fragments в ViewPager останутся в onResume,Я понял, что дети Fragments должны управляться FragmentManager для ViewPagerFragment, а не FragmentManager из Activity.

Я понимаю, в то время, ответы выше былиобойти ограничения 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;
}
19 голосов
/ 28 апреля 2012

Я использовал Handler.post() для установки адаптера вне исходной транзакции фрагмента:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mViewPager.setAdapter(mAdapter);     
        }
    });        
}
0 голосов
/ 30 ноября 2018

Вместо установки адаптера в AsyncTask или любом обработчике просто напишите эту часть кода в onResume () section

Делая так, вы будете:

  • Пусть переход основного фрагмента завершен.
  • Отображение дочернего фрагмента без использования каких-либо фоновых задач.

Код 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());
0 голосов
/ 15 марта 2017

Согласно документации: https://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager()

Если вы хотите управлять вложенным фрагментом, вы должны использовать getChildFragmentManager вместо getFragmentManager.

0 голосов
/ 14 августа 2013

Используя этот блестящий пост Томаса Амсслера: http://tamsler.blogspot.nl/2011/11/android-viewpager-and-fragments-part-ii.html

Iv'e создал адаптер, который можно использовать для создания всех типов фрагментов. Сам адаптер выглядит так:

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);
0 голосов
/ 08 июля 2013

После загрузки родительского фрагмента меня встречает исключение IllegalStateException с сообщением: java.lang.IllegalStateException: рекурсивная запись в executePendingTransactions.

Некоторые исследования привели меня к выводу, что системане может отобразить фрагменты внутри другого фрагмента, ОДНАКО, кажется, есть некоторые признаки того, что это можно сделать именно с помощью ViewPager (ошибка в ViewPager при использовании его с другим фрагментом).

явно установите Id для 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);
      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;
    }

Вы можете использовать id в своих ресурсах xml вместо магического числа 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

0 голосов
/ 04 апреля 2013

Я столкнулся с этой проблемой при попытке инициализации адаптера viewPager на TabIndicator в ViewCreated. Прочитав об этом в Интернете, я понял, что это может быть только потому, что фрагмент еще не был добавлен в действие. Поэтому я переместил код инициализации в onStart () фрагмента, это должно решить вашу проблему. Но не для меня, я все еще получал ту же проблему снова.

Но это потому, что в моем коде я пытался обойти обычное асинхронное выполнение отложенной задачи путем явного вызова executePendingTransactions () после блокировки, что все работало хорошо

...