我在使用SupportMapFragment开发Android应用时遇到了一些麻烦。目前,当我切换片段时,我只是隐藏了SupportMapFragment并将CameraPosition保存在SharedPreferences中。然后,当我再次显示地图时,它会从SharedPreferences加载CameraPosition。这工作正常,但地图本身需要再次加载。必须有一种方法来保存地图的方式,它几乎不需要任何时间弹出备份,如只是将它移动到背景或什么?谁能帮我这个?谢谢!
答案 0 :(得分:1)
一些截图证明我已经完成了它:
我的应用的多重模式
移至单张窗格并选择地图(丹麦语中的'Kort')标签。
发生的事情是:
这使得地图立即显示,具有标记等,因为它们处于多窗格模式。
将对地图片段的引用存储为字段变量,并尽可能使用它。这样您只需要担心一个映射实例(使用findFragmentByTag重新实例化它)。
其次,为了存储地图的状态,我存储了GoogleMap,你可以在地图片段上调用getMap(),在Activity中独立于Activity(与应用程序生命周期绑定的单例)。也就是说,我的地图片段将尽可能获取存储的GoogleMap对象并将其用于所有操作(缩放,标记等)。
以下是我使用的GoogleMapModule(如果您可以使用其中的一部分):
@Singleton
public class GoogleMapsModule extends StandardModule {
private final GoogleMapsModulePreferences modulePreferences;
public class GoogleMapsModulePreferences implements ModulePreferences {
@Override public boolean isActivated() {
return isActivated();
}
public int getAddressEntry() {
return preferences
.getStringInt(R.string.key_googlemaps_address_entry);
}
public int getCityEntry() {
return preferences.getStringInt(R.string.key_googlemaps_city_entry);
}
}
public GoogleMapsModulePreferences getPreferences() {
return modulePreferences;
}
public interface GoogleMapsCallback {
public void mapReady(GoogleMap map);
}
public interface FetchAddressCallback {
public void addressFound(AddressHolder addressHolder);
public void addressLookupFailed();
}
private static final String TAG = "GoogleMapsModule";
public static final int MAPTYPE_NORMAL = 0;
public static final int MAPTYPE_SATELITE = 1;
public static final int MAPTYPE_TERRAIN = 2;
public static final int MAPTYPE_HYBRID = 3;
private AddressHolder addressPin;
private GoogleMap mGoogleMap;
private Geocoder geocoder;
private GoogleMapsCallback googleMapsCallback;
private final Preferences preferences;
@Inject public GoogleMapsModule(@ForApplication Context context,
Preferences preferences, ExpirationCoreModule expiration,
ParseCoreModule parse) {
super(context, preferences, expiration, parse, MODULES.GOOGLEMAPS);
this.modulePreferences = new GoogleMapsModulePreferences();
this.preferences = preferences;
Log.i(TAG, "CREATING MODULE " + TAG);
geocoder = new Geocoder(context, new Locale("da_DK"));
}
@Override public void load() {
MapsInitializer.initialize(context);
loadEnded();
}
public void setGoogleMapsCallback(GoogleMapsCallback mapReadyCallback) {
this.googleMapsCallback = mapReadyCallback;
}
public void destroyMap() {
mGoogleMap = null;
};
public void clearMap() {
clearMap(false);
}
public void clearMap(boolean keepAddressPin) {
this.googleMapsCallback = null;
if (mGoogleMap != null) {
mGoogleMap.clear();
if (keepAddressPin && addressPin != null) {
addAddressPin(addressPin);
}
}
}
public void setMap(GoogleMap map) {
if (mGoogleMap == null) {
mGoogleMap = map;
mGoogleMap.setMyLocationEnabled(true);
mGoogleMap.setTrafficEnabled(true);
mGoogleMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
}
if (googleMapsCallback != null) {
googleMapsCallback.mapReady(mGoogleMap);
}
}
public GoogleMap getMap() {
return mGoogleMap;
}
public void setMapType(int maptype) {
if (mGoogleMap != null) {
mGoogleMap.setMapType(maptype);
}
}
public String mapType(int maptype) {
switch (maptype) {
case MAPTYPE_NORMAL:
return "Normal";
case MAPTYPE_SATELITE:
return "Satelite";
case MAPTYPE_TERRAIN:
return "Terrain";
case MAPTYPE_HYBRID:
return "Hybrid";
default:
return "Normal";
}
}
public void zoomWithBounds(LatLngBounds bounds, int padding)
throws IllegalStateException {
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);
getMap().animateCamera(cu);
}
public void addLocationPin(Location location) {
mGoogleMap.addMarker(new MarkerOptions().position(
new LatLng(location.getLatitude(), location.getLongitude()))
.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_RED)));
}
public void addAddressPin(AddressHolder addressHolder) {
if (addressHolder.position != null) {
String city = addressHolder.city;
String address = addressHolder.address;
// address or city or empty
String title = (address != null && !address.isEmpty()) ? address
: ((city != null) ? city : "");
MarkerOptions markerOptions = new MarkerOptions()
.position(addressHolder.position)
.title(title)
.icon(BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE));
Marker destMarker = mGoogleMap.addMarker(markerOptions);
destMarker.showInfoWindow();
addressPin = addressHolder;
}
}
public void moveTo(AddressHolder addressHolder) {
GoogleMap map = getMap();
CameraUpdate center = CameraUpdateFactory
.newLatLng(addressHolder.position);
CameraUpdate zoom = CameraUpdateFactory.zoomTo(16);
map.moveCamera(center);
map.animateCamera(zoom);
}
public void zoomTo(Location location) {
if (location == null) {
return;
}
zoomTo(new LatLng(location.getLatitude(), location.getLongitude()));
}
public void zoomTo(AddressHolder addressHolder) {
if (getMap() == null || addressHolder.position == null) {
Log.e(TAG, "zoomTo map or address position was null: map "
+ (getMap() == null) + " address position "
+ (addressHolder.position == null));
return;
}
addAddressPin(addressHolder);
// Zoom in, animating the camera.
zoomTo(addressHolder.position);
}
private void zoomTo(LatLng latlng) {
GoogleMap map = getMap();
if (getMap() == null || latlng == null) {
return;
}
// Zoom in, animating the camera.
map.animateCamera(CameraUpdateFactory.newLatLngZoom(latlng, 16));
}
public void lookupAddress(final FetchAddressCallback fetchAddressCallback,
String address, String city) {
new GetAddressPositionTask(fetchAddressCallback, address, city)
.execute();
}
public void lookupAddress(final FetchAddressCallback fetchAddressCallback,
String searchString) {
new GetAddressPositionTask(fetchAddressCallback, searchString)
.execute();
}
private class GetAddressPositionTask extends
AsyncTask<String, Integer, AddressHolder> {
private FetchAddressCallback fetchAddressPositionCallback;
private String searchString;
private String city = "";
private String address = "";
public GetAddressPositionTask(
FetchAddressCallback fetchAddressPositionCallback,
String address, String city) {
this.fetchAddressPositionCallback = fetchAddressPositionCallback;
this.city = city;
this.address = address;
this.searchString = address + ", " + city;
}
public GetAddressPositionTask(
FetchAddressCallback fetchAddressPositionCallback,
String searchString) {
this.fetchAddressPositionCallback = fetchAddressPositionCallback;
this.searchString = searchString;
}
@Override protected void onPreExecute() {
super.onPreExecute();
}
@Override protected AddressHolder doInBackground(String... params) {
final String lookupStringUriencoded = Uri.encode(searchString);
LatLng position = null;
try {
if (geocoder != null) {
List<Address> addresses = geocoder.getFromLocationName(
searchString, 1);
if (addresses != null && !addresses.isEmpty()) {
Address first_address = addresses.get(0);
String foundCity = first_address.getLocality();
if (foundCity != null) {
city = (city.isEmpty()) ? foundCity : city;
}
String addressName = first_address.getThoroughfare();
String streetNumber = first_address
.getSubThoroughfare();
// if (addressName != null && address.isEmpty()) {
address = (streetNumber != null) ? addressName + " "
+ streetNumber : addressName;
// }
position = new LatLng(first_address.getLatitude(),
first_address.getLongitude());
Log.d(TAG, "geocoder was found " + position);
}
} else {
Log.e(TAG, "geocoder was null, is the module loaded?");
}
} catch (IOException e) {
// Log.e(TAG, "geocoder failed, moving on to HTTP");
}
// try HTTP lookup to the maps API
if (position == null) {
HttpGet httpGet = new HttpGet(
"http://maps.google.com/maps/api/geocode/json?address="
+ lookupStringUriencoded + "&sensor=true");
HttpClient client = new DefaultHttpClient();
HttpResponse response;
StringBuilder stringBuilder = new StringBuilder();
try {
response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
InputStream stream = entity.getContent();
int b;
while ((b = stream.read()) != -1) {
stringBuilder.append((char) b);
}
} catch (ClientProtocolException e) {
Log.e(TAG, e.getMessage(), e);
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
JSONObject jsonObject = new JSONObject();
try {
// Log.d("MAPSAPI", stringBuilder.toString());
jsonObject = new JSONObject(stringBuilder.toString());
if (jsonObject.getString("status").equals("OK")) {
jsonObject = jsonObject.getJSONArray("results")
.getJSONObject(0);
JSONArray address_components = jsonObject
.getJSONArray("address_components");
String jsonCity = "";
String jsonAddress = "";
String jsonStreetNumber = "";
// extract looked up address information
for (int i = 0; i < address_components.length(); i++) {
JSONObject address_component = address_components
.getJSONObject(i);
String type = address_component.getJSONArray(
"types").getString(0);
String value = address_component
.getString("long_name");
if (type.equals("locality")) {
jsonCity = value;
}
if (type.equals("route")) {
jsonAddress = value;
}
if (type.equals("street_number")) {
jsonStreetNumber = value;
}
}
Log.d("MAPSAPI", jsonCity + "," + jsonAddress + " "
+ jsonStreetNumber);
city = (city.isEmpty()) ? jsonCity : city;
address = (address.isEmpty()) ? (jsonAddress + " " + jsonStreetNumber)
.trim() : address;
// extract position
jsonObject = jsonObject.getJSONObject("geometry");
jsonObject = jsonObject.getJSONObject("location");
String lat = jsonObject.getString("lat");
String lng = jsonObject.getString("lng");
Log.d("MAPSAPI", "latlng " + lat + ", " + lng);
position = new LatLng(Double.valueOf(lat),
Double.valueOf(lng));
}
} catch (JSONException e) {
Log.e(TAG, e.getMessage(), e);
}
}
return new AddressHolder(address, city, position);
}
@Override protected void onPostExecute(final AddressHolder result) {
Log.d(TAG, "GetAddressPositionTask " + result);
if (result.position != null) {
fetchAddressPositionCallback.addressFound(result);
} else {
fetchAddressPositionCallback.addressLookupFailed();
}
// ensure no more callbacks to this
fetchAddressPositionCallback = null;
super.onPostExecute(result);
}
}
}
我已经在我的应用中实现了Dagger http://square.github.io/dagger/,这解释了@Singleton,这使我能够这样做:
@Inject GoogleMapsModule googleMapsModule;
每当我想在我的代码中的任何地方使用此对象时。不过,我认为您可以通过扩展应用程序来存储地图数据。例如,您可以阅读此博客:http://www.devahead.com/blog/2011/06/extending-the-android-application-class-and-dealing-with-singleton/
要在GoogleMapsModule中保留正确的引用,请执行以下操作:
@Override public void onMapReady(GoogleMap map) {
if (map != null) {
googleMapsModule.setMap(map);
}
}
现在,最后一个松散的结局是,如果活动是从头开始而GoogleMapModule持有引用,那么在地图上完成的任何操作都将无效,因为引用不再与地图相关联。为了处理这个问题,如果savedInstanceState为null,我将销毁地图:
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
if (savedInstanceState != null) {
// all is well
mapsFragment = (GoogleMapFragment) fm
.findFragmentByTag(FRAGMENT_MAP_TAG);
} else {
// no saved instance - destroy the map to enable onMapReady setting a new reference
googleMapsModule.destroyMap();
}
if (device.isMultiPane()) {
addMapFragment(ft).commit();
}
}
// called when the map tab is selected or from onCreate if multipane
private FragmentTransaction addMapFragment(FragmentTransaction ft) {
if (mapsFragment == null) {
AddressHolder address = (device.isMultiPane()) ? null
: getSelectedAddressHolder();
mapsFragment = GoogleMapFragment.newInstance(address);
ft.add(R.id.details, mapsFragment, Turios.FRAGMENT_MAP_TAG);
mapsOptionsFragment = new GoogleMapOptionsFragment();
ft.add(R.id.details, mapsOptionsFragment, FRAGMENT_MAP_OPTIONS_TAG);
} else {
ft.attach(mapsFragment);
ft.attach(mapsOptionsFragment);
}
return ft;
}
最后,Android的默认行为是启动一个新的Activity,执行onCreate,每次启动应用程序,或从另一个Activity等移回。因此,我已将manifest中的launchMode设置为singleTask。
<activity
android:name=".activities.Turios"
android:alwaysRetainTaskState="true"
android:clearTaskOnLaunch="true"
android:launchMode="singleTask"
>
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable_address" />
</activity>
这里解释了Launchmode:http://developer.android.com/guide/topics/manifest/activity-element.html#lmode
答案 1 :(得分:0)
不,没有办法强制地图保留其数据。