studio pin mapview google example ejemplo activity android android-fragments android-mapview android-maps-v2 fragment-lifecycle

android - pin - Restaurar el estado de MapView en rotar y volver



marker android map (3)

Fondo

Tengo una aplicación más grande en la que tuve / tuve varios problemas con la nueva API de Google Maps. Traté de describirlo en una pregunta diferente, pero como parece demasiado complejo, decidí comenzar un nuevo proyecto, lo más simple posible y tratar de reproducir los problemas. Asi que aqui esta.

La situación

Estoy usando Fragments y quiero poner MapView adentro. No quiero usar MapFragment . El proyecto de muestra que preparé puede no ser muy bonito, pero traté de hacerlo lo más simple posible y tenía que contener algunos elementos (nuevamente simplificados) de la aplicación original. Tengo una Activity y mi Fragment personalizado con MapView en ella, agregado programáticamente. El Map contiene algunos puntos / Markers . Después de hacer clic en un Marker se muestra la InfoWindow y al hacer clic en ella se muestra el siguiente Fragment (con la función de replace() ) en el contenido.

Los problemas

Hay dos problemas que tengo:

  1. Cuando se muestra el Map con Markers , la rotación de la pantalla causa que la Class not found when unmarshalling error con mi clase personalizada de MyMapPoint ; no tengo ni idea de por qué y qué significa.

  2. Hago clic en el Marker y luego en la InfoWindow . Después de esto, presiono el botón de retroceso de hardware. Ahora puedo ver el Map pero sin Markers y centrado en 0,0 puntos.

El código

Actividad principal

public class MainActivity extends FragmentActivity { private ArrayList<MyMapPoint> mPoints = new ArrayList<MyMapPoint>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { mPoints.add(new MyMapPoint(1, new LatLng(20, 10), "test point", "description", null)); mPoints.add(new MyMapPoint(2, new LatLng(10, 20), "test point 2", "second description", null)); Fragment fragment = MyMapFragment.newInstance(mPoints); getSupportFragmentManager().beginTransaction() .add(R.id.contentPane, fragment).commit(); } } }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/contentPane" android:layout_width="fill_parent" android:layout_height="fill_parent" />

map_fragment.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" > <com.google.android.gms.maps.MapView xmlns:map="http://schemas.android.com/apk/res-auto" android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>

MyMapFragment

