算了,仍是聊今天咱們要說的,支付寶的「到位」功能。新版支付寶上方第四個tab,傳聞丈母孃足不出戶,同城招女婿的神兵利器。反正上面那些一塊錢看看花、逛逛街、喝喝酒的服務好單純好不作做<( ̄ˇ ̄)/。javascript
我是DEMO: github.com/CarGuo/LbsM… 用力戳♂起來。java
到位的主要特點是地圖加LBS功能,搜索周邊的服務和幫忙,既然是地圖,咱們就站在百度的肩膀來開車吧:android
首先你得有個KEY,在百度地圖API上註冊爲開發者後,你就能夠建立一個應用,記得選Android類型,由於默認是服務器類型。git
以後在AndroidManifest上加上下面這些(網絡、定位權限的就不須要列出了吧),最後在APPlication中初始化SDKInitializer.initialize(this);,這樣你的的MapView就能夠跑起來啦!github
<service
android:name="com.baidu.location.f"
android:enabled="true"
android:process=":remote" />
<meta-data
android:name="api_key"
android:value="CirU5l4MGVbE59tOduYDCO6TDX27CPzV" />
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="CirU5l4MGVbE59tOduYDCO6TDX27CPzV" />複製代碼
雖然用的是MapView,可是實際上操做的,是MapView裏面的BaiduMap。json
下方長♂代碼走起,流程是:比例尺;不要傾角;不要旋轉;設置最大和最小的縮放層級;初始化聚合管理器(後面都是他的事情);Marker(地圖上對應的item)管理器;設置顯示位置的圖標; 初始化咱們須要的搜索Model(經緯度,半徑,表id)用與保存搜索狀態。是否是很簡單,哇塞,我有一個地圖了。api
mBaiduMap = mBaiduMapView.getMap();
// 比例尺控件
mBaiduMapView.showScaleControl(true);
// 縮放控件
mBaiduMapView.showZoomControls(false);
// 百度地圖LoGo -> 正式版切記不能這麼作,本人只是以爲logo醜了
mBaiduMapView.removeViewAt(1);
//不傾斜
mBaiduMap.getUiSettings().setOverlookingGesturesEnabled(false);
//不旋轉
mBaiduMap.getUiSettings().setRotateGesturesEnabled(false);
//設置縮放層級
mBaiduMap.setMaxAndMinZoomLevel(19, 12);
//圖標管理器
mMarkerManager = new MarkerManager(mBaiduMap);
//聚合與渲染管理器
mClusterManager = new ClusterManager<>(this, mBaiduMap, mMarkerManager);
mBaiduMap.setOnMapStatusChangeListener(mClusterManager);
mBaiduMap.setOnMapLoadedCallback(this);
mBaiduMap.setMyLocationEnabled(true);
Bitmap bitmap = CommonUtil.getImageFromAssetsFile(DemoApplication.getApplication(), "current_location.png");
//調整位置圖片,相似百度地圖的小藍點
float scale = 0.80f;
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
mCLBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
bitmap.recycle();
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mCLBitmap);
MyLocationConfiguration myLocationConfiguration = new MyLocationConfiguration(MyLocationConfiguration.LocationMode.NORMAL, false, bitmapDescriptor);
mBaiduMap.setMyLocationConfigeration(myLocationConfiguration);
//顯示位置圖標的builder
MyLocationData locData = new MyLocationData.Builder()
.accuracy(0)
.direction(0).latitude(llat)
.longitude(llng).build();
//顯示位置圖標-珠海
mBaiduMap.setMyLocationData(locData);
//顯示等級-轉換:初始化爲mDefaultRadius半徑的層級用於顯示
float level = LocationLevelUtils.returnCurZoom(mDefaultRadius);
//當前地圖狀態
mCurrentMapStatus = new MapStatus.Builder().target(new LatLng(llat, llng)).zoom(level).build();
mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(mCurrentMapStatus));
//初始化數據搜索model
mSearchModel = new SearchModel();
mSearchModel.setGps(llng + "," + llat);
mSearchModel.setRadius(mDefaultRadius);
mSearchModel.setLevel(level);
mSearchModel.setTableId(DemoApplication.TABLE_ID());複製代碼
初始化好地圖,那麼咱們須要的是,在地圖上顯示圖標,而且將臨近的點聚合起來。百度地圖的DEMO已經實現了這一點,可是還不夠咱們的需求,這是時候咱們要修改Demo下,clusterutil路徑下的類,針對聚合與Marker的顯示作自定義處理。緩存
1)、ClusterItem服務器
這個接口表明着地圖上一個item,可是的百度Demo還不夠,因此在裏面加入新接口,用於顯示咱們的自定圖標,而後實現ClusterBaiduItem繼承ClusterItem,將須要顯示的經緯度和圖標信息,保存在Item裏。網絡
/** * 網絡的單個marker的實例 */
BitmapDescriptor getUrlMarkerIconBitmapDescriptor(boolean select);
/** * 網絡的單個marker的icon路徑 */
String getUrlLocalMarkerIconPath();
/** * 網絡的單個聚合的icon路徑 */
String getUrlClusterIconPath();複製代碼
2)、ClusterManager
這是聚合marker的管理器,內部有渲染類,將ICON渲染到地圖上,同時也包含了地圖狀態變化的接口,這裏咱們把地圖的狀態變化接口回調出來,方便咱們監聽地圖的移動和縮放。
同時對渲染類DefaultClusterRenderer也增長get接口,由於後面咱們須要,在外部動態改變Marker的圖標。地圖上渲染出來的Marker在Render渲染類中,會以ClusterItem爲KEY,緩存在MAP中。
//add myself 修改了地圖狀態變化的回調
public BaiduMap.OnMapStatusChangeListener onMapStatusChangeListener;
/** * add myself */
public DefaultClusterRenderer<T> getDefaultClusterRenderer() {
return (DefaultClusterRenderer) mRenderer;
}複製代碼
3)、NonHierarchicalDistanceBasedAlgorithm
這個類主要關注MAX_DISTANCE_AT_ZOOM,它表明着多遠的距離能夠聚合,這裏我的修改成200,反正我就不喜歡堆在一塊兒╮(╯_╰)╭。
4)、DefaultClusterRenderer
這是大頭,很大的頭,默認渲染類,固然你能夠本身實現,可是秉承着快速(懶)開發的原則,我是直接在上面修改的:
顯示Marker的修改,是將本來只顯示drawable的功能,擴展到支持加載本地圖標的支持,後面咱們只須要,把對應的Marker的圖標下載下來,就能夠顯示不一樣的網絡ICON啦。下方繼續長長長代碼(。・・)ノ。
//聚合背景修改成咱們要的效果
private LayerDrawable makeClusterBackground() {
//讀取聚合圖標
InputStream inputStream;
inputStream = context.getResources().openRawResource(R.raw.cluster);
BitmapDrawable drawable = new BitmapDrawable(inputStream);
mColoredCircleBackground = drawable;
//將外部的圈圈去掉
ShapeDrawable outline = new ShapeDrawable(new RoundRectShape(mOuterCircle, mInsertCircle, null));
outline.getPaint().setColor(0x00000000);
LayerDrawable background = new LayerDrawable(new Drawable[]{outline, mColoredCircleBackground});
//修改padding
int strokeWidth = (int) (mDensity * 3);
background.setLayerInset(1, strokeWidth, strokeWidth, strokeWidth, strokeWidth);
return background;
}
/** * 設置文本的樣式,修改成使用了TextView,不用百度的SquareTextView */
private TextView makeSquareTextView(Context context) {
TextView squareTextView =
new TextView(context);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams((int) (54 * mDensity), (int) (54 * mDensity));
squareTextView.setLayoutParams(layoutParams);
squareTextView.setId(R.id.text);
squareTextView.setGravity(Gravity.CENTER);
return squareTextView;
}
/** * 設置文本的數字顯示 */
protected String getClusterText(int bucket) {
if (bucket < BUCKETS[0]) {
return String.valueOf(bucket);
}
if (bucket > 999) {
bucket = 999;
return String.valueOf(bucket) + "+";
}
return String.valueOf(bucket);
}
//顯示渲染圖標,修改註釋部分代碼
private void perform(MarkerModifier markerModifier) {
if (!shouldRenderAsCluster(cluster)) {
/**若是此處沒有須要聚合的**/
for (T item : cluster.getItems()) {
Marker marker = mMarkerCache.get(item);
MarkerWithPosition markerWithPosition;
if (marker == null) {
MarkerOptions markerOptions = new MarkerOptions();
//markerOptions.animateType(MarkerOptions.MarkerAnimateType.grow);
/** * 下面就是marker的顯示 */
BitmapDescriptor bitmapDescriptor;
//若是有須要顯示的url icon的話,就顯示已下載的url Icon
if (!TextUtils.isEmpty(item.getUrlLocalMarkerIconPath()) && new File(item.getUrlLocalMarkerIconPath()).exists()) {
bitmapDescriptor = item.getUrlMarkerIconBitmapDescriptor(false);
if (bitmapDescriptor == null) {
bitmapDescriptor = item.getBitmapDescriptor();
}
} else {
bitmapDescriptor = item.getBitmapDescriptor();
}
···
onBeforeClusterItemRendered(item, markerOptions);
marker = mClusterManager.getMarkerCollection().addMarker(markerOptions);
markerWithPosition = new MarkerWithPosition(marker);
//根據 item 緩存marker
mMarkerCache.put(item, marker);
···
/**顯示聚合以前完善圖片信息**/
onBeforeClusterRendered(cluster, markerOptions);
····
}複製代碼
5)、IconGenerator
這個類主要是對應聚合Marker的,這裏修改了聚合圖標的大小,背景,文本樣式等等。其中setTextAppearance配置文本的樣式,如顏色,大小等,目前百度自帶Bubble.TextAppearance.Light和Bubble.TextAppearance.Dark,你也能夠本身配置你須要的。
首先,你仍是得有個女朋·····我呸,得是有個KEY,一樣是在百度地圖API,此次選擇的是服務端,建立應用後,最底部能夠選擇sn加密或者的ip白名單。
既然叫服務端,通常這種工做都是服務器去作的,客戶端只須要從服務端拿數據就好。可是有時候服務端沒空理你(沒錯,就是不被理會的我),你就須要直接從百度服務器扣了。
這裏採用的是sn加密,就是根據url,經過申請到的ak和sk,還有各類請求參數,進行utf8轉化和md5加密,具體有興趣能夠看看DEMO,雖然在客戶端這樣加密是沒有意義的。
那麼那咱們就開始請求數據啦:
api.map.baidu.com/geosearch/v…
上方式一次請求中完整的url,其中:
其餘的還有 有關鍵字q,標籤tag,排序sort,過濾等:
還有其餘的就在百度官網了,再說下去真的就廢話了。
請求到數據後,lbs對應返回咱們須要的json,咱們映射成須要的model,組裝ClusterBaiduItem並設置圖標,用於ClusterManager渲染。
可是!!!百度地圖Marker不支持url!這就尷尬了,因此咱們仍是乖乖本身下載吧┑( ̄Д  ̄)┍。
下載邏輯具體可看demo中的 ICONJob(真的好懶),主要的邏輯是:
這裏主要是動態更新Marker的圖標邏輯:
根據下載成功的ICON對應的id,找到對應的ClusterBaiduItem,經過ClusterBaiduItem,在ClusterManager的DefaultClusterRenderer中拿到marker,修改marker的圖標,爲咱們下載成功的圖片,對應的一一更新,哇塞,好簡單。
for (ClusterBaiduItem clusterBaiduItem : mClusterBaiduItems) {
LBSModel lbsModel = clusterBaiduItem.getLBAModel();
//此處根據id設置對應的圖片
if (lbsModel.getUid() == e.geteId()) {
BitmapDescriptor bitmapDescriptor;
if (!TextUtils.isEmpty(clusterBaiduItem.getUrlLocalMarkerIconPath()) &&
new File(clusterBaiduItem.getUrlLocalMarkerIconPath()).exists()) {
bitmapDescriptor = clusterBaiduItem.getUrlMarkerIconBitmapDescriptor(false);
if (bitmapDescriptor == null) {
bitmapDescriptor = clusterBaiduItem.getBitmapDescriptor();
}
} else {
bitmapDescriptor = clusterBaiduItem.getBitmapDescriptor();
}
//從聚合管理器裏面拿到marker,動態改變它
Marker marker = mClusterManager.getDefaultClusterRenderer().getMarker(clusterBaiduItem);
if (marker != null) {
marker.setIcon(bitmapDescriptor);
}
//刷新
mClusterManager.cluster();
return;
}
}複製代碼
在地圖移動和縮放的時候,由於地理信息改變了,須要更新數據,這時候能夠經過setOnMapStatusChangeListener來監聽地圖的變化。
mClusterManager.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {
//記住變化前的上一個狀態
private MapStatus mFrontMapStatus;
@Override
public void onMapStatusChangeStart(MapStatus mapStatus) {
if (mFrontMapStatus == null) {
mFrontMapStatus = mapStatus;
}
}
@Override
public void onMapStatusChangeFinish(MapStatus mapStatus) {
//此處須要注意,若是是進入的時候從新定位了地址,或者進入後在改變地圖狀態,可能也會進入這裏
if (mHadRequest) {
if (StatusChangeLogic(mFrontMapStatus, mapStatus)) {//處理移動與放大
mFrontMapStatus = null;
}
}
mCurrentMapStatus = mapStatus;
}
});
/** * 地圖由於操做而發生了狀態改變 */
private boolean StatusChangeLogic(MapStatus frontMapStatus, MapStatus mapStatus) {
//從新肯定搜索半徑的中心圖標
mSearchModel.setGps(mapStatus.bound.getCenter().longitude + "," + mapStatus.bound.getCenter().latitude);
//從新肯定層級
mSearchModel.setLevel(mapStatus.zoom);
if (frontMapStatus == null)
return false;
//獲得屏幕的距離大小
double areaLength1 = DistanceUtil.getDistance(mapStatus.bound.northeast, mapStatus.bound.southwest);
//計算屏幕的大小半徑
int radius = (int) areaLength1 / 2;
//從新肯定搜索的半徑
mSearchModel.setRadius(radius);
if (frontMapStatus.zoom == mapStatus.zoom) {
if (frontMapStatus.bound == null)
return false;
//若是是移動了,獲得距離
double moveLenght = DistanceUtil.getDistance(frontMapStatus.bound.getCenter(), mapStatus.bound.getCenter());
//若是移動距離大於屏幕的檢索半徑,請求數據
if (moveLenght >= radius) {
RequestNewDataLogic(true, true);
return true;
}
//若是經緯度發生變化了,通常都是切換的城市之類的
if (mChangeStatus != null && (mapStatus.target.latitude) != (int) (mChangeStatus.target.latitude)
&& (int) (mapStatus.target.longitude) != (int) (mChangeStatus.target.longitude) && mIsChangeCity) {
RequestNewDataLogic(true, true);
mIsChangeCity = false;
return true;
}
return false;
} else {
//若是是縮放的話,地圖層級發生改變,從新請求數據
RequestNewDataLogic(true, true);
return true;
}
}複製代碼
效果:
點擊放大,其實也是動態改變marker的圖標,根據ClusterBiaduItem拿到對應的marker,設置爲大圖選中的Bitmap,廢話不說,下方擼碼。
//恢復上一個點擊爲正常狀態
if (mPreClickItem != null) {
mPreClickItem.setBitmapId(R.drawable.default_map_icon);
BitmapDescriptor bitmapDescriptor;
//是否已經下載了ICON
if (!TextUtils.isEmpty(mPreClickItem.getUrlLocalMarkerIconPath()) &&
new File(mPreClickItem.getUrlLocalMarkerIconPath()).exists()) {
bitmapDescriptor = mPreClickItem.getUrlMarkerIconBitmapDescriptor(false);
if (bitmapDescriptor == null) {
bitmapDescriptor = mPreClickItem.getBitmapDescriptor();
}
} else {
bitmapDescriptor = mPreClickItem.getBitmapDescriptor();
}
//從聚合管理器裏面拿到marker,動態改變它
Marker marker = mClusterManager.getDefaultClusterRenderer().getMarker(mPreClickItem);
if (marker != null) {
marker.setIcon(bitmapDescriptor);
}
}
//設置新的點擊爲大圖狀態
if (clusterBaiduItem != null) {
clusterBaiduItem.setBitmapId(R.drawable.default_map_icon_big);
BitmapDescriptor bitmapDescriptor;
//是否已經下載了ICON
if (!TextUtils.isEmpty(clusterBaiduItem.getUrlLocalMarkerIconPath()) &&
new File(clusterBaiduItem.getUrlLocalMarkerIconPath()).exists()) {
bitmapDescriptor = clusterBaiduItem.getUrlMarkerIconBitmapDescriptor(true);
if (bitmapDescriptor == null) {
bitmapDescriptor = clusterBaiduItem.getBitmapDescriptor();
}
} else {
bitmapDescriptor = clusterBaiduItem.getBitmapDescriptor();
}
//從聚合管理器裏面拿到marker,動態改變它
Marker marker = mClusterManager.getDefaultClusterRenderer().getMarker(clusterBaiduItem);
if (marker != null) {
marker.setIcon(bitmapDescriptor);
}
//刷新
mClusterManager.cluster();
}
mPreClickItem = clusterBaiduItem;複製代碼
效果:
點擊聚合展開,其實很簡單!<( ̄︶ ̄)>,將聚合的ClusterBaiduItem,用LatLngBounds.Builder ,將經緯度收集起來,以後經過MapStatusUpdateFactory生成新的mapstatus,BiaduMap使用animateMapStatus便可展開咯。
if (mBaiduMap == null) {
return;
}
if (clusterBaiduItems.getItems().size() > 0) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (ClusterBaiduItem clusterBaiduItem : clusterBaiduItems.getItems()) {
builder.include(clusterBaiduItem.getPosition());
}
mBaiduMap.animateMapStatus(MapStatusUpdateFactory
.newLatLngBounds(builder.build()));
}複製代碼
這裏大體上就是實現到位圖標的粗糙邏輯,最後仍是經過DEMO擼起來,會更有感受<( ̄ˇ ̄)/。
我是DEMO: github.com/CarGuo/LbsM… 用戶戳♂起來。
我的Github:github.com/CarGuo