android - studio - IllegalArgumentException: No se encontró una vista para la identificación para el fragmento-ViewPager en ViewPager
viewpager android github (8)
Actualizar:
He leído el código fuente del FragmentManager y finalmente obtuve la verdadera razón: esta excepción ocurre cuando los fragmentos desean adjuntarse al viewpager antes de que el viewpager se adjunte a su padre. En otras palabras, antes de que el método getView () regrese, los fragmentos se inflan. Luego se llama al método findViewById () del contenedor del ViewPager, pero el ViewPager se encuentra en un estado separado aún, por lo que se encuentra el valor nulo y se lanza la excepción IllegalArgumentException.
La solución es crear un ViewPager personalizado y establecer el adaptador de forma lenta:
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);
}
}
Y en el método getView (), use storeAdapter () en lugar de setAdapter.
Las siguientes afirmaciones no son correctas. Las palabras de arriba son la razón real.
Finalmente tengo la respuesta. Está formado por dos partes.
En el ViewPager primario, utilicé un
FragmentPagerAdapter
para contener fragmentos, pero ahora uso unFragmentStatePagerAdapter
lugar. La diferencia entre estos dos se puede encontrar aquí: Diferencia entre FragmentPagerAdapter y FragmentStatePagerAdapter .
En pocas palabras, FragmentPagerAdapter almacenará más información cuando se detiene un fragmento. En esta situación, el primer fragmento en el ViewPager primario se detiene pero no se destruye mientras se destruyen las vistas en este fragmento. Después de reanudar, el fragmento intenta volver a inflar todas las vistas. Pero antes degetView()
métodogetView()
y recrear el sub-ViewPager, el FragmentManager secundario intenta encontrar el sub-ViewPager para contener los fragmentos previamente almacenados. Por lo tanto, se produce la "excepción java.lang.IllegalArgumentException: no se ha encontrado una vista para id".Después de reemplazar el FragmentPagerAdapter con FragmentStatePagerAdapter, aparece otro problema. falta el sub-viewpager cuando el fragmento principal (el primer fragmento en el viewpager principal) fue detenido, destruido y reanudado. Esto sucede cuando se elige el primer fragmento, poco después se elige el tercer fragmento y, por último, se vuelve a elegir el primer fragmento.
Creo que este es un error de Android SDK. Inspirado here y here , utilizo algunos métodos difíciles para resolver el problema. El punto es que, cuando se destruye un fragmento principal, el miembro de campo --- mChildFragmentManager "termina con un estado interno roto" y no se elimina por completo. Cuando se vuelve a crear el fragmento principal, mChildFragmentManager no es nulo, pero los fragmentos secundarios ya se destruyeron después de que se destruyó el fragmento principal, que fue administrado por mChildFragmentManager. Por lo tanto, el sub ViewPager muestra una vista vacía en la pantalla, que responde a un fragmento falso que en realidad no existe. Lo curioso es que, después de deslizar a la derecha en el sub ViewPager varias veces, los sub-fragmentos y las vistas vuelven a aparecer.
Aquí están los códigos:
Adaptador para padres:
@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;
}
El método clave:
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);
}
}
He encontrado el problema que me preocupa por días.
Hay un ViewPager
en la actividad principal que contiene 3 Fragment
como fragmentos de pestañas. En el primer fragmento hay un ListView
que contiene algunas vistas, y que es el más importante, otro ViewPager
. Quiero mantener algunas fotos en el sub ViewPager
, y usar algunos fragmentos más aquí.
Ahora está el problema:
Cuando el primer Fragment
se detiene (el tercer fragmento en el ViewPager
primario se ve en la pantalla) y se reanuda (el usuario cambia al segundo fragmento), la aplicación falla y el depurador dice:
java.lang.IllegalArgumentException: No view found for id 0x7f05008b (com.example.viewpager:id/sub_viewpager) for fragment ScreenSlidePageFragment
Ya he usado el getChildFragmentManager()
ya que esta es una situación de fragmentos anidados .
Aquí está el código clave del adaptador de lista correspondiente al primer fragmento en el ViewPager principal:
@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;
}
Aquí está el código de 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;
}
}
}
Aquí está la lista detallada de errores:
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
Aunque la solución de Xieyi funciona perfectamente bien, se basa en hacer una reflexión, lo que no es realmente algo bueno.
Como respuesta alternativa, logré eliminar los fragmentos secundarios utilizando una transacción de fragmentos, lo que creo que debería ser el enfoque preferido aquí.
Primero, registre una devolución de llamada del ciclo de vida del fragmento al administrador de fragmentos secundarios para que podamos mantener la referencia a los fragmentos secundarios.
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);
A continuación, solo necesitamos iterar sobre la lista de fragmentos para eliminarlos del administrador de fragmentos secundarios, en 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);
}
Eso es.
Cuando tenga una Actividad y un Fragmento que estaría contenido por la Actividad, tendrá layout-xml con una identificación donde se colocará el fragmento.
Si el ID no se encuentra en la Actividad, obtendrá esta excepción.
Ejemplo:
getSupportFragmentManager().beginTransaction()
.add(R.id.banner_container, bannerFragment)
.commit();
si R.id.banner_container no es un ID en el Activity-layout-xml obtendrá la excepción.
Se encontró con el mismo problema al usar ViewPagers anidados. Solucioné el problema reemplazando ambos FragmentPagerAdapter s con FragmentStatePagerAdapter s.
Si su ViewPager
ha estado en el Fragment
, utilice no getFragmentManager
sino getChildFragmentManager
.
Tuve el mismo problema antes, e intenté la respuesta aceptada pero no funcionó para mí. Después de más investigación encontré mi solución.
Esto esta en mi fragmento
CustomViewPager customViewPager = (customViewPager) View.inflate(parentActivity, R.layout.custom_viewpager, null);
Y este es mi trazado.
<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"/>
Tuve un problema similar. Tengo un fragmento (HomeFragment) con un ListView y un ViewPager que tiene algunos fragmentos dentro, todo funciona bien cuando el fragmento comienza, cuando hago clic en alguna fila en el ListView, me llevará a otro fragmento, pero cuando quiero volver al fragmento anterior (HomeFragment) Me aparece el mensaje No se ha encontrado ninguna vista para el error de identificación (tengo addToBackStack el fragmento).
La solución fue verificar en onCreateView de HomeFragment si ya se habían creado View y otros componentes, como:
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);
//...
}
}
Con esta verificación, cuando vuelve al fragmento anterior, todo funciona bien, ya se crearon la Vista, el buscapersonas, la lista, etc., por lo que no es necesario volver a crearlo. =)
Xieyi hizo un buen trabajo al explicar el razonamiento detrás de la excepción.
Aquí está la solución que encontré:
@Override
public void onPause() {
super.onPause();
for ( Fragment f : getChildFragmentManager().getFragments() ) {
if ( f instanceof MyFragmentType ) {
getChildFragmentManager().beginTransaction().remove( f ).commit();
}
}
}
Estaba recibiendo la excepción cada vez que fui a un nuevo fragmento y volvía. Asegúrese de eliminar los fragmentos en su método de "separación", luego puede cargarlos y volver a cargar sus vistas cuando vuelva al fragmento.