Подтвердить что ты не робот

Отображать фрагмент просмотра в фрагменте

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

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

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

На самом деле, если я добавлю кнопку в родительский фрагмент, который вызывает mPager.setAdapter(mAdapter) на моем ViewPager при нажатии, 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);
    }
}
4b9b3361

Ответ 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);
        }
}

Ответ 2

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

Ответ 3

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

В то время я понял, что ответы, приведенные выше, заключались в том, чтобы обойти ограничения Fragments неспособности иметь детей Fragments. Однако теперь, когда в последней библиотеке поддержки есть поддержка Fragment nesting, вам больше не нужно взломать настройку адаптера для вашего 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;
}

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

При загрузке родительского фрагмента мне встречается исключение 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

Ответ 7

Используя этот блестящий пост Томаса Амсслера: 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);

Ответ 9

Вместо того, чтобы устанавливать адаптер в 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());