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 nuevo FrameLayout
  • 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:

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.

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); } 
  • Cómo hacer el ancho de DialogFragment a Fill_Parent
  • Fragmenttabhost rendimiento es lento?
  • La etiqueta de meta-datos no tiene valor correcto
  • Métodos de fragmento: attach (), detach (), remove (), replace (), popBackStack ()
  • Listview conjunto adaptador fragmento nullpointerexception
  • Android Google Maps en Fragmento
  • Multi-Pane fragmentos dentro de viewpager
  • OnItemClickListener en un ListView dentro de un fragmento no funciona
  • Android.support.v4.app.FragmentManager O ¿android.app.FragmentManager?
  • PopBackStack hace que llamar a oncreateView del fragmento una y otra vez
  • FragmentTransaction no hacer nada
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.