IllegalArgumentException: нет представления для id для фрагмента – ViewPager в ViewPager

Я встретил проблему, которая беспокоила меня в течение нескольких дней.

В основной активности есть ViewPager который содержит 3 Fragment виде фрагментов вкладки. В первом фрагменте есть ListView который содержит некоторые представления, а это самый важный, другой ViewPager . Я хочу провести несколько фотографий в суб ViewPager и использовать здесь несколько фрагментов.

Теперь есть проблема:
Когда первый Fragment остановлен ( третий фрагмент в родительском ViewPager на экране) и возобновляется (пользователь переключается на второй фрагмент), приложение выходит из строя, и отладчик говорит:

 java.lang.IllegalArgumentException: No view found for id 0x7f05008b (com.example.viewpager:id/sub_viewpager) for fragment ScreenSlidePageFragment 

Я уже использовал getChildFragmentManager() поскольку это ситуация с вложенными фрагментами .

Вот ключевой код адаптера списка, соответствующий первому фрагменту родительского ViewPager:

 @Override public View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); switch (type) { case TYPE_BANNER: if (convertView == null) { convertView = mBannerView.getBannerView(parent); } mBannerView.update(convertView); break; case TYPE_ITEM: break; } return convertView; } 

Вот код mBannerView :

 public class BannerView { private static final DisplayImageOptions IMAGE_OPTIONS_SCALE_STRETCHED = new DisplayImageOptions.Builder() .cacheInMemory() .cacheOnDisc() .imageScaleType(ImageScaleType.EXACTLY_STRETCHED) .build(); private FragmentActivity mActivity; private Fragment mFragment; private List<Banner> mBanners; private ScreenSlidePagerAdapter mPagerAdapter; private ViewPager mViewPager; public BannerView(FragmentActivity activity, Fragment fragment) { mActivity = activity; mFragment = fragment; } public void update(View convertView) { mViewPager = (ViewPager) convertView; if (mBanners != null && !mBanners.isEmpty()) { if (mPagerAdapter == null) { mPagerAdapter = new ScreenSlidePagerAdapter(mFragment.getChildFragmentManager()); mViewPager.setAdapter(mPagerAdapter); } } mViewPager.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnBannerClickListener != null) { mOnBannerClickListener.onBannerClick(); } } }); } class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { public ScreenSlidePagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new ScreenSlidePageFragment(mBanners.get(position).getImageUrl()); } @Override public int getCount() { return mBanners == null ? 0 : mBanners.size(); } } class ScreenSlidePageFragment extends Fragment { private String mUrl; ScreenSlidePageFragment(String url) { super(); mUrl = url; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.item_banner, container, false); if (view != null) { ImageView imageView = (ImageView) view.findViewById(R.id.item_banner_image); imageView.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); ImageLoader.getInstance().displayImage(mUrl, imageView, IMAGE_OPTIONS_SCALE_STRETCHED); } return view; } } } 

Вот подробный список ошибок:

 11-10 18:12:19.217 1444-1444/? E/MessageQueue-JNI﹕ java.lang.IllegalArgumentException: No view found for id 0x7f05008b (com.example.viewpager:id/sub_viewpager) for fragment ScreenSlidePageFragment{428d8ea0 #0 id=0x7f05008b} at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:919) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1086) at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1884) at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1514) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947) at android.support.v4.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1280) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:672) at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1467) at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:472) at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:141) at android.support.v4.view.ViewPager.populate(ViewPager.java:1068) at android.support.v4.view.ViewPager.populate(ViewPager.java:914) at android.support.v4.view.ViewPager$3.run(ViewPager.java:244) at android.support.v4.view.ViewPager.completeScroll(ViewPager.java:1761) at android.support.v4.view.ViewPager.onInterceptTouchEvent(ViewPager.java:1896) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1854) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2211) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1912) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2211) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1912) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2211) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1912) at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2228) at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1471) at android.app.Activity.dispatchTouchEvent(Activity.java:2424) at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2176) at android.view.View.dispatchPointerEvent(View.java:7571) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3883) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3778) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3379) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3429) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3398) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3483) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3406) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3540) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3379) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3429) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3398) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3406) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3379) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5419) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5399) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5370) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5493) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:182) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:132) at android.os.Looper.loop(Looper.java:124) at android.app.ActivityThread.main(ActivityThread.java:5289) at java.lang 

Обновить:

Я прочитал исходный код FragmentManager и, наконец, получил истинную причину: это исключение происходит, когда фрагменты хотят быть прикреплены к viewpager до того, как viewpager привязан к его родительскому элементу. Другими словами, перед возвратом метода getView () фрагменты раздуваются. Затем вызывается метод findViewById () контейнера ViewPager, но ViewPager находится в состоянии отсоединения, поэтому нуль обнаружен и вызывается исключение IllegalArgumentException.

Решением является создание настраиваемого ViewPager и ленивый набор адаптера:

 public class BannerViewPager extends ViewPager { PagerAdapter mPagerAdapter; @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mPagerAdapter != null) { super.setAdapter(mPagerAdapter); mPageIndicator.setViewPager(this); } } @Override public void setAdapter(PagerAdapter adapter) { } public void storeAdapter(PagerAdapter pagerAdapter) { mPagerAdapter = pagerAdapter; } public BannerViewPager(Context context) { super(context); } public BannerViewPager(Context context, AttributeSet attrs) { super(context, attrs); } } 

