因为viewpager的预加载机制,使得联网应用会多出内存以及网络的使用量,同时,在viewpager下使用高德地图,也会因此出现各种莫名其妙的问题,因此,需要使用懒加载的手段。
实现懒加载,只需继承fragment类然后重写与界面显示相关的方法即可。
public abstract class LazyFragment extends Fragment{
protected boolean isVisible;
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {//frahment从不可见到完全可见的时候,会调用该方法
super.setUserVisibleHint(isVisibleToUser);
if (getUserVisibleHint() && isInit){
isVisible = true;
onVisible();
}else {
isVisible = false;
isInit=false;
onInvisible();
}
}
protected abstract void lazyLoad();//懒加载的方法,在这个方法里面我们为Fragment的各个组件去添加数据
protected void onVisible(){
lazyLoad();
}
protected void onInvisible(){
}
}
isInit是一个在MapFragment里定义的bool型变量,用来判断高德地图所在的fragment是否已经初始化,如果不添加,那么点击viewpager里的tab跳转可能会出现空指针错误。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map, container, false);
isFirstLoc = 1;
isInit = true;
mapView = (MapView) v.findViewById(R.id.map);
mapView.onCreate(savedInstanceState);// 此方法必须重写
screenWidth = ScreenUtils.getScreenWidth(getActivity());
screenHeight = ScreenUtils.getScreenHeight(getActivity());
setUpMapIfNeeded();
return v;
}
完成地图空间的初始化以及定位标识符的初始化,防止多次重复定位。高度与宽度用来后面的地图多标志聚合。
private void setUpMapIfNeeded() {
((MainActivity) getActivity()).setFabVisible(0);//业务要求,读者可忽视
aMap = mapView.getMap();
uiSettings = aMap.getUiSettings();
aMap.setLocationSource(this);//设置定位监听,实现LocationSource的接口
aMap.setOnMarkerClickListener(this);// 设置点击marker事件监听器
aMap.setOnCameraChangeListener(this);// 对amap添加移动地图事件监听器
//是否显示定位按钮
uiSettings.setMyLocationButtonEnabled(true);
//设置缩放控件
uiSettings.setZoomGesturesEnabled(true);
//显示比例尺控件
uiSettings.setScaleControlsEnabled(true);
uiSettings.setZoomControlsEnabled(true);
//显示定位层并可以触发定位事件
aMap.setMyLocationEnabled(true);
//开始定位
location();
}
完成地图一些基本的参数配置。
private void location() {
Log.d("定位测试", "location方法已经执行");
//初始化定位
aMapLocationClient = new AMapLocationClient(getActivity());
//设置定位回调监听
aMapLocationClient.setLocationListener(this);
//初始化定位参数
aMapLocationClientOption = new AMapLocationClientOption();
//设置定位模式为Hight_Accuracy高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式
aMapLocationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//设置是否返回地址信息(默认返回地址信息)
aMapLocationClientOption.setNeedAddress(true);
//设置是否只定位一次,默认为false
aMapLocationClientOption.setOnceLocation(false);
//设置是否强制刷新WIFI,默认为强制刷新
aMapLocationClientOption.setWifiActiveScan(true);
//设置是否允许模拟位置,默认为false,不允许模拟位置
aMapLocationClientOption.setMockEnable(false);
//设置定位间隔,单位毫秒,默认为2000ms
aMapLocationClientOption.setInterval(10000);
//给定位客户端对象设置定位参数
aMapLocationClient.setLocationOption(aMapLocationClientOption);
//启动定位
aMapLocationClient.startLocation();
}
配置定位参数并开始定位。
public void onLocationChanged(AMapLocation aMapLocation) {
//Log.d("定位回掉方法测试",aMapLocation.toString());
if (aMapLocation != null) {
if (aMapLocation.getErrorCode() == 0) {
//可在其中解析amapLocation获取相应内容。
lat = aMapLocation.getLatitude();//获取纬度
lon = aMapLocation.getLongitude();//获取经度
//获取定位时间
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(aMapLocation.getTime());
df.format(date);
Log.d("缩放标志位", "定位次数" + isFirstLoc);
// 如果不设置标志位,此时再拖动地图时,它会不断将地图移动到当前的位置
if (isFirstLoc == 1) {
//设置缩放级别(缩放级别为4-20级)
isFirstLoc = 0;
aMap.moveCamera(CameraUpdateFactory.zoomTo(15));
//将地图移动到定位点
aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(lat, lon)));
//点击定位按钮 能够将地图的中心移动到定位点
mOnLocationChangedListener.onLocationChanged(aMapLocation);
}
if (null == markerLocal) {
//只有为空时才重绘,防止内存泄漏
addMarkerToMap(lat, lon);
}
} else {
//定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
Log.e("地图错误", "定位失败, 错误码:" + aMapLocation.getErrorCode() + ", 错误信息:"
+ aMapLocation.getErrorInfo());
}
}
}
此方法是完成定位的回调方法,同时也是移动地图的回调方法。再次方法里获取定位的结果并显示在地图上。
//添加标志到地图上
private void addMarkerToMap(Double lat, Double lon) {
NetWorks.getAroundPosts(lat, lon, new Observer() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("MapFragment",e.getMessage());
}
@Override
public void onNext(PostLab postLab) {
Log.e("地图","成功获取数据");
if (markerOptionsListall != null) {
//每次重绘前清除数据
markerOptionsListall.clear();
}
posts = postLab.getmPosts();
for (Post post :posts) {
double tempLat = post.getmLat();
double tempLon = post.getmLon();
final LatLng mLatLng = new LatLng(tempLat, tempLon);
final String mTitle = post.getmTitle();
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.draggable(true);
markerOptions.icon(BitmapDescriptorFactory.defaultMarker());
View v = getActivity().getLayoutInflater()
.inflate(R.layout.custom_infowindow_content, null);
ImageView imageView = (ImageView) v.findViewById(R.id.custom_content_image);
TextView textView = (TextView) v.findViewById(R.id.custom_content_title);
imageLoader.displayImage(post.getmImageUrl(),
imageView, ImageLoaderOptionUtil.getOptions());
textView.setText(mTitle);
markerOptions.position(mLatLng);
//将这个view转为bitmap格式
markerOptions.icon(BitmapDescriptorFactory.fromView(v));
//markerOptions.title(mTitle);
markerOptionsListall.add(markerOptions);
markerLocal = aMap.addMarker(markerOptions);
}
}
});
}
本方法的目的是根据经纬度信息将标记添加到地图上,我是业务需要,从网络获取多个经纬度信息然后添加到地图上。
protected void lazyLoad() {
if (null != aMap) {
//每次重新加载地图前,清除数据
aMap.clear();
markerLocal = null;
markerOptionsListall.clear();
markerOptionsListInView.clear();
}
setUpMapIfNeeded();
}
public class MapFragment extends LazyFragment implements
AMapLocationListener, LocationSource, AMap.OnMarkerClickListener,
AMap.OnCameraChangeListener {
private AMap aMap;
private MapView mapView;
private UiSettings uiSettings;
private AMapLocationClient aMapLocationClient;
private OnLocationChangedListener mOnLocationChangedListener = null;
private AMapLocationClientOption aMapLocationClientOption;
private Double lat; //经度
private Double lon; //纬度
private int isFirstLoc;//定位标志位
public static boolean isInit = false;//控件初始化标志位
private Marker markerLocal;
private int screenHeight;// 屏幕高度(px)
private int screenWidth;// 屏幕宽度(px)
//视野内的marker
private ArrayList markerOptionsListInView = new ArrayList<>();
//所有的marker
private ArrayList markerOptionsListall = new ArrayList<>();
//markerOptionsListall初始化标志位
private int isListallInit = 0;
//接收到的post集合
private List posts;
//获取imageloader实例
private final ImageLoader imageLoader = ImageLoader.getInstance();
public MapFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map, container, false);
isFirstLoc = 1;
isInit = true;
mapView = (MapView) v.findViewById(R.id.map);
mapView.onCreate(savedInstanceState);// 此方法必须重写
screenWidth = ScreenUtils.getScreenWidth(getActivity());
screenHeight = ScreenUtils.getScreenHeight(getActivity());
setUpMapIfNeeded();
return v;
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
deactivate();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
super.onDestroy();
//mapView.onDestroy();
}
@Override
public void onDestroyView() {
super.onDestroyView();
mapView.onDestroy();
}
private void setUpMapIfNeeded() {
((MainActivity) getActivity()).setFabVisible(0);
aMap = mapView.getMap();
uiSettings = aMap.getUiSettings();
aMap.setLocationSource(this);//设置定位监听,实现LocationSource的接口
aMap.setOnMarkerClickListener(this);// 设置点击marker事件监听器
aMap.setOnCameraChangeListener(this);// 对amap添加移动地图事件监听器
//是否显示定位按钮
uiSettings.setMyLocationButtonEnabled(true);
//设置缩放控件
uiSettings.setZoomGesturesEnabled(true);
//显示比例尺控件
uiSettings.setScaleControlsEnabled(true);
uiSettings.setZoomControlsEnabled(true);
//显示定位层并可以触发定位事件
aMap.setMyLocationEnabled(true);
//开始定位
location();
}
private void location() {
Log.d("定位测试", "location方法已经执行");
//初始化定位
aMapLocationClient = new AMapLocationClient(getActivity());
//设置定位回调监听
aMapLocationClient.setLocationListener(this);
//初始化定位参数
aMapLocationClientOption = new AMapLocationClientOption();
//设置定位模式为Hight_Accuracy高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式
aMapLocationClientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//设置是否返回地址信息(默认返回地址信息)
aMapLocationClientOption.setNeedAddress(true);
//设置是否只定位一次,默认为false
aMapLocationClientOption.setOnceLocation(false);
//设置是否强制刷新WIFI,默认为强制刷新
aMapLocationClientOption.setWifiActiveScan(true);
//设置是否允许模拟位置,默认为false,不允许模拟位置
aMapLocationClientOption.setMockEnable(false);
//设置定位间隔,单位毫秒,默认为2000ms
aMapLocationClientOption.setInterval(10000);
//给定位客户端对象设置定位参数
aMapLocationClient.setLocationOption(aMapLocationClientOption);
//启动定位
aMapLocationClient.startLocation();
}
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
//Log.d("定位回掉方法测试",aMapLocation.toString());
if (aMapLocation != null) {
if (aMapLocation.getErrorCode() == 0) {
//可在其中解析amapLocation获取相应内容。
lat = aMapLocation.getLatitude();//获取纬度
lon = aMapLocation.getLongitude();//获取经度
//获取定位时间
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(aMapLocation.getTime());
df.format(date);
Log.d("缩放标志位", "定位次数" + isFirstLoc);
// 如果不设置标志位,此时再拖动地图时,它会不断将地图移动到当前的位置
if (isFirstLoc == 1) {
//设置缩放级别(缩放级别为4-20级)
isFirstLoc = 0;
aMap.moveCamera(CameraUpdateFactory.zoomTo(15));
//将地图移动到定位点
aMap.moveCamera(CameraUpdateFactory.changeLatLng(new LatLng(lat, lon)));
//点击定位按钮 能够将地图的中心移动到定位点
mOnLocationChangedListener.onLocationChanged(aMapLocation);
}
if (null == markerLocal) {
//只有为空时才重绘,防止内存泄漏
addMarkerToMap(lat, lon);
}
} else {
//定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
Log.e("地图错误", "定位失败, 错误码:" + aMapLocation.getErrorCode() + ", 错误信息:"
+ aMapLocation.getErrorInfo());
}
}
}
//添加标志到地图上
private void addMarkerToMap(Double lat, Double lon) {
NetWorks.getAroundPosts(lat, lon, new Observer() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("MapFragment",e.getMessage());
}
@Override
public void onNext(PostLab postLab) {
Log.e("地图","成功获取数据");
if (markerOptionsListall != null) {
//每次重绘前清除数据
markerOptionsListall.clear();
}
posts = postLab.getmPosts();
for (Post post :posts) {
double tempLat = post.getmLat();
double tempLon = post.getmLon();
final LatLng mLatLng = new LatLng(tempLat, tempLon);
final String mTitle = post.getmTitle();
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.draggable(true);
markerOptions.icon(BitmapDescriptorFactory.defaultMarker());
View v = getActivity().getLayoutInflater()
.inflate(R.layout.custom_infowindow_content, null);
ImageView imageView = (ImageView) v.findViewById(R.id.custom_content_image);
TextView textView = (TextView) v.findViewById(R.id.custom_content_title);
imageLoader.displayImage(post.getmImageUrl(),
imageView, ImageLoaderOptionUtil.getOptions());
textView.setText(mTitle);
markerOptions.position(mLatLng);
markerOptions.icon(BitmapDescriptorFactory.fromView(v));
//markerOptions.title(mTitle);
markerOptionsListall.add(markerOptions);
markerLocal = aMap.addMarker(markerOptions);
}
}
});
}
/**
* 获取视野内的marker 根据聚合算法合成自定义的marker 显示视野内的marker
*/
private void resetMarks() {
// 开始刷新
Projection projection = aMap.getProjection();
Point p = null;
markerOptionsListInView.clear();
// 获取在当前视野内的marker;提高效率
for (MarkerOptions mp : markerOptionsListall) {
p = projection.toScreenLocation(mp.getPosition());
if (p.x < 0 || p.y < 0 || p.x > screenWidth || p.y > screenHeight) {
// 不添加到计算的列表中
} else {
markerOptionsListInView.add(mp);
}
}
// 自定义的聚合类MyMarkerCluster
ArrayList clustersMarker = new ArrayList<>();
for (MarkerOptions mp : markerOptionsListInView) {
if (clustersMarker.size() == 0) {
// 添加一个新的自定义marker
clustersMarker.add(new MarkerImageView(
getActivity(), mp, projection, 80));// 80=相距多少才聚合
} else {
boolean isIn = false;
for (MarkerImageView cluster : clustersMarker) {
// 判断当前的marker是否在前面marker的聚合范围内 并且每个marker只会聚合一次。
if (cluster.getBounds().contains(mp.getPosition())) {
cluster.addMarker(mp);
isIn = true;
break;
}
}
// 如果没在任何范围内,自己单独形成一个自定义marker。在和后面的marker进行比较
if (!isIn) {
clustersMarker.add(new MarkerImageView(
getActivity(), mp, projection, 80));// 80=相距多少才聚合
}
}
}
// 设置聚合点的位置和icon
for (MarkerImageView mmc : clustersMarker) {
mmc.setpositionAndIcon();
}
aMap.clear();
// 重新添加 marker
for (MarkerImageView cluster : clustersMarker) {
aMap.addMarker(cluster.getOptions());
}
}
@Override
protected void lazyLoad() {
if (null != aMap) {
//每次重新加载地图前,清除数据
aMap.clear();
markerLocal = null;
markerOptionsListall.clear();
markerOptionsListInView.clear();
}
setUpMapIfNeeded();
}
@Override
public void activate(OnLocationChangedListener onLocationChangedListener) {
mOnLocationChangedListener = onLocationChangedListener;
}
@Override
public void deactivate() {
mOnLocationChangedListener = null;
}
//marker点击事件
@Override
public boolean onMarkerClick(Marker marker) {
marker.hideInfoWindow();
LatLng latLng = marker.getPosition();
return false;
}
@Override
public void onCameraChange(CameraPosition cameraPosition) {
}
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
if (markerOptionsListall.size() != 0) {
resetMarks();
}
}
}
聚合标志工具类
public class MarkerImageView {
//上下文
private Context context;
//marker类
private MarkerOptions options;
//当前可观区域里的 聚合过之后的集合
private ArrayList includeMarkers = new ArrayList<>();
// 创建区域
private LatLngBounds bounds;
private int count = 0;
/**
* 头像加载完监听
*/
public MarkerImageView(Context context, MarkerOptions firstMarkers,
Projection projection, int gridSize) {
this.context = context;
options = new MarkerOptions();
Point point = projection.toScreenLocation(firstMarkers.getPosition());
//范围类
Point southwestPoint = new Point(point.x - gridSize, point.y + gridSize);
//范围类
Point northeastPoint = new Point(point.x + gridSize, point.y - gridSize);
bounds = new LatLngBounds(projection.fromScreenLocation(southwestPoint),
projection.fromScreenLocation(northeastPoint));
//设置初始化marker属性
options.anchor(0.5f, 0.5f).title(firstMarkers.getTitle()).position(firstMarkers.getPosition())
.icon(firstMarkers.getIcon())
.snippet(firstMarkers.getSnippet());
includeMarkers.add(firstMarkers);
Log.d("计数",""+count++);
}
public MarkerImageView(Context context) {
this.context = context;
}
public LatLngBounds getBounds() {
return bounds;
}
public MarkerOptions getOptions() {
return options;
}
public void setOptions(MarkerOptions options) {
this.options = options;
}
/**
* 添加marker
*/
public void addMarker(MarkerOptions markerOptions) {
includeMarkers.add(markerOptions);// 添加到列表中
}
/**
* 设置聚合点的中心位置以及图标
*/
public void setpositionAndIcon() {
int size = includeMarkers.size();
double lat = 0.0;
double lng = 0.0;
// 一个的时候
if (size == 1) {//设置marker单个属性
// 设置marker位置
Log.d("单个marker", "被使用");
options.position(new LatLng(
includeMarkers.get(0).getPosition().latitude,
includeMarkers.get(0).getPosition().longitude));
options.title("聚合点");
options.icon(BitmapDescriptorFactory
.fromBitmap(getViewBitmap(getView(size))));
} else {// 聚合的时候
//设置marker聚合属性
Log.d("多个marker", includeMarkers.size()+"个被使用");
for (MarkerOptions op : includeMarkers) {
lat += op.getPosition().latitude;
lng += op.getPosition().longitude;
}
// 设置marker的位置为中心位置为聚集点的平均位置
options.position(new LatLng(lat / size, lng / size));
options.title("聚合点");
options.icon(BitmapDescriptorFactory
.fromBitmap(getViewBitmap(getView(size))));
}
}
/**
* marker视图
*/
public View getView(int num) {
View view = LayoutInflater.from(context).inflate(
R.layout.view_gaode_img, null);
/** 数量 */
TextView txt_num = (TextView) view
.findViewById(R.id.view_gaode_txt_num);
/** 头像 */
ImageView img_portrait = (ImageView) view
.findViewById(R.id.view_gaode_img_portrait);
img_portrait.setPadding(8, 8, 8, 12);
if (num > 1) {
txt_num.setText(num + "");
} else if (num == 1) {
//在此处修改未聚合时候的显示方式
txt_num.setText(num + "");
img_portrait.setBackgroundResource(R.drawable.green_pin);
//overDraw();
}
return view;
}
/**
* 把一个view转化成bitmap对象
*/
public static Bitmap getViewBitmap(View view) {
Bitmap bitmap = null;
try {
if (view != null) {
view.measure(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(),
view.getMeasuredHeight());
view.buildDrawingCache();
bitmap = view.getDrawingCache();
}
} catch (Exception e) {
}
return bitmap;
}
}
值得注意的一点是,在for循环中向markerOptionsListall中添加markerOptions时,一定要每次循环都要声明并定义markOptions,因为类当参数传递时是直接使用的引用,如果不这样做,每次循环都会重用markOptions对象,导致一些莫名其妙的错误。
多点聚合主要参考这位大神的博客:这儿。