Przywracanie stanu MapView przy obracaniu i z powrotem
Tło
Mam większą aplikację, w której miałem / mam kilka problemów z nowym Google Maps API. Próbowałem opisać to w innym pytaniu , ale ponieważ wydaje się to zbyt skomplikowane, postanowiłem rozpocząć nowy projekt, tak prosty, jak to tylko możliwe i spróbować odtworzyć problemy. Oto i ona.
Sytuacja
Używam Fragments
i chcę umieścić MapView
w środku. Nie chcę używać MapFragment
. Przykładowy projekt I przygotowana może nie jest zbyt piękna, ale starałem się, aby była jak najprostsza i musiała zawierać pewne elementy (ponownie uproszczone) z oryginalnej aplikacji.
Mam jeden Activity
i mój custom Fragment
z MapView
w nim, dodany programowo. Map
zawiera kilka punktów / Markers
. Po kliknięciu na Marker
wyświetla się InfoWindow
i kliknięcie na nią powoduje wyświetlenie następnej Fragment
(z funkcją replace()
) w treści.
Problemy
Są dwie kwestie I mieć:
Gdy wyświetlany jest
Map
zMarkers
obrót ekranu powoduje błądClass not found when unmarshalling
z moją klasą customMyMapPoint
- nie mam pojęcia dlaczego i co to znaczy.Klikam
Marker
, a następnieInfoWindow
. Po tym naciskam przycisk hardware back. Teraz widzęMap
ale bezMarkers
i wyśrodkowany w0,0
punkt.
Na kod
Główna aktywność
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];
}
};
}
}
Jeśli chcesz rzucić okiem na inne akta - daj mi znać. tutaj znajdziesz kompletny projekt, wystarczy umieścić swój własny klucz API Maps w pliku AndroidManifest.xml
.
EDIT
Udało mi się jeszcze bardziej uprościć przykład i zaktualizować powyższy kod.
3 answers
Oto moja poprawka do pierwszego problemu:
Wygląda na to, że Map
próbuje rozparcelować cały pakiet, a nie tylko własne informacje, kiedy wywołuję mMap.onCreate(savedInstanceState)
i ma z tym problem, Jeśli używam własnej klasy Parcelable
. Rozwiązaniem, które zadziałało dla mnie, było usunięcie moich dodatków z savedInstanceState
zaraz po ich użyciu-zanim zadzwonię do Map onCreate()
. Robię to z savedInstanceState.remove(MY_KEY)
. Inną rzeczą, którą musiałem zrobić, to zadzwonić mMap.onSaveInstanceState()
przed dodaniem własnych informacji do outState
W Fragment's
onSaveInstanceState(Bundle outState)
funkcja.
I oto jak poradziłem sobie z drugim:
Uprościłem przykładowy projekt do gołych kości. Dodałem raw Markers
do mapy i jeśli zamienię Fragment
na mapę na inną to po kliknięciu "wstecz" nadal mam "zerowaną" mapę.
Więc zrobiłem dwie rzeczy:
- zapisanie
CameraPosition
wonPause()
funkcja przywracająca go wonResume()
- ustawienie
mMap
na null wonPause()
więc kiedyFragment
powróci, {[14] } są dodawane ponownie przez funkcjęaddMapPoints()
(musiałem ją trochę zmienić bit, ponieważ zapisywałem i sprawdzałemMarkers
id ' s).
Oto próbki kodu:
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;
}
}
I aby zaktualizować pozycję kamery w onResume()
musiałem ręcznie zainicjować mapy. Zrobiłem to w setUpMap()
:
private void setUpMap() {
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
}
mMap.setOnInfoWindowClickListener(this);
mMap.setOnMapLongClickListener(this);
addMapPoints();
}
Zdaję sobie sprawę, że to nie są realne rozwiązania - tylko nadrzędne, ale to najlepsze, co mogę zrobić na razie i projekt musi trwać. Jeśli ktoś znajdzie czystsze poprawki będę wdzięczny za poinformowanie mnie o nich.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-07 14:07:47
Przyszedł tu w poszukiwaniu wskazówek dotyczących onSaveInstance
i NullPointerException
. Próbowałem różnych obejść, w tym tego, o którym wspomniał @Izydorr, ale nie miałem szczęścia. Problem był z FragmentPagerAdapter
- adapterem ViewPager
, który hostował moje fragmenty, które osadzały MapView
s. Po zmianie na FragmentStatePagerAdapter
NPEs odeszły. Whew!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-18 22:44:45
Implementuje Twój fragment / aktywność z GoogleMap.OnCameraChangeListener
i nadpisać metodę {[2] } i do ciebie Można zapisać nową pozycję kamery, a onResume
użyć
googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPositionSaved));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-24 10:34:55