И в методе getView () используйте storeAdapter () вместо setAdapter.

Следующие утверждения неверны. Приведенные выше слова являются фактической причиной.


Наконец у меня есть ответ. Он состоит из двух частей.

  1. В родительском ViewPager я использовал FragmentPagerAdapter для хранения фрагментов, но теперь вместо этого использую FragmentStatePagerAdapter . Разницу между этими двумя можно найти здесь: Разница между FragmentPagerAdapter и FragmentStatePagerAdapter .
    Проще говоря, FragmentPagerAdapter будет хранить больше информации при остановке фрагмента. В этой ситуации первый фрагмент в родительском ViewPager останавливается, но не уничтожается, а виды в этом фрагменте уничтожаются. После возобновления фрагмент попытается повторно раздуть все виды. Но до getView() метода getView() и воссоздания под-ViewPager дочерний FragmentManager пытается найти суб-ViewPager для хранения ранее сохраненных фрагментов. Таким образом, появляется «java.lang.IllegalArgumentException: нет представления, найденного для id».

  2. После того как я заменил FragmentPagerAdapter на FragmentStatePagerAdapter, появится другая проблема. Отсутствует суб-просмотрщик, когда родительский фрагмент (первый фрагмент в родительском viewpager) был остановлен, уничтожен и возобновлен. Это происходит, когда выбирается первый фрагмент, вскоре после этого выбирается третий фрагмент, и, наконец, первый фрагмент повторно выбирается.
    Я думаю, что это ошибка android sdk. Вдохновленный здесь и здесь , я использую некоторые сложные методы для решения проблемы. Дело в том, что когда родительский фрагмент уничтожается, член поля — mChildFragmentManager «заканчивается сломанным внутренним состоянием» и не полностью очищается. Когда родительский фрагмент воссоздается, mChildFragmentManager не является нулевым, но субфрагменты уже уничтожены после уничтожения родительского фрагмента, который управлялся mChildFragmentManager. Таким образом, sub ViewPager отображает пустой экран на экране, который отвечает на фальшивый фрагмент, который на самом деле не существует. Самое смешное, что несколько раз прокручивая правую часть под ViewPager, снова появляются фрагменты и представления.

Вот коды:

Родительский адаптер:

 @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = getBannerView(mParent); } mViewPager = (ViewPager) convertView; if (mBanners != null && !mBanners.isEmpty()) { if (mPagerAdapter == null) { FragmentManager childFM = mFragment.getChildFragmentManager(); removeOldFragment(childFM); mPagerAdapter = new ScreenSlidePagerAdapter(childFM, mBanners); mViewPager.setAdapter(mPagerAdapter); } } return convertView; } 

