安卓:在谷歌地图中保存地图状态

8

我以前用过这段代码,但现在在恢复地图状态时遇到了一些错误。我从David Gassner的教程中获取了这段代码,现在我想再次使用它。我有点困惑,在跟踪中放置了一些抛出信息。LogCat报告指向RESUME起始行上的错误。有人知道如何修复吗?

map.animateCamera(update);          
map.setMapType(mgr.getSavedMapType())

这是代码:

这里是代码:

@Override
protected void onStop() {
    super.onStop();
    MapStateManager mgr = new MapStateManager(this);
    mgr.saveMapState(mMap);
    Toast.makeText(this, "Map State has been save?", Toast.LENGTH_SHORT).show();
}

@Override
protected void onResume() {
    super.onResume();
    MapStateManager mgr = new MapStateManager(this);
    CameraPosition position = mgr.getSavedCameraPosition();
    if (position != null) {
        CameraUpdate update = CameraUpdateFactory.newCameraPosition(position);
        Toast.makeText(this, "entering Resume State", Toast.LENGTH_SHORT).show();
        mMap.moveCamera(update);

        mMap.setMapType(mgr.getSavedMapType());
    }
}
public class MapStateManager {

    private static final String LONGITUDE = "longitude";
    private static final String LATITUDE = "latitude";
    private static final String ZOOM = "zoom";
    private static final String BEARING = "bearing";
    private static final String TILT = "tilt";
    private static final String MAPTYPE = "MAPTYPE";

    private static final String PREFS_NAME ="mapCameraState";

    private SharedPreferences mapStatePrefs;

    public MapStateManager(Context context) {
        mapStatePrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
    }

    public void saveMapState(GoogleMap mapMie) {
        SharedPreferences.Editor editor = mapStatePrefs.edit();
        CameraPosition position = mapMie.getCameraPosition();

        editor.putFloat(LATITUDE, (float) position.target.latitude);
        editor.putFloat(LONGITUDE, (float) position.target.longitude);
        editor.putFloat(ZOOM, position.zoom);
        editor.putFloat(TILT, position.tilt);
        editor.putFloat(BEARING, position.bearing);
        editor.putInt(MAPTYPE, mapMie.getMapType());
        editor.commit();
    }

    public CameraPosition getSavedCameraPosition() {
        double latitude = mapStatePrefs.getFloat(LATITUDE, 0);
        if (latitude == 0) {
            return null;
        }
        double longitude = mapStatePrefs.getFloat(LONGITUDE, 0);
        LatLng target = new LatLng(latitude, longitude);

        float zoom = mapStatePrefs.getFloat(ZOOM, 0);
        float bearing = mapStatePrefs.getFloat(BEARING, 0);
        float tilt = mapStatePrefs.getFloat(TILT, 0);

        CameraPosition position = new CameraPosition(target, zoom, tilt, bearing);
        return position;
    }

    public int getSavedMapType() {
        return mapStatePrefs.getInt(MAPTYPE, GoogleMap.MAP_TYPE_NORMAL);
    }
}

这里是来自logcat的错误信息。我只包括了红色的行。
> 01-07 12:51:46.145: E/AndroidRuntime(15770): FATAL EXCEPTION: main
01-07 12:51:46.145: E/AndroidRuntime(15770): java.lang.RuntimeException: Unable to resume activity {com.ourThesis.junieNegentien2015/com.ourThesis.junieNegentien2015.MainActivity}: java.lang.NullPointerException
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2936)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2965)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2400)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread.access$600(ActivityThread.java:162)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.os.Handler.dispatchMessage(Handler.java:107)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.os.Looper.loop(Looper.java:194)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread.main(ActivityThread.java:5371)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at java.lang.reflect.Method.invokeNative(Native Method)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at java.lang.reflect.Method.invoke(Method.java:525)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at dalvik.system.NativeStart.main(Native Method)
01-07 12:51:46.145: E/AndroidRuntime(15770): Caused by: java.lang.NullPointerException
01-07 12:51:46.145: E/AndroidRuntime(15770):    at com.ourThesis.junieNegentien2015.MainActivity.onResume(MainActivity.java:183)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1190)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.Activity.performResume(Activity.java:5213)
01-07 12:51:46.145: E/AndroidRuntime(15770):    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2923)
01-07 12:51:46.145: E/AndroidRuntime(15770):    ... 12 more

