在旋转和返回时恢复 MapView 的状态 [英] Restoring MapView's state on rotate and on back

查看:27
本文介绍了在旋转和返回时恢复 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 时,屏幕旋转会导致 Class not found when unmarshalling 错误与我的自定义 MyMapPoint class - 我不知道为什么和它意味着什么.

  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.

代码

主活动

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

    }
}

如果您需要查看任何其他文件 - 请告诉我.这里你可以找到一个完整的项目,你只需要把自己的Maps API KEY放在<代码>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:

当我调用 mMap.onCreate(savedInstanceState) 时,似乎 Map 试图解包所有的包,而不仅仅是它自己的信息,如果它有问题我正在使用我的自定义 Parcelable 类.对我有用的解决方案是在我使用它们后立即从 savedInstanceState 中删除它们 - 在我调用 Map 的 onCreate() 之前.我用 savedInstanceState.remove(MY_KEY) 来做.我必须做的另一件事是在将我自己的信息添加到 Fragment's onSaveInstanceState(BundleoutState) 函数.

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. onPause()函数中保存CameraPosition以在onResume()
  2. 中恢复它
  3. onPause() 中将 mMap 设置为 null,以便当 Fragment 返回时,添加 Markers再次通过 addMapPoints() 函数(因为我正在保存和检查 Markers id,所以我不得不稍微改变它).
  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天全站免登陆