NullPointerException en FrameLayout.onMeasure ()

Editar: He creado un proyecto Github que se bloquea de la misma manera que mi aplicación. Lo puedes encontrar aquí

Edit: Como he depurado esto y he probado varios cambios, me di cuenta de que el código original que publiqué no era realmente relevante, por lo que lo eliminé:

Tengo una Activity que puede conmutar entre 3 Fragment diferentes cuando el usuario empuja uno de 3 IconButton s que he insertado en el ActionBar en un diseño personalizado, como cambiar entre varios modos:

Introduzca aquí la descripción de la imagen

Cuando primero lanzo la aplicación (después de haberla desinstalado), estoy predeterminado en el modo Globo. El modo Globo tiene varios fragmentos en un ViewPager , la mayoría de estos fragmentos son subclases del ViewPager android.support.v4.app.ListFragment . Estoy creando todas las 8 de mis pestañas en el método onCreate de la actividad principal. Esto funciona como se esperaba, y todo se carga correctamente.

Cuando voy al modo "Perfil" (haciendo clic en el icono de la cara), puedo iniciar sesión en la aplicación y ver mi perfil. Esto también funciona como se espera, todo se carga correctamente.

Cuando empujo el APK nuevamente desde Android Studio, ya que ahora estoy conectado (Preferencias guardadas), me prefiero al modo "feed de amigos" (las dos personas que toman las manos). Puedo cambiar de modo al Perfil, y todo funciona como esperaba, pero cuando cambio al modo Globo, se está estrellando en el código de Android:

 08-14 14:43:22.744 28804-29323/com.trover E/AndroidRuntime﹕ FATAL EXCEPTION: main Process: com.trover, PID: 28804 java.lang.NullPointerException at android.widget.FrameLayout.onMeasure(FrameLayout.java:309) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at com.android.internal.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:327) at android.view.View.measure(View.java:16497) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125) at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2291) at android.view.View.measure(View.java:16497) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1912) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1109) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1291) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:996) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5600) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761) at android.view.Choreographer.doCallbacks(Choreographer.java:574) at android.view.Choreographer.doFrame(Choreographer.java:544) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5001) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method) 

En las últimas dos semanas he hecho dos cambios importantes al proyecto:

  1. He eliminado la biblioteca ActionbarSherlock. Originalmente esperaba quitar la biblioteca support.v4 hasta que me di cuenta de que la necesitaba para ViewPager.
  2. Cambié de Eclipse a Android Studio, que incluía mover todos los archivos a la nueva estructura de Android Studio.

Esta mañana volví a instalar Eclipse y fui al punto justo después de quitar ActionbarSherlock, y no pude recrear este accidente.