public class MyMapFragment extends Fragment implements OnInfoWindowClickListener { public static final String KEY_POINTS = "points"; private MapView mMapView; private GoogleMap mMap; private HashMap<MyMapPoint, Marker> mPoints = new HashMap<MyMapPoint, Marker>(); public static MyMapFragment newInstance(ArrayList<MyMapPoint> points) { MyMapFragment fragment = new MyMapFragment(); Bundle args = new Bundle(); args.putParcelableArrayList(KEY_POINTS, points); fragment.setArguments(args); return fragment; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mMapView.onSaveInstanceState(outState); MyMapPoint[] points = mPoints.keySet().toArray( new MyMapPoint[mPoints.size()]); outState.putParcelableArray(KEY_POINTS, points); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { Bundle extras = getArguments(); if ((extras != null) && extras.containsKey(KEY_POINTS)) { for (Parcelable pointP : extras.getParcelableArrayList(KEY_POINTS)) { mPoints.put((MyMapPoint) pointP, null); } } } else { MyMapPoint[] points = (MyMapPoint[]) savedInstanceState .getParcelableArray(KEY_POINTS); for (MyMapPoint point : points) { mPoints.put(point, null); } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View layout = inflater.inflate(R.layout.map_fragment, container, false); mMapView = (MapView) layout.findViewById(R.id.map); return layout; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mMapView.onCreate(savedInstanceState); setUpMapIfNeeded(); addMapPoints(); } @Override public void onPause() { mMapView.onPause(); super.onPause(); } @Override public void onResume() { super.onResume(); setUpMapIfNeeded(); mMapView.onResume(); } @Override public void onDestroy() { mMapView.onDestroy(); super.onDestroy(); } public void onLowMemory() { super.onLowMemory(); mMapView.onLowMemory(); }; private void setUpMapIfNeeded() { if (mMap == null) { mMap = ((MapView) getView().findViewById(R.id.map)).getMap(); if (mMap != null) { setUpMap(); } } } private void setUpMap() { mMap.setOnInfoWindowClickListener(this); addMapPoints(); } private void addMapPoints() { if (mMap != null) { HashMap<MyMapPoint, Marker> toAdd = new HashMap<MyMapPoint, Marker>(); for (Entry<MyMapPoint, Marker> entry : mPoints.entrySet()) { Marker marker = entry.getValue(); if (marker == null) { MyMapPoint point = entry.getKey(); marker = mMap.addMarker(point.getMarkerOptions()); toAdd.put(point, marker); } } mPoints.putAll(toAdd); } } @Override public void onInfoWindowClick(Marker marker) { Fragment fragment = DetailsFragment.newInstance(); getActivity().getSupportFragmentManager().beginTransaction() .replace(R.id.contentPane, fragment) .addToBackStack(null).commit(); } public static class MyMapPoint implements Parcelable { private static final int CONTENTS_DESCR = 1; public int objectId; public LatLng latLng; public String title; public String snippet; public MyMapPoint(int oId, LatLng point, String infoTitle, String infoSnippet, String infoImageUrl) { objectId = oId; latLng = point; title = infoTitle; snippet = infoSnippet; } public MyMapPoint(Parcel in) { objectId = in.readInt(); latLng = in.readParcelable(LatLng.class.getClassLoader()); title = in.readString(); snippet = in.readString(); } public MarkerOptions getMarkerOptions() { return new MarkerOptions().position(latLng) .title(title).snippet(snippet); } @Override public int describeContents() { return CONTENTS_DESCR; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(objectId); dest.writeParcelable(latLng, 0); dest.writeString(title); dest.writeString(snippet); } public static final Parcelable.Creator<MyMapPoint> CREATOR = new Parcelable.Creator<MyMapPoint>() { public MyMapPoint createFromParcel(Parcel in) { return new MyMapPoint(in); } public MyMapPoint[] newArray(int size) { return new MyMapPoint[size]; } }; } }

Si necesita echar un vistazo a cualquier otro archivo, hágamelo saber. Here puede encontrar un proyecto completo, solo tiene que poner su propia clave de la API de Maps en el archivo AndroidManifest.xml .

EDITAR

Logré hacer el ejemplo aún más simple y actualicé el código anterior.


Aquí está mi solución al primer problema:

Parece que Map está intentando separar todo el paquete, no solo su propia información cuando llamo a mMap.onCreate(savedInstanceState) y tiene un problema si uso mi clase Parcelable personalizada. La solución que funcionó para mí fue eliminar mis extras de savedInstanceState tan pronto como los usé, antes de llamar a Map onCreate() . Lo hago con savedInstanceState.remove(MY_KEY) . Otra cosa que tuve que hacer fue llamar a mMap.onSaveInstanceState() antes de agregar mi propia información a outState en Fragment''s onSaveInstanceState(Bundle outState) .

Y así es como manejé el segundo:

Simplifiqué el proyecto de ejemplo a fondo. Estaba agregando Markers crudos al mapa y si reemplazo el Fragment con mapa con otro, luego de hacer clic en "volver" todavía obtengo el mapa "anulado". Entonces hice dos cosas:

  1. guardando CameraPosition en la función onPause() para restaurarlo en onResume()
  2. estableciendo mMap en null en onPause() así que cuando el Fragment vuelve, los Markers son agregados nuevamente por la función addMapPoints() (tuve que cambiarlo un poco ya que estaba guardando y comprobando los identificadores de Markers ).

Aquí hay ejemplos de código:

private CameraPosition cp;

...

public void onPause() { mMapView.onPause(); super.onPause(); cp = mMap.getCameraPosition(); mMap = null; }

...

public void onResume() { super.onResume(); setUpMapIfNeeded(); mMapView.onResume(); if (cp != null) { mMap.moveCamera(CameraUpdateFactory.newCameraPosition(cp)); cp = null; } }

Y para actualizar la posición de la cámara en onResume() tuve que inicializar manualmente los mapas. Lo hice en setUpMap() :

private void setUpMap() { try { MapsInitializer.initialize(getActivity()); } catch (GooglePlayServicesNotAvailableException e) { } mMap.setOnInfoWindowClickListener(this); mMap.setOnMapLongClickListener(this); addMapPoints(); }

Me doy cuenta de que esas no son soluciones reales, solo anulaciones, pero es lo mejor que puedo hacer por ahora y el proyecto debe continuar. Si alguien encuentra soluciones más limpias, le estaré agradecido por informarme sobre ellas.


Implementa su fragmento / actividad con GoogleMap.OnCameraChangeListener y reemplaza el método onCameraChange y en usted puede guardar la nueva posición de la cámara, y en el uso de onResume

googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPositionSaved));


Vine aquí en busca de sugerencias sobre onSaveInstance y NullPointerException . He intentado varias soluciones incluyendo la mencionada por @Izydorr pero no he tenido suerte. El problema era con FragmentPagerAdapter , el adaptador de ViewPager que hospedaba mis fragmentos que MapView . Después de cambiarlo a FragmentStatePagerAdapter los NPE se han ido. ¡Uf!