Ключевой метод:

  private void removeOldFragment(FragmentManager fm) { try { Field added = fm.getClass().getDeclaredField("mAdded"); added.setAccessible(true); added.set(fm, null); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } try { Field active = fm.getClass().getDeclaredField("mActive"); active.setAccessible(true); active.set(fm, null); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } 

Когда у вас есть Activity и Fragment, которые будут содержаться в Activity, у вас будет layout-xml с идентификатором, где должен быть помещен фрагмент.

Если идентификатор не находится в Управлении, вы получите это исключение.

Пример:

 getSupportFragmentManager().beginTransaction() .add(R.id.banner_container, bannerFragment) .commit(); 

Если R.id.banner_container не является идентификатором в Activity-layout-xml, вы получите исключение.

Xieyi проделал хорошую работу по объяснению причин, лежащих в основе исключения.

Вот решение, которое я нашел:

 @Override public void onPause() { super.onPause(); for ( Fragment f : getChildFragmentManager().getFragments() ) { if ( f instanceof MyFragmentType ) { getChildFragmentManager().beginTransaction().remove( f ).commit(); } } } 

Я получал исключение всякий раз, когда возвращался к новому фрагменту и возвращался. Удостоверьтесь, что вы удаляете фрагменты в методе «cleaup», после чего вы можете загружать, когда вы возвращаетесь к фрагменту, можете перезагрузить свои представления.

В этой же проблеме возникла проблема при использовании вложенных ViewPagers. Я исправил проблему, заменив оба FragmentPagerAdapter s на FragmentStatePagerAdapter s.

Хотя решение Xieyi работает отлично, это в значительной степени основано на отражении, что не очень хорошо.

В качестве альтернативного ответа мне удалось удалить дочерние фрагменты, используя фрагментную транзакцию, и я считаю, что это предпочтительный подход.

Сначала зарегистрируйте обратный вызов жизненного цикла фрагмента для диспетчера дочерних фрагментов, чтобы мы могли ссылаться на дочерние фрагменты.

 private List<Fragment> mFragments = new ArrayList<>(); @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); …(DO YOUR STUFF) //register a fragment lifecycle callback getChildFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() { @Override public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) { super.onFragmentCreated(fm, f, savedInstanceState); mFragments.add(f); } @Override public void onFragmentDestroyed(FragmentManager fm, Fragment f) { super.onFragmentDestroyed(fm, f); mFragments.remove(f); } }, false); 

Затем нам нужно только перебрать список фрагментов, чтобы удалить их из диспетчера дочерних фрагментов, внутри onCreateView:

 @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //remove child fragments before inflating view Iterator<Fragment> iterator = mFragments.iterator(); while (iterator.hasNext()) { Fragment fragment = iterator.next(); iterator.remove(); getChildFragmentManager().beginTransaction() .remove(fragment) .commitNow(); } return inflater.inflate(R.layout.your_fragment, container, false); } 

Вот и все.

У меня была аналогичная проблема. У меня есть фрагмент (HomeFragment) со списком ListView и ViewPager, в котором есть фрагменты внутри, все работает отлично, когда начинается фрагмент, когда я нажимаю какую-либо строку в ListView, это приведет меня к другому фрагменту, но когда я захочу Вернитесь к ранее фрагменту (HomeFragment). Я получаю No view, найденный для ошибки id (у меня есть addToBackStack фрагмент).

Решение состояло в том, чтобы проверить в onCreateView HomeFragment, если View и другие компоненты уже были созданы, например:

 View view; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (view == null) { //All the code in the onCreateView view = inflater.inflate(R.layout.item_banner, container, false); //... } } 

С этой проверкой, когда она возвращается к предыдущему фрагменту, все работает отлично, View, пейджер, список и т. Д. Уже созданы, поэтому нет необходимости создавать его снова. знак равно

Раньше у меня была такая же проблема, и я попробовал принятый ответ, но это не сработало для меня. После нескольких исследований я нашел свое решение.

Это в моем фрагменте

 CustomViewPager customViewPager = (customViewPager) View.inflate(parentActivity, R.layout.custom_viewpager, null); 

И это мой макет

 <com.test.package.ui.CustomViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/custom_viewpager" android:layout_width="match_parent" android:layout_height="match_parent"/> 
Intereting Posts
Удалить название на панели инструментов в appcompat-v7 Как обращаться с кнопкой возврата в Search View в android Модернизация пользовательского клиента для проверки подлинности WebTokens Ionic App w / Cordova, как вставить камеру? Что означает «.line» в синтаксисе кода smali? (Android-Smali Code) Почему GetServerAuthCodeResult устарел? Как я могу сделать что-то эквивалентное в установленном приложении? Как включить окно управления версиями в студии Android Как исправить исключение разрешения INJECT_EVENT при отправке прикосновений к тесту ActivityInstrumentationTestCase2 Почему eglMakeCurrent () не работает с EGL_BAD_MATCH? Использование AppBarLayout.Behavior.DragCallback для управления прокруткой разворачивающейся панели инструментов MediaScanner ServiceConnectionLeaked Установить фоновое изображение для всего приложения в android Что я должен использовать для повышения производительности, с девятью патчами или ресурсом xml? Как изменить цвет значка выбранной вкладки TabLayout? Как реализовать панель инструментов Android на панели инструментов