Cosas que he intentado:

  • Construir manualmente el proyecto con Gradle, e instalarlo manualmente y ejecutarlo. Cuando hago los mismos pasos, se bloquea de la misma manera.
  • Ejecutar esto a través del depurador, y detenerse en el accidente. La línea 309 es la siguiente:

      if (mMeasureAllChildren || child.getVisibility() != GONE) { 

En este punto en el código, child es null , sin embargo, la llamada a getChildCount() en la línea 296 está devolviendo un valor de 2. Todavía no tengo idea de qué vista se está estrellando realmente en este punto , no hay valor definitorio en ninguna De la variable como navegar a través de ellos en el depurador.

  • Al iniciar sesión / salir, se cambian los elementos que se encuentran en el menú de desbordamiento. Cuando haces clic en IconButton s en la parte superior, llamo a setBackgroundDrawable en los IconButtons para establecer el resaltado amarillo (o establecer null si ya no es el botón activo). Cuando comentar tanto el código en onCreateOptionsMenu y el código en setHighlightedIconButton() , el bloqueo ya no se produce
  • Cuando ViewPager todos los fragmentos del ViewPager , el bloqueo ya no se produce
  • Como @kcoppock mencionado a continuación, podría ser que estoy inflando incorrectamente algo en alguna parte. Tras el artículo que vinculó, modifiqué cada llamada en mi aplicación donde llamaba inflater.inflate(R.layout.somelayout, null) lugar de inflater.inflate(R.layout.somelayout, viewGroupContainer, false) , excepto en el caso Donde estoy creando vistas personalizadas en las pestañas (donde no conozco el grupo viewGroup a los padres en). En ese caso, estoy configurando manualmente LayoutParameters.

Estoy completamente en una pérdida en este momento en cuanto a lo que incluso tratar a continuación.

Update: Cambiar la aplicación para que siempre se inicie en el modo Globe evita el bloqueo, por lo que parece ser algo con el cambio al modo Globo si no empezamos con él activo.

Aquí está mi código para construir las pestañas para el viewPager de la actividad principal:

  // We HAVE to read this value out before creating the layout, or onTabSelected will set it back to zero final SharedPreferences prefs = getApplicationContext().getSharedPreferences( Const.Preferences.PREFS_FILE, Context.MODE_PRIVATE); boolean deniedLocationServices = prefs.getBoolean(Const.Preferences.LOCATION_SERVICES_DENIED, false); int previousTab = prefs.getInt(Const.Preferences.PREVIOUS_TAB, 0); setContentView(R.layout.main_browse); mFragmentManager = getSupportFragmentManager(); mActionBar = getActionBar(); mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); // these three lines hide the 'Trover' logo mActionBar.setDisplayHomeAsUpEnabled(false); mActionBar.setDisplayUseLogoEnabled(false); mActionBar.setIcon(new ColorDrawable(android.R.color.black)); mActionBar.setCustomView(R.layout.action_bar_icons); mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME); final int numTabs = 8; TabbedBrowsePagerTab[] tabs = new TabbedBrowsePagerTab[numTabs]; tabs[mAllTabPosition] = buildAllTab(); tabs[mWhatsHotTabPosition] = buildWhatsHotTab(); tabs[mWhatsNewTabPosition] = buildWhatsNewTab(); tabs[mJumpToTabPosition] = buildJumpToTab(); tabs[mFoodTabPosition] = buildFoodTab(); tabs[mOutdoorTabPosition] = buildOutdoorTab(); tabs[mArtTabPosition] = buildArtTab(); tabs[mLatestTabPosition] = buildLatestTab(); mPagerAdapter = new TabbedBrowsePagerAdapter(this, tabs); mViewPager = (ViewPager) findViewById(R.id.pager); mViewPager.setAdapter(mPagerAdapter); mViewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(final int position) { try { mActionBar.setSelectedNavigationItem(position); } catch (final IllegalStateException e) { TroverApplication.logError(TAG, "IllegalArgumentsException setting tab position in PageChangeListener"); } } }); // Build the actual tabs on the actionbar for (int i = 0; i < tabs.length; i++) { mActionBar.addTab(mActionBar.newTab() .setTabListener(this)); LayoutInflater inflater = LayoutInflater.from(this); View customView = inflater.inflate(R.layout.custom_action_bar_tabs, null); customView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); TextView tabCustom = (TextView) customView.findViewById(R.id.custom_action_bar_tab); tabCustom.setText(tabs[i].getTabTitle()); tabCustom.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); tabCustom.setTypeface(TroverApplication.getDefaultFontBold()); mActionBar.getTabAt(i).setCustomView(tabCustom); } // Now set up the button listeners for those icons mActionBar.getCustomView().findViewById(R.id.action_bar_friend_feed_button).setOnClickListener(this); mActionBar.getCustomView().findViewById(R.id.action_bar_me_button).setOnClickListener(this); mActionBar.getCustomView().findViewById(R.id.action_bar_nearby_button).setOnClickListener(this); mViewPager.setCurrentItem(previousTab); TroverLocationManager manger = TroverLocationManager.get(); if (!manger.isNetworkLocationEnabled() && !deniedLocationServices) { showLocationServicesDialog(); } if (AuthManager.get().isAuthenticated()) { mCurrentMode = Mode.NEWS_MODE; // <----- if I change this to GLOBE_MODE it doesn't crash } else { mCurrentMode = Mode.GLOBE_MODE; } validateView(); 

Aquí está el diseño main_browse.xml:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="fill_parent" android:layout_width="fill_parent" > <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/main_camera_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_marginBottom="15dp" android:layout_marginRight="15dp" android:background="@drawable/camera_floating" /> </RelativeLayout> 

Aquí está la función setHighlightedIconButton:

 private void setHighlightedIconButton() { ImageButton button; View iconContainer = mActionBar.getCustomView(); PaintDrawable selectedBackground = new PaintDrawable(getResources().getColor(R.color.trover_selected_icon_background)); button = (ImageButton) iconContainer.findViewById(R.id.action_bar_friend_feed_button); if (mCurrentMode == Mode.NEWS_MODE) { button.setBackgroundDrawable(selectedBackground); button.setClickable(false); } else { button.setBackgroundDrawable(null); button.setClickable(true); } button = (ImageButton) iconContainer.findViewById(R.id.action_bar_nearby_button); if (mCurrentMode == Mode.GLOBE_MODE) { button.setBackgroundDrawable(selectedBackground); button.setClickable(false); } else { button.setBackgroundDrawable(null); button.setClickable(true); } button = (ImageButton) iconContainer.findViewById(R.id.action_bar_me_button); if (mCurrentMode == Mode.PROFILE_MODE) { button.setBackgroundDrawable(selectedBackground); button.setClickable(false); } else { button.setBackgroundDrawable(null); button.setClickable(true); } } 

Y esta es la función validateView () que lo llama:

 /** * Handles transitions between different fragments by checking the current mode and authentication state. * This function can handle being called multiple times, and will always try to do the least work possible * each time. */ private void validateView() { invalidateOptionsMenu(); setHighlightedIconButton(); boolean authenticated = AuthManager.get().isAuthenticated(); switch(mCurrentMode) { case GLOBE_MODE: // Note - we don't record a screen here because the tabs will do that removeMeFragment(); removeNewsFragment(); removeOnboardingFragment(); mViewPager.setVisibility(View.VISIBLE); mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); mPagerAdapter.madeVisible(); break; case NEWS_MODE: if (mPreviousTabPosition == mJumpToTabPosition) { InputMethodManager imm = (InputMethodManager) getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0); } recordScreen(getCurrentModeTrackingString(), this); removeMeFragment(); removeOnboardingFragment(); if (mNewsFeedFragment == null) { mNewsFeedFragment = DiscoveryListFragment.newNewsFeedInstance(); mFragmentManager.beginTransaction().add(android.R.id.content, mNewsFeedFragment).commit(); } mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); mViewPager.setVisibility(View.GONE); break; case PROFILE_MODE: if (mPreviousTabPosition == mJumpToTabPosition) { InputMethodManager imm = (InputMethodManager) getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0); } if (authenticated) { recordScreen(getCurrentModeTrackingString(), this); removeNewsFragment(); removeOnboardingFragment(); if (mProfileFragment == null) { mProfileFragment = UserDetailFragment.newMeInstance(); mFragmentManager.beginTransaction().add(android.R.id.content, mProfileFragment).commit(); } } else { recordScreen(getCurrentModeTrackingString() + "/onboarding", this); removeMeFragment(); removeNewsFragment(); if (mOnboardingFragment == null) { removeOnboardingFragment(); mOnboardingFragment = new OnboardingFragment(); mFragmentManager.beginTransaction().add(android.R.id.content, mOnboardingFragment).commit(); } } mViewPager.setVisibility(View.GONE); break; default: TroverApplication.logError(TAG, "Invalid mode!"); } } 

Aquí está el onCreateOptionsMenu para la Actividad principal:

 public boolean onCreateOptionsMenu(final Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu, menu); if (!AuthManager.get().isAuthenticated()) { MenuItem item = menu.findItem(R.id.menu_notifications); if (item != null) { item.setVisible(false); } item = menu.findItem(R.id.menu_recommended_users); if (item != null) { item.setVisible(false); } } return true; } 

Esta es la parte de la función FrameLayout.onMeasure() que forma parte del código fuente de la API de Android 19 donde se está bloqueando:

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); <-- this is returning 2 final boolean measureMatchParentChildren = MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY || MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY; mMatchParentChildren.clear(); int maxHeight = 0; int maxWidth = 0; int childState = 0; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (mMeasureAllChildren || child.getVisibility() != GONE) { <-- crash happens here measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (measureMatchParentChildren) { if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) { mMatchParentChildren.add(child); } } } } 

 View customView = inflater.inflate(R.layout.custom_action_bar_tabs, null); 