@Tim Castelijns,以下是修改后的模式,但仍然无法正常工作,您能看一下吗?
private SupportMapFragment mapFragment;
    private GoogleMap map;  
    private Location mCurrentLocation;
    private GoogleApiClient mGoogleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main); 
        setupMapIfNeeded();
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);

    }

    private void setupMapIfNeeded() {
        mapFragment = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map));             
        if (mapFragment != null) {          
            mapFragment.getMapAsync(new OnMapReadyCallback()
            {
                @Override
                public void onMapReady(GoogleMap map) 
                {
                    loadMap(map);                                       
                }
            });
        } else 
        {
            Toast.makeText(this, "Error - Map Fragment was null!!", Toast.LENGTH_SHORT).show();
        }
    }

    protected void loadMap(GoogleMap googleMap) {
        map = googleMap;
        if (map != null) {
            // Map is ready
            Toast.makeText(this, "Map Fragment was loaded properly!", Toast.LENGTH_LONG).show();
            initListeners();            

            // map is loaded, trace your location
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(LocationServices.API)               
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .build();
            connectClient();

            //calling resume state
            resumeState(map);

        } else {
            Toast.makeText(this, "Error - Map was null!!", Toast.LENGTH_SHORT).show();
        }
    }

    protected void connectClient() {
        // Connect the client.
        if (isGooglePlayServicesAvailable() && mGoogleApiClient != null) {
            mGoogleApiClient.connect();
            Toast.makeText(this, "Google API Client successfully connected", Toast.LENGTH_LONG).show();
        }
    }

    /*
     * Called when the Activity becomes visible.
    */
    @Override
    protected void onStart() {
        super.onStart();
        connectClient();
        }

    public void resumeState(GoogleMap googleMap) {      
        map = googleMap;
        MapStateManager mgr = new MapStateManager(this);
        CameraPosition position = mgr.getSavedCameraPosition();
        if (position != null) {
            CameraUpdate update = CameraUpdateFactory.newCameraPosition(position);
            Toast.makeText(this, "entering Resume State", Toast.LENGTH_LONG).show();
            map.moveCamera(update);

            map.setMapType(mgr.getSavedMapType());
        }
    }

    /*
     * Called when the Activity is no longer visible.
     */    
    @Override
    protected void onPause() {
        super.onPause();
        MapStateManager mgr = new MapStateManager(this);
        mgr.saveMapState(map);
        Toast.makeText(this, "Map State has been save?", Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onResume() {     
        super.onResume();       
        setupMapIfNeeded();
    }

    @Override
    public void onConnected(Bundle dataBundle) {
        // Display the connection status
        Location location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        if (location != null) {
            Toast.makeText(this, "GPS location was found!", Toast.LENGTH_SHORT).show();
            LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
            CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 16);
            map.animateCamera(cameraUpdate);
            /*startLocationUpdates();*/
        } else {
            Toast.makeText(this, "Current location was null, enable GPS on your mobile!", Toast.LENGTH_SHORT).show();
        }
    }

    private void initListeners() {
        map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
        map.getUiSettings().setZoomControlsEnabled(true);
        map.setMyLocationEnabled(true);
        map.setOnMarkerClickListener(null);        
        map.setOnMapLongClickListener(null);
        map.setOnInfoWindowClickListener(null);
        map.setOnMapClickListener(null);
        map.setTrafficEnabled(true);
        map.setOnMapLongClickListener(this);
        map.setOnMapClickListener(this);
        map.setPadding(0, 80, 0, 0);

    }

包括来自logcat的错误信息。 - Tim
@TimCastelijns 我已经包含了logcat错误 :) - Larigyn
有人愿意帮我吗? - Larigyn
我正在使用最新的SDK(28)与GoogleMap和支持片段;看起来GoogleMap已经保留了它的相机状态,并且只要我在我的主活动中正确地恢复片段,它就会被恢复,而无需我编写任何代码! - RobP
3个回答

12

问题是在恢复时你的mMap为空,因此你无法使用例如mMap.moveCamera();,这会导致你的应用程序崩溃。

解决方法是在尝试再次使用地图之前检查是否需要设置地图。

我稍微修改了你的代码,在onCreateonResume中都加入了检查地图是否需要设置的代码。请注意,我使用onPause而不是onStop,因为它是onResume的对应函数。

