在旋转和背面还原MapView的状态 [英] Restoring MapView's state on rotate and on back

查看:120
本文介绍了在旋转和背面还原MapView的状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我有一个更大的应用程序,其中新的Google Maps API遇到了/遇到了一些问题.我试图在一个不同的问题中对其进行描述,但是由于看起来太复杂了,所以我决定开始一个新项目,尽可能简单,并尝试重现问题.所以就在这里.

I have a larger application in which I had/have several problems with new Google Maps API. I tried to describe it in a different question but since it seems too complex I decided to start a new project, as simple as possible and try to reproduce problems. So here it is.

情况

我正在使用Fragments,并且想将MapView放入其中.我不想使用MapFragment.我准备的示例项目可能不是很漂亮,但是我尝试使其尽可能简单,并且必须包含原始应用程序中的某些元素(再次简化). 我有一个Activity和我的自定义Fragment其中带有MapView,以编程方式添加. Map包含一些点/Markers.单击Marker后,将显示InfoWindow并单击它会导致下一个Fragment内容显示(具有replace()功能).

I'm using Fragments and want to put MapView inside. I don't want to use MapFragment. The sample project I prepared may be not very beautiful but I tried to make it as simple as possible and it had to contain some elements (again simplified) from the original app. I have one Activity and my custom Fragment with MapView in it, added programatically. The Map contains some points/Markers. After clicking on a Marker the InfoWindow is shown and clicking on it causes next Fragment being shown (with replace() function) in content.

问题

我有两个问题:

  1. 当显示带有MarkersMap时,屏幕旋转会导致我的自定义MyMapPoint类出现Class not found when unmarshalling错误-我不知道为什么,这意味着什么.

  1. When the Map with Markers is displayed screen rotation causes Class not found when unmarshalling error with my custom MyMapPoint class - I have no idea why and what it means.

我单击Marker,然后单击InfoWindow.之后,我按下硬件后退按钮.现在,我可以看到Map,但是没有Markers,并且居中于0,0点.

I click the Marker and then InfoWindow. After this I press hardware back button. Now I can see the Map but with no Markers and centered in 0,0 point.

代码

MainActivity

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

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

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

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

    }
}

如果您需要查看其他文件,请告诉我. 在这里,您可以找到完整的项目,只需在AndroidManifest.xml文件.

If you need to take a look at any other file - let me know. Here you can find a complete project, you just have to put your own Maps API KEY in AndroidManifest.xml file.

编辑

我设法使示例更加简单,并更新了上面的代码.

I managed to make the example even more simple and updated the code above.

推荐答案

这是我解决第一个问题的方法:

Here is my fix to the first problem:

似乎Map试图取消打包所有捆绑包,而不仅仅是我调用mMap.onCreate(savedInstanceState)时的信息,如果使用自定义的Parcelable类,则存在问题.对我有用的解决方案是,在我使用Map的onCreate()之前,一经使用就从savedInstanceState中删除了我的多余内容.我用savedInstanceState.remove(MY_KEY)来做.我要做的另一件事是在Fragment's onSaveInstanceState(Bundle outState)函数中将自己的信息添加到outState之前,先调用mMap.onSaveInstanceState().

It seems that Map is trying to unparcel all the bundle, not just it's own information when I call mMap.onCreate(savedInstanceState) and it has problem with it if I'm using my custom Parcelable class. The solution that worked for me was removing my extras from savedInstanceState as soon as I used them - before I call Map's onCreate(). I do it with savedInstanceState.remove(MY_KEY). Another thing I had to do was to call mMap.onSaveInstanceState() before adding my own information to outState in Fragment's onSaveInstanceState(Bundle outState) function.

这是我处理第二个问题的方法:

And here's how I handled the second one:

我将示例项目简化为基本内容.我在地图上添加了原始的Markers,如果我用另一个地图替换了Fragment,那么在单击返回"后,我仍然会得到空"的地图. 所以我做了两件事:

I simplified the example project to the bare bones. I was adding raw Markers to the map and if I replace the Fragment with map with another one then after clicking "back" I still got "nulled" map. So I did two things:

  1. CameraPosition保存在onPause()函数中以在onResume()
  2. 中将其还原
  3. onPause()中将mMap设置为null,以便当Fragment返回时,通过addMapPoints()函数再次添加Markers(由于保存和检查,我不得不对其稍作更改. Markers id's.
  1. saving CameraPosition in onPause() function to restore it in onResume()
  2. setting mMap to null in onPause() so when the Fragment comes back, the Markers are added again by the addMapPoints() function (I had to change it a little bit since I was saving and checking Markers id's).

以下是代码示例:

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

要更新onResume()中的相机位置,我必须手动初始化地图.我是在setUpMap()中完成的:

And to update the camera position in onResume() I had to manually initialize maps. I did it in setUpMap():

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

我意识到这些不是真正的解决方案-只是覆盖,但这是我目前能做的最好的事情,并且该项目必须继续进行.如果有人找到更清洁的修复程序,我将不胜感激,将不胜感激.

I realize that those aren't real solutions - just overrides but it's the best I can do for now and the project must go on. If anyone finds cleaner fixes I'll be grateful for letting me know about them.

这篇关于在旋转和背面还原MapView的状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