Esta es probablemente la fuente de su problema. Si pasa en null , el inflador no sabe qué tipo de LayoutParams generar (que los genera desde el ViewGroup padre). Creí que esto generó ViewGroup.LayoutParams por defecto, pero tal vez no proporciona ningún LayoutParams en absoluto.

Debe reemplazar esto con:

 View customView = inflater.inflate(R.layout.custom_action_bar_tabs, parent, false); 

Donde parent es el ViewGroup en el que se customView . Si no tiene un padre disponible, puede establecer algunos LayoutParams personalizados manualmente:

 View customView = inflater.inflate(R.layout.custom_action_bar_tabs, null); customView.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); 

Número 2:

Dado que la excepción se eleva durante el diseño, dos llamadas en el método validateView parecen dudosas. Reemplace las dos llamadas de esta manera: mViewPager.setVisibility(View.GONE); Con mViewPager.setVisibility(View.INVISIBLE);

La transición entre ido y visible cuando se introduce el modo de globo está activando la disposición. Pasar de invisible a visible no activará el diseño y eso es una buena suposición para dónde ocurre un NPE.

Actualizar. Basado en su comentario, supongamos que la transición gone -> visible sólo está desencadenando un NPE en un diseño diferente, es decir, en su barra de acción personalizada.

Al descargar la jerarquía de vista de una aplicación de ejemplo (Eclipse> DDMS> Dump Ver Jerarquía para View Automator), encontré algunos FrameLayouts en mi barra de acción, lo cual es interesante. Mi aplicación de ejemplo no está usando su vista personalizada, por supuesto, pero incluso en el icono / parte de inicio de la barra de acción hay un ImageView dentro de un diseño de marco.

