最近项目工程使用百度地图,功能确实强大,但就像论坛里很多人提的那样,在MapView的刷新问题上,百度地图还有一定bug,比如在一个dialog中放一个MapView,却发现dialog中的MapView展现效果很差,很多地图上的标志没有刷新出来。比如下图
我们可以看到整个地图完全乱套了,现在我贴在这个dialog的代码,然后一起分析下问题
package baidumapsdk.demo;
import android.app.Dialog;
import android.content.Context;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import com.baidu.mapapi.map.MapView;
import com.baidu.platform.comapi.basestruct.GeoPoint;
public class MapDialog extends Dialog{
/**
* MapView 是地图主控件
*/
private MapView mMapView = null;
private Button mBtnBack = null;
public MapDialog(Context context) {
super(context);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mapdialog);
mMapView = (MapView)findViewById(R.id.bmapView);
mMapView.setBuiltInZoomControls(true);
mMapView.getController().setCenter(new GeoPoint((int)(39.945 * 1E6), (int)(116.404 * 1E6)));
mBtnBack = (Button)findViewById(R.id.btn_back);
mBtnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
dismiss();
}
});
}
@Override
public void dismiss() {
super.dismiss();
}
}
我们看到在Dialog中我们就直接从xml中引用了地图,不过在 百度地图Hello World 介绍中,百度地图似乎还调动了MapView的onResume和onPause等方法,虽然它们都是在Activity中调用的,而我们这是Dialog,但没有条件我们可以创造条件调用,直接在构造函数里调用onResume,dismiss()里调用onPause
package baidumapsdk.demo;
import android.app.Dialog;
import android.content.Context;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import com.baidu.mapapi.map.MapView;
import com.baidu.platform.comapi.basestruct.GeoPoint;
public class MapDialog extends Dialog{
/**
* MapView 是地图主控件
*/
private MapView mMapView = null;
private Button mBtnBack = null;
public MapDialog(Context context) {
super(context);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mapdialog);
mMapView = (MapView)findViewById(R.id.bmapView);
mMapView.setBuiltInZoomControls(true);
mMapView.onResume();
mMapView.getController().setCenter(new GeoPoint((int)(39.945 * 1E6), (int)(116.404 * 1E6)));
mBtnBack = (Button)findViewById(R.id.btn_back);
mBtnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
dismiss();
}
});
}
@Override
public void dismiss() {
mMapView.onPause();
super.dismiss();
}
}
这个时候似乎我们的使用有效果了,这样使用方式Dialog中MapView刷新没有问题了。
但是新问题来了。我们Dialog一般都是在Activity中调用的,如果调用的Activity中也有MapView
package baidumapsdk.demo;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import com.baidu.mapapi.BMapManager;
import com.baidu.mapapi.map.MKMapViewListener;
import com.baidu.mapapi.map.MKOfflineMap;
import com.baidu.mapapi.map.MKOfflineMapListener;
import com.baidu.mapapi.map.MapController;
import com.baidu.mapapi.map.MapPoi;
import com.baidu.mapapi.map.MapView;
import com.baidu.platform.comapi.basestruct.GeoPoint;
/**
* 演示MapView的基本用法
*/
public class BaseMapDemo extends Activity implements MKOfflineMapListener{
final static String TAG = "MainActivity";
/**
* MapView 是地图主控件
*/
private MapView mMapView = null;
/**
* 用MapController完成地图控制
*/
private MapController mMapController = null;
private Button mBtnDialog = null;
private Button mBtnActivity = null;
private MKOfflineMap mOffline = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 使用地图sdk前需先初始化BMapManager.
* BMapManager是全局的,可为多个MapView共用,它需要地图模块创建前创建,
* 并在地图地图模块销毁后销毁,只要还有地图模块在使用,BMapManager就不应该销毁
*/
DemoApplication app = (DemoApplication)this.getApplication();
if (app.mBMapManager == null) {
app.mBMapManager = new BMapManager(this);
/**
* 如果BMapManager没有初始化则初始化BMapManager
*/
app.mBMapManager.init(DemoApplication.strKey,new DemoApplication.MyGeneralListener());
}
/**
* 由于MapView在setContentView()中初始化,所以它需要在BMapManager初始化之后
*/
setContentView(R.layout.activity_main);
mMapView = (MapView)findViewById(R.id.bmapView);
mMapView.setBuiltInZoomControls(true);
/**
* 获取地图控制器
*/
mMapController = mMapView.getController();
/**
* 设置地图是否响应点击事件 .
*/
mMapController.enableClick(true);
/**
* 设置地图缩放级别
*/
mMapController.setZoom(12);
/**
* 将地图移动至指定点
* 使用百度经纬度坐标,可以通过http://api.map.baidu.com/lbsapi/getpoint/index.html查询地理坐标
* 如果需要在百度地图上显示使用其他坐标系统的位置,请发邮件至mapapi@baidu.com申请坐标转换接口
*/
GeoPoint p ;
double cLat = 39.945 ;
double cLon = 116.404 ;
Intent intent = getIntent();
if ( intent.hasExtra("x") && intent.hasExtra("y") ){
//当用intent参数时,设置中心点为指定点
Bundle b = intent.getExtras();
p = new GeoPoint(b.getInt("y"), b.getInt("x"));
}else{
//设置中心点为天安门
p = new GeoPoint((int)(cLat * 1E6), (int)(cLon * 1E6));
}
mMapController.setCenter(p);
mOffline = new MKOfflineMap();
/**
* 初始化离线地图模块,MapControler可从MapView.getController()获取
*/
mOffline.init(mMapController, this);
// mOffline.scan();
mBtnActivity = (Button)findViewById(R.id.btn_activity);
mBtnDialog = (Button)findViewById(R.id.btn_dialog);
mBtnActivity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(BaseMapDemo.this, GeometryDemo.class);
startActivity(intent);
}
});
mBtnDialog.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
MapDialog dialog = new MapDialog(BaseMapDemo.this);
dialog.show();
}
});
}
@Override
protected void onPause() {
/**
* MapView的生命周期与Activity同步,当activity挂起时需调用MapView.onPause()
*/
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
/**
* MapView的生命周期与Activity同步,当activity恢复时需调用MapView.onResume()
*/
mMapView.onResume();
super.onResume();
}
@Override
protected void onDestroy() {
/**
* MapView的生命周期与Activity同步,当activity销毁时需调用MapView.destroy()
*/
mMapView.destroy();
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mMapView.onRestoreInstanceState(savedInstanceState);
}
@Override
public void onGetOfflineMapState(int arg0, int arg1) {
// TODO Auto-generated method stub
}
}
这个Activity的中使用MapView的方式就是百度官方代码使用MapView的方式,当我们dimiss了dialog,发现Activity上的MapView的刷新也出问题了,甚至我测试有的时候Activity上的MapView连动都不能动,整个屏幕像死机了一样。
而且刷新混乱的方式跟之前Dialog混乱的方式很像,之前我们解决Dialog刷新混乱的问题的时候,是在Dialog调用了MapView的onResume方法,现在我们也来在dismiss了Dialog后再次调用Activity中MapView的onResume,有onResume方法就得有相应的onPause方法对应,显然,是在show Dialog的时候让Activity中的MapView onPause,
现在我们把Dialog和Activity中代码修改如下:
package baidumapsdk.demo;
import android.app.Dialog;
import android.content.Context;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import baidumapsdk.demo.BaseMapDemo.MapDialogCallback;
import com.baidu.mapapi.map.MapView;
import com.baidu.platform.comapi.basestruct.GeoPoint;
public class MapDialog extends Dialog{
/**
* MapView 是地图主控件
*/
private MapView mMapView = null;
private Button mBtnBack = null;
private MapDialogCallback mCallback;
public MapDialog(Context context) {
super(context);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mapdialog);
mMapView = (MapView)findViewById(R.id.bmapView);
mMapView.setBuiltInZoomControls(true);
mMapView.onResume();
mMapView.getController().setCenter(new GeoPoint((int)(39.945 * 1E6), (int)(116.404 * 1E6)));
mBtnBack = (Button)findViewById(R.id.btn_back);
mBtnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
dismiss();
}
});
}
@Override
public void dismiss() {
mMapView.onPause();
mCallback.mapback();
super.dismiss();
}
public void registerCallback(MapDialogCallback callback) {
mCallback = callback;
}
}
package baidumapsdk.demo;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
import com.baidu.mapapi.BMapManager;
import com.baidu.mapapi.map.MKMapViewListener;
import com.baidu.mapapi.map.MKOfflineMap;
import com.baidu.mapapi.map.MKOfflineMapListener;
import com.baidu.mapapi.map.MapController;
import com.baidu.mapapi.map.MapPoi;
import com.baidu.mapapi.map.MapView;
import com.baidu.platform.comapi.basestruct.GeoPoint;
/**
* 演示MapView的基本用法
*/
public class BaseMapDemo extends Activity implements MKOfflineMapListener{
final static String TAG = "MainActivity";
/**
* MapView 是地图主控件
*/
private MapView mMapView = null;
/**
* 用MapController完成地图控制
*/
private MapController mMapController = null;
private Button mBtnDialog = null;
private Button mBtnActivity = null;
private MKOfflineMap mOffline = null;
public interface MapDialogCallback{
void mapback();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* 使用地图sdk前需先初始化BMapManager.
* BMapManager是全局的,可为多个MapView共用,它需要地图模块创建前创建,
* 并在地图地图模块销毁后销毁,只要还有地图模块在使用,BMapManager就不应该销毁
*/
DemoApplication app = (DemoApplication)this.getApplication();
if (app.mBMapManager == null) {
app.mBMapManager = new BMapManager(this);
/**
* 如果BMapManager没有初始化则初始化BMapManager
*/
app.mBMapManager.init(DemoApplication.strKey,new DemoApplication.MyGeneralListener());
}
/**
* 由于MapView在setContentView()中初始化,所以它需要在BMapManager初始化之后
*/
setContentView(R.layout.activity_main);
mMapView = (MapView)findViewById(R.id.bmapView);
mMapView.setBuiltInZoomControls(true);
/**
* 获取地图控制器
*/
mMapController = mMapView.getController();
/**
* 设置地图是否响应点击事件 .
*/
mMapController.enableClick(true);
/**
* 设置地图缩放级别
*/
mMapController.setZoom(12);
/**
* 将地图移动至指定点
* 使用百度经纬度坐标,可以通过http://api.map.baidu.com/lbsapi/getpoint/index.html查询地理坐标
* 如果需要在百度地图上显示使用其他坐标系统的位置,请发邮件至mapapi@baidu.com申请坐标转换接口
*/
GeoPoint p ;
double cLat = 39.945 ;
double cLon = 116.404 ;
Intent intent = getIntent();
if ( intent.hasExtra("x") && intent.hasExtra("y") ){
//当用intent参数时,设置中心点为指定点
Bundle b = intent.getExtras();
p = new GeoPoint(b.getInt("y"), b.getInt("x"));
}else{
//设置中心点为天安门
p = new GeoPoint((int)(cLat * 1E6), (int)(cLon * 1E6));
}
mMapController.setCenter(p);
mOffline = new MKOfflineMap();
/**
* 初始化离线地图模块,MapControler可从MapView.getController()获取
*/
mOffline.init(mMapController, this);
// mOffline.scan();
mBtnActivity = (Button)findViewById(R.id.btn_activity);
mBtnDialog = (Button)findViewById(R.id.btn_dialog);
mBtnActivity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(BaseMapDemo.this, GeometryDemo.class);
startActivity(intent);
}
});
mBtnDialog.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mMapView.onPause();
MapDialog dialog = new MapDialog(BaseMapDemo.this);
dialog.registerCallback(new MapDialogCallback() {
@Override
public void mapback() {
// TODO Auto-generated method stub
mMapView.onResume();
}
});
dialog.show();
}
});
}
@Override
protected void onPause() {
/**
* MapView的生命周期与Activity同步,当activity挂起时需调用MapView.onPause()
*/
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
/**
* MapView的生命周期与Activity同步,当activity恢复时需调用MapView.onResume()
*/
mMapView.onResume();
super.onResume();
}
@Override
protected void onDestroy() {
/**
* MapView的生命周期与Activity同步,当activity销毁时需调用MapView.destroy()
*/
mMapView.destroy();
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mMapView.onSaveInstanceState(outState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mMapView.onRestoreInstanceState(savedInstanceState);
}
@Override
public void onGetOfflineMapState(int arg0, int arg1) {
// TODO Auto-generated method stub
}
}
按照这个方法修改代码后发现地图无论是在Dialog和Activity中都能正常使用,刷新问题不复存在。
由于我们看不到onResume和onPause中的源码,因此对于这个刷新问题,我们只能做猜测,我个人觉得MapView中有些刷新和显示上的控制是在onResume中操作,而在onPause中去暂停操作,个人觉得绘制地图应该是很耗资源的事情,所以确实需要这样的一些操作。但可能有些操作可能是静态全局性的,所以不管一个工程中有多少个MapView,都有些共用的操作,因此才会导致不同地图的刷新问题似乎会相互影响。
经过几天研究,我个人觉得目前的解决方案是每个MapView要有单独维护的onResume和onPause调用,不管这个MapView是在Dialog还是Activity中显示,在你需要使用MapView就调用其onResume方法,在你暂时不需要使用的时候就调用其onPause方法。
本编博文的源码在 源码可以下载到。