懶加載
因爲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跳轉可能會出現空指針錯誤。
高德地圖的具體實現
onCreateView()
@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;
}
完成地圖空間的初始化以及定位標識符的初始化,防止多次重複定位。高度與寬度用來後面的地圖多標誌聚合。
setUpMapIfNeeded()
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();
}
完成地圖一些基本的參數配置。
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();
}
配置定位參數並開始定位。
onLocationChanged()
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());
}
}
}
此方法是完成定位的回調方法,同時也是移動地圖的回調方法。再次方法裏獲取定位的結果並顯示在地圖上。
addMarkerToMap()
//添加標誌到地圖上
private void addMarkerToMap(Double lat, Double lon) {
NetWorks.getAroundPosts(lat, lon, new Observer<PostLab>() {
@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);
}
}
});
}
本方法的目的是根據經緯度信息將標記添加到地圖上,我是業務需要,從網絡獲取多個經緯度信息然後添加到地圖上。
LazyLoad()
protected void lazyLoad() {
if (null != aMap) {
//每次重新加載地圖前,清除數據
aMap.clear();
markerLocal = null;
markerOptionsListall.clear();
markerOptionsListInView.clear();
}
setUpMapIfNeeded();
}
整體代碼
MapFragment
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<MarkerOptions> markerOptionsListInView = new ArrayList<>();
//所有的marker
private ArrayList<MarkerOptions> markerOptionsListall = new ArrayList<>();
//markerOptionsListall初始化標誌位
private int isListallInit = 0;
//接收到的post集合
private List<Post> 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<PostLab>() {
@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<MarkerImageView> 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();
}
}
}
MarkerImageView
聚合標誌工具類
public class MarkerImageView {
//上下文
private Context context;
//marker類
private MarkerOptions options;
//當前可觀區域裏的 聚合過之後的集合
private ArrayList<MarkerOptions> 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對象,導致一些莫名其妙的錯誤。
多點聚合主要參考這位大神的博客:這兒。