Por lo tanto, intente reemplazar este código complicado:

 // these three lines hide the 'Trover' logo mActionBar.setDisplayHomeAsUpEnabled(false); mActionBar.setDisplayUseLogoEnabled(false); mActionBar.setIcon(new ColorDrawable(android.R.color.black)); mActionBar.setCustomView(R.layout.action_bar_icons); mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM|ActionBar.DISPLAY_SHOW_HOME); 

Con este código más simple que debe hacer lo mismo de una manera segura:

 mActionBar.setCustomView(R.layout.action_bar_icons); mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); 

Si no es así, el dumping de su vista real y la investigación de los FrameLayouts en su barra de acción personalizada debe ser útil.

Terminé archivando un error contra AOSP para este:

https://code.google.com/p/android/issues/detail?id=75056

La respuesta (bastante concisa) que llegué allí indicó que en la función ViewPager.onMeasure() intenta ejecutar cualquier transacción de Fragment pendiente. Por desgracia, suena como mi mostrar / ocultar los otros fragmentos están interfiriendo con este proceso y causar la NPE.

La solución que alentaron fue llamar a FragmentManager.executePendingTransactions() al final de mi función validateView() , presumiblemente para finalizar las transacciones de fragmentos que he comprometido para que el viewPager no se vea afectado por ellas.

Usted tiene identificación incorrecta:

 <ProgressBar android:id="@+id/trover_list.loading" ... /> 

No se puede usar el punto . carbonizarse.

Sólo una corazonada pero trate de mover setHighlightedIconButton (); Después de la casilla del conmutador en validateView (). Esto puede forzar a los niños de NEWS_MODE a instanciar esperanzadamente la prevención del puntero nulo en onMeasure cuando se cambia el fondo.

Voy a tomar una puñalada en ella. Una cosa que salta es que el menú de opciones dinámicas se implementa de una manera poco convencional. Debería usar en su lugar:

 public boolean onCreateOptionsMenu(final Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu, menu); return super.onCreateOptionsMenu(menu); } 

Seguido por:

 public boolean onPrepareOptionsMenu(final Menu menu) { boolean auth = AuthManager.get().isAuthenticated(); MenuItem item = menu.findItem(R.id.menu_notifications); if (item != null) { item.setVisible(auth); } item = menu.findItem(R.id.menu_recommended_users); if (item != null) { item.setVisible(auth); } return super.onPrepareOptionsMenu(); } 

Los dos cambios son: (1) usando onPrepareOptionsMenu para cambios dinámicos en el menú, y (2) asegurándose de llamar a la superclase.

Mirando mi propio código veo que he roto la regla (2) antes sin tener un problema, pero cuando el código se está estrellando vale la pena intentarlo.

Para Android 3+, debes llamar a invalidateOptionsMenu() para activar una nueva llamada al método de preparación, pero ya veo que ya está en tu código, por lo que debería estar bien.

  • Multi-Pane fragmentos dentro de viewpager
  • Acceda a la vista de fragmentos de padres de ViewPager en el fragmento secundario de Pager
  • ¿Las pestañas anidadas con el visor de desplazamiento en android?
  • Android Uso de ViewPager desde la biblioteca de soporte técnico
  • No se puede convertir de android.support.v4.app.Fragment a android.app.Fragment
  • Aumentar la duración del desplazamiento de la lectura de viewpager
  • Cómo utilizar tabContentStart para iniciar el contenido desde la mitad de la pantalla del dispositivo
  • Cambiar el estilo de animación de ViewPager
  • Android: intenta invocar el método virtual 'void android.support.v4.app.Fragment.setMenuVisibility (boolean)' en una referencia de objeto nulo
  • Android ViewPager: Actualización de fragmentos fuera de la pantalla pero en caché en ViewPager
  • Soltar sombra para un ViewPager
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.