MapView inside Fragmento – el niño especificado ya tiene un padre
Estoy tratando de mostrar un MapView dentro de un fragmento (utilizando la biblioteca de compatibilidad hackeado). Lo siguiente ha funcionado bien en el pasado:
- Fragmento
onCreateView()
simplemente devuelve un nuevoFrameLayout
-
onActivityCreated()
del fragmento obtiene el MapView de Acitivity y lo añade a su jerarquía de vista -
onDestroyView()
elimina el MapView de su jerarquía de vistas
Ahora me gustaría que el fragmento de utilizar un diseño definido en xml para que pueda tener algunas otras cosas UI. Colocar el elemento MapView
en el archivo de diseño siempre se bloquea, así que lo hago de esta manera:
- fragmento de llamada dentro de la clase AppWidgetProvider
- Viewpageindicator dentro del fragmento
- Detección de Android en el fragmento táctil dentro
- La barra de acción se mueve hacia abajo mientras se reemplaza con fragmento
- Android Fragment onCreateView después de onDestroy no se vuelve a llamar
Map_screen_fragment.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <FrameLayout android:id="@+id/map_container" android:layout_width="fill_parent" android:layout_height="fill_parent" > </FrameLayout> </LinearLayout>
Mi MapScreenActivity
tiene el MapView
real, y el fragmento llama a getMapView()
, por lo que no se ejecuta en el problema "no se puede tener más de un MapView":
MapScreenActivity.java
public class MapScreenActivity extends FragmentActivity { protected Fragment fragment; protected MapView mapView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.single_pane_empty); if (savedInstanceState == null) { fragment = new MapScreenFragment(); getSupportFragmentManager().beginTransaction().add(R.id.root_container, fragment) .commit(); } } public MapView getMapView() { if (mapView == null) { mapView = new MapView(this, getResources().getString(R.string.maps_api_key)); } return mapView; } }
MapScreenFragment.java
public class MapScreenFragment extends Fragment { protected ViewGroup mapContainer; protected MapView mapView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle args) { View root = inflater.inflate(R.layout.map_screen_fragment, container); mapContainer = (ViewGroup) root.findViewById(R.id.map_container); return root; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mapView = ((MapScreenActivity) getActivity()).getMapView(); mapView.setClickable(true); mapView.setBuiltInZoomControls(true); mapContainer.addView(mapView); } @Override public void onDestroyView() { super.onDestroyView(); mapContainer.removeView(mapView); } }
En teoría, todo debería funcionar de la misma manera que el new FrameLayout
método new FrameLayout
descrito en primer lugar. Sin embargo, tengo esto cada vez:
02-24 18:01:28.139: E/AndroidRuntime(502): FATAL EXCEPTION: main 02-24 18:01:28.139: E/AndroidRuntime(502): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.mapfragment/com.example.mapfragment.MapScreenActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1647) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.ActivityThread.access$1500(ActivityThread.java:117) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.os.Handler.dispatchMessage(Handler.java:99) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.os.Looper.loop(Looper.java:130) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.ActivityThread.main(ActivityThread.java:3683) 02-24 18:01:28.139: E/AndroidRuntime(502): at java.lang.reflect.Method.invokeNative(Native Method) 02-24 18:01:28.139: E/AndroidRuntime(502): at java.lang.reflect.Method.invoke(Method.java:507) 02-24 18:01:28.139: E/AndroidRuntime(502): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839) 02-24 18:01:28.139: E/AndroidRuntime(502): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597) 02-24 18:01:28.139: E/AndroidRuntime(502): at dalvik.system.NativeStart.main(Native Method) 02-24 18:01:28.139: E/AndroidRuntime(502): Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. 02-24 18:01:28.139: E/AndroidRuntime(502): at android.view.ViewGroup.addViewInner(ViewGroup.java:1976) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.view.ViewGroup.addView(ViewGroup.java:1871) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.view.ViewGroup.addView(ViewGroup.java:1828) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.view.ViewGroup.addView(ViewGroup.java:1808) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.support.v4.app.NoSaveStateFrameLayout.wrap(Unknown Source) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.support.v4.app.FragmentManagerImpl.moveToState(Unknown Source) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.support.v4.app.FragmentManagerImpl.moveToState(Unknown Source) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.support.v4.app.BackStackRecord.run(Unknown Source) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.support.v4.app.FragmentManagerImpl.execPendingActions(Unknown Source) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.support.v4.app.FragmentActivity.onStart(Unknown Source) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.Activity.performStart(Activity.java:3791) 02-24 18:01:28.139: E/AndroidRuntime(502): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1620) 02-24 18:01:28.139: E/AndroidRuntime(502): ... 11 more
He intentado quitar el MapView
de su padre antes de volver de getMapView()
, y que todavía se estrella. Realmente no entiendo por qué este enfoque no funciona, cualquier ayuda en todo sería apreciado.
- Barra de Acción Sherlock SearchView no se expande al hacer clic
- OnLoadFinished no se llama después de regresar de una pulsación de botón HOME
- Android throw InflateException binario línea de archivo xml: Error al inflar fragmento de clase
- Fragmento crear / restaurar una vista duplicada cuando vuelva a conectar
- Reutilizando fragmentos en un fragmento
- ¿Puedo usar CollapsingToolbarLayout en un fragmento del cajón de navegación?
- Cómo iniciarActivityForResult de Adapter para obtener el resultado de nuevo a Fragment
- App se está estrellando en la parte posterior presionado cuando el uso de mapa dentro de fragmento en android?
No estoy seguro de si este es el mismo problema que estaba teniendo, pero resulta que el inflado está bien, siempre y cuando no se adhieren a la raíz.
Por ejemplo, necesitas hacer algo como esto:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.id.my_layout, container, false); }
Si no agrega ese último argumento false
a la llamada inflate inflate()
, obtendrá la IllegalStateException.
Creo que lo que está sucediendo es que sin el argumento extra false
, el árbol de vista inflada se agrega a la vista raíz ( container
) y cuando se inicia la Actividad, el sistema intenta agregar el árbol de vistas a la raíz de nuevo. De ahí el error.
Después de probar una multitud de cosas, terminé haciendo esto:
La vista raíz que regresé en onCreateView()
se crea mediante programación, no inflado. Sin embargo, inflar otras vistas y agregarlas como niños a la vista raíz creada mediante programación no parece causar ningún problema.
El sombrero está apagado a cualquier persona hacia fuera allí quién puede calcular hacia fuera cuál es detrás de este comportamiento impar. Espero que esto pueda ser útil para otros.
EDITAR
Ha sido un tiempo desde que volví a este tema, pero como recuerdo, esto no funcionaría para mí …
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) { return inflater.inflate(R.layout.some_layout, container, false); }
… mientras que esto funcionaría para mí …
private ViewGroup mapContainer; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) { mapContainer = new LinearLayout(getActivity()); return mapContainer; }
… y más tarde en onActivityCreated()
Me gustaría obtener un MapView de la actividad y añadirlo como un niño de mapContainer. Si quería otras vistas (como quizás una cabecera por encima de la MapView
), podría inflarse por separado y agregarlos a mapContainer, como muestra este fragmento de código.
private ViewGroup mapContainer; public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) { mapContainer = new LinearLayout(getActivity()); mapContainer.setOrientation(LinearLayout.VERTICAL); View headerView = inflater.inflate(R.layout.some_layout, mapContainer, false); mapContainer.addView(headerView); return mapContainer; }
Esto es lo que funcionó para mí … Siempre que cambié fragmentos que hice
if (mapContainer.getChildAt(0) != null){ mapContainer.removeViewAt(0); }
No lo puse en mis métodos onDestroy () o onPause (). Lo hice cuando cambiaba fragmentos. Por alguna razón mi fragmento no estaba llamando onPause ().
En mi método onResume () lo hice
mapContainer.addView(mapView);
Y todo funciona para mí. No más IllegalStateExceptions.
[UPDATE] Escribí una pequeña biblioteca, aplastando todas estas soluciones LocalActivityManager juntas: https://github.com/coreform/android-tandemactivities
[Old post] Usted debe
mapContainer.removeView(mapView);
Antes que tú
mapContainer.addView(mapView);
Para evitar tal excepción.
… como en literalmente la línea anterior.
OK, seguimiento después de los comentarios:
Prueba esto:
if(mapView.getParent() != null) { mapContainer.addView(mapView); }
- ValueEventListener vs ChildEventListener para RecyclerView en Android
- ¿Cómo puedo hacer una aplicación Java utilizando la API Monkeyrunner?