26

SfondoRipristinare lo stato di MapView sulla rotazione e sulla schiena

Ho un'applicazione più ampio in cui ho avuto/avere diversi problemi con nuova API di Google Maps. Ho provato a descriverlo in a different question ma visto che sembra troppo complesso ho deciso di iniziare un nuovo progetto, il più semplice possibile e provare a riprodurre i problemi. Quindi eccolo qui.

La situazione

sto usando Fragments e vuole mettere MapView all'interno. Non voglio usare MapFragment. Il progetto di esempio che ho preparato potrebbe non essere molto bello, ma ho cercato di renderlo il più semplice possibile e ha dovuto contenere alcuni elementi (ancora una volta semplificati) dall'app originale. Ho uno Activity e il mio Fragment personalizzato con MapView in esso, aggiunto in modo programmatico. Lo Map contiene alcuni punti/Markers. Dopo aver fatto clic su un Marker viene visualizzato il numero InfoWindow e facendo clic su di esso viene visualizzato il successivo numero Fragment (con la funzione replace()) nel contenuto.

I problemi

ci sono due questioni che ho:

  1. Quando il Map con Markers viene visualizzata la rotazione dello schermo provoca Class not found when unmarshalling errore con la mia classe personalizzata MyMapPoint - non ho idea del perché e cosa significa.

  2. Faccio clic sullo Marker e quindi su InfoWindow. Dopo questo premo il pulsante back dell'hardware. Ora posso vedere lo Map ma senza Markers e centrato nel punto 0,0.

Il codice

MainActivity

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]; 
      } 
     }; 

    } 
} 

Se avete bisogno di dare un'occhiata a qualsiasi altro file - fatemelo sapere. Here puoi trovare un progetto completo, devi solo mettere la tua API chiave di Maps nel file AndroidManifest.xml.

EDIT

sono riuscito a fare l'esempio ancora più semplice e aggiornato il codice di cui sopra.

risposta

30

Ecco il mio fix al primo problema:

Sembra che Map sta cercando di unparcel tutto il pacchetto, non solo è di possedere informazioni quando chiamo mMap.onCreate(savedInstanceState) e ha problema con esso se sto usando il mio classe personalizzata Parcelable. La soluzione che ha funzionato per me è stata la rimozione dei miei extra da savedInstanceState non appena li ho usati - prima chiamo Map's onCreate(). Lo faccio con savedInstanceState.remove(MY_KEY). Un'altra cosa che dovevo fare era chiamare mMap.onSaveInstanceState() prima di aggiungere le mie informazioni a outState nella funzione onSaveInstanceState(Bundle outState).

Ed ecco come ho gestito la seconda:

ho semplificato il progetto di esempio alle ossa nude. Stavo aggiungendo il grezzo Markers alla mappa e se sostituisco lo Fragment con la mappa con un altro e dopo aver cliccato su "indietro" ho ancora ottenuto la mappa "annullata". Così ho fatto due cose:

  1. risparmio CameraPosition in onPause() funzione per ripristinare in onResume()
  2. impostazione mMap a null in onPause() così quando il Fragment ritorna, il Markers vengono aggiunti ancora una volta dalla funzione addMapPoints() (I ho dovuto cambiarlo un po 'da quando stavo salvando e controllando gli ID Markers).

Qui ci sono esempi di codice:

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; 
    } 
} 

E per aggiornare la posizione della fotocamera in onResume() ho dovuto manualmente inizializza le mappe. L'ho fatto in setUpMap():

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

mi rendo conto che queste non sono soluzioni reali - solo le sostituzioni ma è il meglio che posso fare per ora e il progetto deve andare avanti. Se qualcuno trova soluzioni più pulite, sarò grato di avermelo fatto sapere.

+0

Applausi per la tua risposta alla Mappa che tentava di annullare l'intero pacchetto, ho implementato il tuo suggerimento secoli fa, ma recentemente ho aggiunto un 'ViewPager' al mio frammento che ha causato di nuovo il' BadParcelableException'.Si scopre che lo stato salvato di 'ViewPager' non può essere non ottimizzato o dato che è dalla libreria di supporto, quindi ho deciso di copiare lo stato della mappa in un nuovo pacchetto invece di rimuovere tutti i pacchetti che conoscevo, la mia risposta è qui: http : //stackoverflow.com/a/24651246/1007151 – darnmason

0

Implementa il tuo frammento/attività con GoogleMap.OnCameraChangeListener e di override metodo onCameraChange e dentro è possibile salvare la nuova posizione della telecamera, e onResume uso

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

Questa interfaccia è ora obsoleta. Vedere qui: https://stackoverflow.com/questions/38727517/oncamerachangelistener-is-deprecated – Ruben2112

1

venuti qui alla ricerca di eventuali suggerimenti per quanto riguarda onSaveInstance e NullPointerException. Ho provato vari metodi, incluso quello menzionato da @Izydorr, ma non ho avuto fortuna. Il problema era con FragmentPagerAdapter - l'adattatore dello ViewPager che ospitava i miei frammenti che incorporavano lo MapView s. Dopo averlo modificato su FragmentStatePagerAdapter, gli NPE sono andati via. Meno male!

Problemi correlati