2012-02-25 15 views
10

Estoy tratando de mostrar un MapView dentro de un fragmento (usando la biblioteca de compatibilidad pirateada). A continuación se ha trabajado bien en el pasado:MapView inside Fragmento - el hijo especificado ya tiene un padre

  • del fragmento de onCreateView() simplemente devuelve una nueva del fragmento onActivityCreated()FrameLayout
  • obtiene el MapView del Acitivity y lo añade a su jerarquía de vistas
  • onDestroyView() elimina la MapView de su ver jerarquía

Ahora me gustaría que el fragmento use un diseño definido en xml para que pueda tener algunas otras cosas de UI. Poner el elemento MapView en el archivo de diseño se estrella siempre, por lo que estoy haciendo 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 sostiene el actual MapView, y el fragmento de llamadas getMapView(), por lo no corro en el "no puede tener más de un MapView" cuestión:

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, esto debería funcionar de la misma manera como el método de new FrameLayout primero descrito. Sin embargo, me sale 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 la del MapView desde su padre antes de volver de getMapView(), y que vuelve a colgarse. Realmente no entiendo por qué este enfoque no funciona, cualquier ayuda sería apreciada.

Respuesta

15

No estoy seguro de si este es el mismo problema que tenía, pero resulta que inflar está bien, siempre y cuando no se adhiera a la raíz.

Por ejemplo, tiene que 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 añadir que la última false argumento para la llamada inflate(), por lo que recibirá el IllegalStateException.

Creo que lo que está sucediendo es que sin el argumento adicional false, su árbol de vista inflado se adjunta a la vista raíz (container) y luego cuando se inicia la actividad, el sistema intenta agregar el árbol de vista a la raíz otra vez . De ahí el error.

+1

Ha pasado un tiempo desde que volví a visitar ese proyecto, pero los fragmentos eran bastante nuevos para mí en ese momento, por lo que sospecho que esta es realmente la respuesta correcta. – Karakuri

+1

Gracias por esto, no se dio cuenta de lo que estaba pasando hasta que mostró el último argumento :) – lfxgroove

+0

Buena llamada, gracias por eso. :-) –

0

[ACTUALIZACIÓN] escribí una pequeña biblioteca, maceración todas estas soluciones LocalActivityManager juntos: https://github.com/coreform/android-tandemactivities

[antiguo puesto] Usted debe

mapContainer.removeView(mapView); 

antes

mapContainer.addView(mapView); 

a 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); 
} 
+0

Lo he intentado también y no funciona. Aún da el mismo error exacto. – Karakuri

+0

También probé variaciones en '((ViewGroup) mapView.getParent()). RemoveView (mapView)', y nada de eso tampoco funciona. – Karakuri

+0

intente descubrir a qué padre su MapView ya está conectado. si es el padre al que intenta agregarlo, entonces se agrega para que pueda manejar el caso con lógica o una instrucción try/catch. Es la solución que publiqué en los comentarios sobre este hilo: http://nextlevelandroid.com/?p=114 (comentarios de lindo) funciona y no requiere cada FragmentActivity friggen para extender MapActivity! – straya

1

Después de probar una multitud de cosas, terminé haciendo esto:

La vista raíz volví en onCreateView() se crea programáticamente, no inflado. Sin embargo, inflar otras vistas y agregarlas como hijos a la vista raíz creada mediante programación no parece causar ningún problema.

Sombrero para cualquiera que sepa qué hay detrás de este extraño comportamiento. Espero que esto pueda ser útil para otros.

EDITAR

Ha sido un tiempo desde que volví a visitar este tema, pero por lo que recuerdo, esto no haría trabajo para mí ...

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) { 
    return inflater.inflate(R.layout.some_layout, container, false); 
} 

... mientras que este sería trabajo 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() obtendría un MapView de la actividad y lo agregaría como hijo de mapContainer. Si quería otras vistas (como quizás un encabezado sobre el MapView), podría inflarlas por separado y agregarlas 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; 
} 
+0

¿Podría publicar un ejemplo, por favor? –

0

Esto es lo que funcionó para mí ... Cada vez que cambié fragmentos hice

if (mapContainer.getChildAt(0) != null){ 
     mapContainer.removeViewAt(0); 
} 

No puse que en mi métodos onPause (OnDestroy)() o. Lo hice cada vez que cambiaba fragmentos. Por alguna razón, mi fragmento no llamaba a Pausa().

En mi método onResume() lo hice

mapContainer.addView(mapView); 

y todo funciona para mí. No más IllegalStateExceptions.

Cuestiones relacionadas