it-swarm.com.ru

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

У меня есть фрагмент, который содержит 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);
    }
}
54
howettl

используйте 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);
        }
}
60
Yashwanth Kumar

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

PS. Извините за использование ответа вместо комментария, но моя репутация слишком низкая.

37
Leszek

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

Я понимаю, что в то время, ответы выше должны были обойти ограничения 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;
}
24
Steven Byle

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

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

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

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

0
Sanket

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

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

0
Salvatore Cozzubo

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

Некоторые исследования привели меня к выводу, что система не может отображать фрагменты внутри другого фрагмента, ОДНАКО, кажется, есть некоторые признаки того, что это можно сделать именно с помощью 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);
      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
Kugyo

Вместо установки адаптера в 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
Karan

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