private GoogleMap mMap;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    setupMapIfNeeded();
}

private void setupMapIfNeeded() {
    // Obtain the SupportMapFragment and get notified when the map is ready to be used.
    if (mMap == null) {
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }
}
@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;

    MapStateManager mgr = new MapStateManager(this);
    CameraPosition position = mgr.getSavedCameraPosition();
    if (position != null) {
        CameraUpdate update = CameraUpdateFactory.newCameraPosition(position);
        Toast.makeText(this, "entering Resume State", Toast.LENGTH_SHORT).show();
        mMap.moveCamera(update);

        mMap.setMapType(mgr.getSavedMapType());
    }
}

@Override
protected void onPause() {
    super.onPause();
    MapStateManager mgr = new MapStateManager(this);
    mgr.saveMapState(mMap);
    Toast.makeText(this, "Map State has been save?", Toast.LENGTH_SHORT).show();
}

@Override
protected void onResume() {
    super.onResume();
    setupMapIfNeeded();
}

MapstateManager完全相同。

public class MapStateManager {

    private static final String LONGITUDE = "longitude";
    private static final String LATITUDE = "latitude";
    private static final String ZOOM = "zoom";
    private static final String BEARING = "bearing";
    private static final String TILT = "tilt";
    private static final String MAPTYPE = "MAPTYPE";

    private static final String PREFS_NAME ="mapCameraState";

    private SharedPreferences mapStatePrefs;

    public MapStateManager(Context context) {
        mapStatePrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
    }

    public void saveMapState(GoogleMap mapMie) {
        SharedPreferences.Editor editor = mapStatePrefs.edit();
        CameraPosition position = mapMie.getCameraPosition();

        editor.putFloat(LATITUDE, (float) position.target.latitude);
        editor.putFloat(LONGITUDE, (float) position.target.longitude);
        editor.putFloat(ZOOM, position.zoom);
        editor.putFloat(TILT, position.tilt);
        editor.putFloat(BEARING, position.bearing);
        editor.putInt(MAPTYPE, mapMie.getMapType());
        editor.commit();
    }

    public CameraPosition getSavedCameraPosition() {
        double latitude = mapStatePrefs.getFloat(LATITUDE, 0);
        if (latitude == 0) {
            return null;
        }
        double longitude = mapStatePrefs.getFloat(LONGITUDE, 0);
        LatLng target = new LatLng(latitude, longitude);

        float zoom = mapStatePrefs.getFloat(ZOOM, 0);
        float bearing = mapStatePrefs.getFloat(BEARING, 0);
        float tilt = mapStatePrefs.getFloat(TILT, 0);

        CameraPosition position = new CameraPosition(target, zoom, tilt, bearing);
        return position;
    }

    public int getSavedMapType() {
        return mapStatePrefs.getInt(MAPTYPE, GoogleMap.MAP_TYPE_NORMAL);
    }
}

你能看一下这段代码吗?还是不起作用 :( - Larigyn
@JunieNegentien 如果有新的问题,请提出新的问题。 - Tim
谢谢,这真的很有帮助! - RonEskinder
迄今为止最佳答案! - Zhar

3

如果您只想在方向更改之间保存和恢复相机位置,则可以在saveInstanceState Bundle上直接保存CameraPosition实例本身。 CameraPosition实现了ReflectedParcelable,因此应该可以正常工作。

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    if (googleMap != null) {
        outState.putParcelable(STATE_KEY_MAP_CAMERA, googleMap.getCameraPosition());
    }
}

并进行恢复,需要执行以下操作:
if (savedInstanceState != null) {
    CameraUpdate cameraPos = CameraUpdateFactory.newCameraPosition(
        (CameraPosition)(savedInstanceState.getParcelable(STATE_KEY_MAP_CAMERA)));
    googleMap.moveCamera(cameraPos);
}

2

mapFragment.setRetainInstance(true);可以在onCreate方法中使用,用于保存地图片段状态。


可能会有一些额外的信息是有益的。 - Pyves
你可以在互联网上找到相关信息;我对此不是很了解。 - Mikhail
我本想这么做,但这样会泄漏你的活动实例。唯一的解决方案似乎是手动保存/恢复状态(如果例如已加载了5000个标记,则可能需要很长时间),或在清单中使用configChanges(这也会引起其他问题…)。 - Tim Autin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接