Tengo una aplicación con tres pestañas.
Cada pestaña tiene su propio archivo de diseño .xml. Main.xml tiene su propio fragmento de mapa. Es el que aparece cuando se inicia la aplicación por primera vez.
Todo funciona bien, excepto cuando cambio entre pestañas. Si intento volver a la pestaña de fragmentos de mapa, aparece este error. Cambiar a y entre otras pestañas funciona bien.
¿Qué podría estar mal aquí?
Esta es mi clase principal y mi main.xml, así como una clase relevante que uso (también encontrará el registro de errores en la parte inferior)
clase principal
package com.nfc.demo;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.Toast;
public class NFCDemoActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar
.newTab()
.setText("Map")
.setTabListener(
new TabListener<MapFragment>(this, "map",
MapFragment.class)));
bar.addTab(bar
.newTab()
.setText("Settings")
.setTabListener(
new TabListener<SettingsFragment>(this, "settings",
SettingsFragment.class)));
bar.addTab(bar
.newTab()
.setText("About")
.setTabListener(
new TabListener<AboutFragment>(this, "about",
AboutFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
}
// setContentView(R.layout.main);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public static class TabListener<T extends Fragment> implements
ActionBar.TabListener {
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(Activity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(Activity activity, String tag, Class<T> clz,
Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
// Check to see if we already have a fragment for this tab,
// probably from a previously saved state. If so, deactivate
// it, because our initial state is that a tab isn't shown.
mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
FragmentTransaction ft = mActivity.getFragmentManager()
.beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName(),
mArgs);
ft.add(android.R.id.content, mFragment, mTag);
} else {
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT)
.show();
}
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapFragment"
android:name="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
clase relevante (MapFragment.java)
package com.nfc.demo;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MapFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.main, container, false);
}
public void onDestroy() {
super.onDestroy();
}
}
error
android.view.InflateException: Binary XML file line #7:
Error inflating class fragment
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at com.nfc.demo.MapFragment.onCreateView(MapFragment.java:15)
at android.app.Fragment.performCreateView(Fragment.java:1695)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:885)
at android.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1255)
at android.app.BackStackRecord.run(BackStackRecord.java:672)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:441)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException:
Binary XML file line #7: Duplicate id 0x7f040005, tag null, or
parent id 0xffffffff with another fragment for
com.google.android.gms.maps.MapFragment
at android.app.Activity.onCreateView(Activity.java:4722)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
... 19 more
Respuestas:
La respuesta que Matt sugiere funciona, pero hace que el mapa se vuelva a crear y se vuelva a dibujar, lo que no siempre es deseable. Después de muchas pruebas y errores, encontré una solución que me funciona:
Como buena medida, aquí está "map.xml" (R.layout.map) con R.id.mapFragment (android: id = "@ + id / mapFragment"):
Espero que esto ayude, pero no puedo garantizar que no tenga ningún efecto adverso.
Editar: Hubo algunos efectos adversos, como al salir de la aplicación y volver a iniciarla. Dado que la aplicación no se cierra por completo necesariamente (sino que se pone en suspensión en segundo plano), el código anterior que envié fallará al reiniciar la aplicación. He actualizado el código a algo que funciona para mí, tanto entrando y saliendo del mapa como saliendo y reiniciando la aplicación, no estoy muy contento con el bit try-catch, pero parece funcionar bastante bien.
Al mirar el seguimiento de la pila, se me ocurrió que podría verificar si el fragmento del mapa está en el FragmentManager, sin necesidad del bloque try-catch, código actualizado.Más ediciones: resulta que necesitas ese try-catch después de todo. Simplemente comprobar que el fragmento del mapa no funcionó tan bien después de todo. Blergh
fuente
SupportMapFragment
inonDestroyView
.El problema es que lo que intentas hacer no debe hacerse. No deberías estar inflando fragmentos dentro de otros fragmentos. De la documentación de Android :
Si bien puede realizar la tarea con los hacks presentados aquí, le sugiero que no lo haga. Es imposible estar seguro de que estos hacks manejarán lo que cada nuevo sistema operativo Android hace cuando intentas inflar un diseño para un fragmento que contiene otro fragmento.
La única forma compatible con Android de agregar un fragmento a otro fragmento es a través de una transacción desde el administrador de fragmentos hijo.
Simplemente cambie su diseño XML en un contenedor vacío (agregue una ID si es necesario):
Luego, en el
onViewCreated(View view, @Nullable Bundle savedInstanceState)
método Fragment :fuente
4.3
al usar unSupportMapFragment
definido en XML. La creación dinámica del fragmento y su inyección en una vista de contenedor resolvió el problema. Ver esta respuesta SO .SupportMapFragment.newInstance();
developers.google.com/maps/documentation/android-api/mapTuve el mismo problema y pude resolverlo eliminando manualmente
MapFragment
elonDestroy()
método de laFragment
clase. Aquí hay un código que funciona y hace referencia alMapFragment
by ID en el XML:Si no lo elimina
MapFragment
manualmente, se quedará para que no le cueste muchos recursos recrear / mostrar la vista del mapa nuevamente. Parece que mantener el subyacenteMapView
es excelente para alternar entre pestañas, pero cuando se usa en fragmentos, este comportamiento haceMapView
que se cree un duplicado en cada nuevoMapFragment
con la misma ID. La solución es eliminar manualmenteMapFragment
y, por lo tanto, recrear el mapa subyacente cada vez que se infla el fragmento.También noté esto en otra respuesta [ 1 ].
fuente
Esta es mi respuesta:
1, crea un diseño xml como el siguiente:
2, en la clase Fragmento, agregue un mapa de Google mediante programación.
fuente
mMapFragment.getMap();
vuelvenull
. ¿Alguna idea de por qué?Mi solución:
fuente
Declarar objeto SupportMapFragment globalmente
En el método onCreateView () ponga el código debajo
En onDestroyView () poner debajo del código
En su archivo xml ponga el código debajo
El código anterior resolvió mi problema y funciona bien
fuente
Recomendaría
replace()
más queattach()
/detach()
en su manejo de pestañas.O cambia a
ViewPager
. Aquí hay un proyecto de muestra que muestraViewPager
, con pestañas, alojamiento de 10 mapas.fuente
Otra solución:
Eso es todo, si no es nulo, no es necesario reiniciarlo, eliminarlo del padre es un paso innecesario.
fuente
Hoy he perdido horas para encontrar la razón, afortunadamente este problema no se debe a la implementación de MapFragment, desafortunadamente, esto no funciona porque los fragmentos anidados solo son compatibles a través de la biblioteca de soporte de la rev 11.
Mi implementación tiene una actividad con barra de acción (en modo de pestañas) con dos pestañas (sin visor), una con el mapa y la otra con una lista de entradas. Por supuesto, he sido bastante ingenuo al usar MapFragment dentro de mis fragmentos de tabulación, y listo, la aplicación se bloqueaba cada vez que volvía a map-tab.
(El mismo problema que tendría también en caso de que mi fragmento de pestaña infle cualquier diseño que contenga cualquier otro fragmento).
Una opción es usar MapView (en lugar de MapFragment), aunque con algo de sobrecarga (ver MapView Docs como reemplazo directo en el layout.xml, otra opción es usar la biblioteca de soporte desde la versión 11 pero luego tomar un enfoque programático dado que los fragmentos anidados no son compatibles a través del diseño, o simplemente trabajando programáticamente destruyendo explícitamente el fragmento (como en la respuesta de Matt / Vidar), por cierto: se logra el mismo efecto usando MapView (opción 1).
Pero en realidad, no quería perder el mapa cada vez que tabulaba, es decir, quería mantenerlo en la memoria y limpiar solo cuando se cerraba la actividad, así que decidí simplemente ocultar / mostrar el mapa mientras tabulaba, ver FragmentTransaction / hide
fuente
Para aquellos que todavía se encuentran con este problema, la mejor manera de asegurarse de no obtener este error con un Mapa en una pestaña es hacer que el Fragmento se extienda en
SupportMapFragment
lugar de anidarSupportMapFragment
dentro del Fragmento utilizado para la Pestaña.Acabo de hacer que esto funcione usando a
ViewPager
con aFragmentPagerAdapter
, con SupportMapFragment en la tercera pestaña.Aquí está la estructura general, tenga en cuenta que no hay necesidad de anular el
onCreateView()
método, y no hay necesidad de inflar ningún diseño xml:Resultado:
Aquí está el código de clase completo con el que solía probar, que incluye el Fragmento de marcador de posición utilizado para las dos primeras Pestañas, y el Fragmento de Mapa utilizado para la tercera Pestaña:
fuente
Respeto todas las respuestas, pero encontré esta solución: si n es el número de pestañas, entonces:
Ejemplo: en el caso mencionado:
View pager implementa una cola para que no tenga que dejar que elimine ese fragmento. onCreateView se llama solo una vez.
fuente
Está regresando o inflando el diseño dos veces, solo verifique si solo se infla una vez.
fuente
Los fragmentos anidados no son compatibles actualmente. Pruebe el paquete de soporte, revisión 11 .
fuente
¿Has estado intentando hacer referencia a tu
MapFragment
clase personalizada en el archivo de diseño?fuente
Si usará solo la respuesta de Vidar Wahlberg, obtendrá un error cuando abra otra actividad (por ejemplo) y vuelva al mapa. O en mi caso, abra otra actividad y luego, desde la nueva actividad, vuelva a abrir el mapa (sin usar el botón Atrás). Pero cuando combine la solución Vidar Wahlberg y la solución Matt no tendrá excepciones.
diseño
Fragmento
fuente
Tenía esto en viewPager y el bloqueo se debió a que cualquier fragmento tenía que tener su propia etiqueta, no se permiten etiquetas duplicadas o identificadores para el mismo fragmento.
fuente
Creo que hubo algunos errores en la versión anterior de App-Compat para Fragment infantil. Intenté @Vidar Wahlberg y @ Matt's y no funcionaron para mí. Después de actualizar la biblioteca appcompat, mi código se ejecuta perfectamente sin ningún esfuerzo adicional.
fuente
Lo que debe tener en cuenta aquí es que su aplicación se bloqueará gravemente en cualquiera de los dos casos: -
Aquí hay un fragmento de código de muestra para el uso correcto de MapView
XML
El resultado se ve así: -
Espero que ayude a alguien.
fuente
En esta solución no necesita tomar una variable estática;
fuente
Llego un poco tarde a la fiesta, pero ninguna de estas respuestas me ayudó en mi caso. Yo estaba usando Google Map como SupportMapFragment y PlaceAutocompleteFragment tanto en mi fragmento. Como todas las respuestas señalaron el hecho de que el problema es que SupportMapFragment es el mapa que se debe recrear y volver a dibujar, pero después de excavar descubrí que mi problema era en realidad con PlaceAutocompleteFragment
Así que aquí está la solución de trabajo para aquellos que enfrentan este problema debido a SupportMapFragment y SupportMapFragment
Y en onDestroyView borre SupportMapFragment y SupportMapFragment
fuente
Intente configurar un id (android: id = "@ + id / maps_dialog") para su diseño padre mapView. Funciona para mi.
fuente
Cualquier persona que venga aquí ahora que recibe este tipo de error al abrir uno
Dialog
u otroFragment
con Google PlacesAutocompleteSupportFragment
, pruebe esta frase (no sé qué tan seguro es esto, pero funciona para mí):autocompleteFragment.getFragmentManager().beginTransaction().remove(autocompleteFragment).commit();
antes de descartar / destruir tu fragmento.
fuente
¿Por qué no inserta un mapa usando el objeto MapView en lugar de MapFragment? No estoy seguro de si hay alguna limitación en MapView, aunque lo encontré útil.
fuente