26

HintergrundMapView des Staates auf drehen Wiederherstellen und auf der Rückseite

Ich habe eine größere Anwendung, in der ich mit API neue Google Maps einige Probleme hatten/haben. Ich habe versucht, es zu beschreiben in a different question, aber da es zu komplex scheint, entschied ich, ein neues Projekt zu starten, so einfach wie möglich und versuchen, Probleme zu reproduzieren. Hier ist es also.

Die Situation

ich Fragments bin mit und wollen MapView innen setzen. Ich möchte nicht MapFragment verwenden. Das Beispielprojekt, das ich vorbereitet habe, ist vielleicht nicht sehr schön, aber ich habe versucht, es so einfach wie möglich zu machen und es musste einige Elemente (wieder vereinfacht) von der ursprünglichen App enthalten. Ich habe eine Activity und meine benutzerdefinierte Fragment mit drin, programmatisch hinzugefügt. Die Map enthält einige Punkte/Markers. Nach einem Klick auf eine Marker wird die InfoWindow angezeigt und ein Klick darauf bewirkt, dass die nächste Fragment (mit replace() Funktion) im Inhalt angezeigt wird.

Die Probleme

Es gibt zwei Probleme ich habe:

  1. Wenn die Map mit Markers Bildschirm-Rotation angezeigt wird, verursacht Class not found when unmarshalling Fehler mit meiner benutzerdefinierten MyMapPoint Klasse - ich habe keine Ahnung, warum und was es bedeutet.

  2. Ich klicke auf Marker und dann InfoWindow. Danach drücke ich den Hardware-Zurück-Knopf. Jetzt kann ich die Map aber ohne Markers und mittig in 0,0 Punkt sehen.

Der Code

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

    } 
} 

Wenn Sie einen Blick auf jede andere Datei nehmen müssen - lassen Sie mich wissen. Here Sie können ein komplettes Projekt finden, Sie müssen nur Ihre eigenen Maps API KEY in AndroidManifest.xml Datei.

EDIT

ich es geschafft, das Beispiel noch einfacher und der Code oben aktualisiert zu machen.

Antwort

30

Hier ist meine Lösung für das erste Problem:

Es scheint, dass Map versucht, all das Bündel unparcel, es ist nicht nur eigene Informationen, wenn ich mMap.onCreate(savedInstanceState) nennen und es hat mit ihrem Problem, wenn ich mit meinem benutzerdefinierte Parcelable Klasse. Die Lösung, die für mich funktionierte, war das Entfernen meiner Extras von savedInstanceState, sobald ich sie benutzte - bevor ich Map's onCreate() anrufe. Ich mache es mit savedInstanceState.remove(MY_KEY). Eine andere Sache, die ich tun musste, war mMap.onSaveInstanceState() vor dem Hinzufügen meiner eigenen Informationen zu outState in onSaveInstanceState(Bundle outState) Funktion.

Und hier ist, wie ich die zweite behandelt:

ich das Beispielprojekt auf die nackten Knochen vereinfacht. Ich fügte rohe Markers der Karte hinzu und wenn ich die Fragment durch Karte mit einer anderen dann nach dem Klicken "zurück" ersetze, erhielt ich noch "nulled" Karte. Also habe ich zwei Dinge:

  1. CameraPosition in onPause() Funktion Speichern, um sie wiederherzustellen in onResume()
  2. mMap in onPause() so auf null gesetzt wird, wenn die Fragment zurückkommt, die Markers hinzugefügt werden wieder durch die addMapPoints() Funktion (I musste es ein wenig ändern, seit ich gespeichert und überprüft habe Markers IDs).

Hier sind Codebeispiele:

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

Und die Kameraposition zu aktualisieren, in onResume() musste ich manuell Karten initialisieren Ich habe es in setUpMap():

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

Ich weiß, dass das keine echten Lösungen - nur überschreibt aber es ist das Beste, was ich jetzt tun kann, und das Projekt muss weitergehen. Wenn jemand sauberere Korrekturen findet, bin ich dankbar, dass er mich darüber informiert hat.

+0

Cheers für Ihre Antwort auf der Karte versucht, das gesamte Bundle zu entpacken, ich habe Ihren Vorschlag vor langer Zeit implementiert, aber vor kurzem ein 'ViewPager' zu meinem Fragment hinzugefügt, das die' BadParcelableException' erneut verursacht.Stellt sich heraus, dass der "ViewPager" -Speicherstatus nicht entpackt werden kann, da er aus der Support-Bibliothek stammt. Daher habe ich den Map-Status in ein neues Bundle kopiert, anstatt alle Pakete, die ich kannte, zu entfernen. Meine Antwort lautet hier: http : //stackoverflow.com/a/24651246/1007151 – darnmason

0

Implementiert Ihr Fragment/Aktivität mit GoogleMap.OnCameraChangeListener und Überschreibung Methode onCameraChange und in die neue Kameraposition speichern kann, und onResume Verwendung

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

Diese Schnittstelle ist jetzt veraltet. Siehe hier: https://stackoverflow.com/questions/38727517/oncamerachelisteneris-is-deprecated – Ruben2112

1

hier kam irgendwelcher Hinweise auf der Suche in Bezug auf onSaveInstance und NullPointerException. Ich habe verschiedene Workarounds ausprobiert, einschließlich des von @Izydorr erwähnten, aber ich hatte kein Glück. Das Problem war mit FragmentPagerAdapter - der Adapter der ViewPager, die meine Fragmente beherbergte, die die MapView s eingebettet wurden. Nach der Änderung zu FragmentStatePagerAdapter sind die NPEs weggegangen. Wütend!

Verwandte Themen