LZ-Says:前段時(shí)間更新了一篇關(guān)于高德地圖的簡(jiǎn)單使用,其中主要包含實(shí)現(xiàn)地圖顯示以及定位簡(jiǎn)單功能,具體文章如下:
Android Study 之玩轉(zhuǎn)高德地圖一部曲[實(shí)現(xiàn)顯示地圖以及定位功能]
寫(xiě)這個(gè)的<font color=#FF0000>目的</font>有倆個(gè),一個(gè)是對(duì)自己的一個(gè)<font color=#FF0000>記錄,想看的時(shí)候隨時(shí)能點(diǎn)擊看看</font>;二就是針對(duì)和LZ一樣的人群,<font color=#FF0000>簡(jiǎn)單快捷高效實(shí)現(xiàn)地圖開(kāi)發(fā),我走過(guò)的坑,在坑上浪費(fèi)的時(shí)間,大家看了之后就能多少避免一些。</font>
寫(xiě)文章,方便自己,共享他人,相互學(xué)習(xí),相互交流,共同進(jìn)步,何樂(lè)不為?
本文意在快速實(shí)現(xiàn)高德地圖POI檢索附近,具體官方文檔以及官方GitHub都已經(jīng)有相關(guān)demo,LZ只是把自己的開(kāi)發(fā)過(guò)程敘述下而已,希望大家都有自己的收獲~
下面為大家展示先最終實(shí)現(xiàn)效果:
POI簡(jiǎn)介
POI,又稱(chēng)Point of Interest(興趣點(diǎn))。在地圖表達(dá)中,一個(gè) POI 可代表一棟大廈、一家商鋪、一處景點(diǎn)等等。通過(guò)POI搜索,完成找餐館、找景點(diǎn)、找?guī)鹊鹊墓δ堋?/p>
主要細(xì)化為4個(gè)部分,如下:
關(guān)鍵字檢索POI
根據(jù)關(guān)鍵字檢索適用于在某個(gè)城市搜索某個(gè)名稱(chēng)相關(guān)的POI,例如:查找北京市的“肯德基”。
<font color=#FF0000>注意:
1、關(guān)鍵字未設(shè)置城市信息(默認(rèn)為全國(guó)搜索)時(shí),如果涉及多個(gè)城市數(shù)據(jù)返回,僅會(huì)返回建議城市,請(qǐng)根據(jù)APP需求,選取城市進(jìn)行搜索。
2、不設(shè)置POI的類(lèi)別,默認(rèn)返回“餐飲服務(wù)”、“商務(wù)住宅”、“生活服務(wù)”這三種類(lèi)別的POI,下方提供了POI分類(lèi)碼表,請(qǐng)按照列表內(nèi)容設(shè)置希望檢索的POI類(lèi)型。(建議使用POI類(lèi)型的代碼進(jìn)行檢索)周邊檢索POI
適用于搜索某個(gè)位置附近的POI,可設(shè)置POI的類(lèi)別,具體查詢(xún)所在位置的餐飲類(lèi)、住宅類(lèi)POI,例如:查找天安門(mén)附近的廁所等等場(chǎng)景。
<font color=#FF0000>與關(guān)鍵字檢索的唯一區(qū)別需要通過(guò) PoiSearch 的 setBound 方法設(shè)置圓形查詢(xún)范圍多邊形內(nèi)檢索的POI
不同于周邊搜索,周邊搜索是一個(gè)圓形范圍,而多邊形搜索的范圍是一個(gè)多邊形,適用于在搜索某個(gè)不規(guī)則區(qū)域的POI查詢(xún),例如:查找中關(guān)村范圍內(nèi)的停車(chē)場(chǎng)。ID檢索POI
通過(guò)關(guān)鍵字檢索、周邊檢索以及多邊形檢索,或者任意形式得到的高德POI ID信息,可通過(guò)ID檢索來(lái)獲取POI完整詳細(xì)信息。輸入內(nèi)容自動(dòng)提示
輸入提示是指根據(jù)用戶(hù)輸入的關(guān)鍵詞,給出相應(yīng)的提示信息,將最有可能的搜索詞呈現(xiàn)給用戶(hù),以減少用戶(hù)輸入信息,提升用戶(hù)體驗(yàn)。如:輸入“方恒”,提示“方恒國(guó)際中心A座”,“方恒購(gòu)物中心”等道路沿途檢索POI
從搜索 SDK 3.5.0 版本開(kāi)始支持,可查詢(xún)路徑沿途的加油站、ATM、汽修店、廁所
接下來(lái),帶大家一起玩轉(zhuǎn)POI檢索附近~讓你欲罷不能,so easy~
話不多說(shuō),開(kāi)干
既然要實(shí)現(xiàn)POI檢索附近,那首要肯定是引入依賴(lài),如下:
compile 'com.amap.api:search:latest.integration'
接下來(lái),為大家展示下我們實(shí)現(xiàn)后的界面效果,方便我們?nèi)?xiě)我們的layout文件,如下圖:
在中間部分,也就是上圖中 住宅區(qū) 學(xué)校 樓宇 商場(chǎng) 部分,高德官方自定義了一個(gè)控件,直接繼承于RadioGroup,代碼如下:
package cn.hlq.gaodemapdemo.weight;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import cn.hlq.gaodemapdemo.R;
/**
* Created by HLQ on 2017/5/15
*/
public class SegmentedGroup extends RadioGroup {
private int mMarginDp;
private Resources resources;
private int mTintColor;
private int mCheckedTextColor = Color.WHITE;
private LayoutSelector mLayoutSelector;
private Float mCornerRadius;
public SegmentedGroup(Context context) {
super(context);
resources = getResources();
mTintColor = resources.getColor(R.color.color_android_indicator_text);
mMarginDp = (int) getResources().getDimension(R.dimen.dp_1);
mCornerRadius = getResources().getDimension(R.dimen.dp_5);
mLayoutSelector = new LayoutSelector(mCornerRadius);
}
/* Reads the attributes from the layout */
private void initAttrs(AttributeSet attrs) {
TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(
attrs,
R.styleable.SegmentedGroup,
0, 0);
try {
mMarginDp = (int) typedArray.getDimension(
R.styleable.SegmentedGroup_sc_border_width,
getResources().getDimension(R.dimen.dp_1));
mCornerRadius = typedArray.getDimension(
R.styleable.SegmentedGroup_sc_corner_radius,
getResources().getDimension(R.dimen.dp_5));
mTintColor = typedArray.getColor(
R.styleable.SegmentedGroup_sc_tint_color,
getResources().getColor(R.color.color_android_indicator_text));
mCheckedTextColor = typedArray.getColor(
R.styleable.SegmentedGroup_sc_checked_text_color,
getResources().getColor(android.R.color.white));
} finally {
typedArray.recycle();
}
}
public SegmentedGroup(Context context, AttributeSet attrs) {
super(context, attrs);
resources = getResources();
mTintColor = resources.getColor(R.color.color_android_indicator_text);
mMarginDp = (int) getResources().getDimension(R.dimen.dp_1);
mCornerRadius = getResources().getDimension(R.dimen.dp_5);
initAttrs(attrs);
mLayoutSelector = new LayoutSelector(mCornerRadius);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//Use holo light for default
updateBackground();
}
public void setTintColor(int tintColor) {
mTintColor = tintColor;
updateBackground();
}
public void setTintColor(int tintColor, int checkedTextColor) {
mTintColor = tintColor;
mCheckedTextColor = checkedTextColor;
updateBackground();
}
public void updateBackground() {
int count = super.getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
updateBackground(child);
// If this is the last view, don't set LayoutParams
if (i == count - 1) break;
LayoutParams initParams = (LayoutParams) child.getLayoutParams();
LayoutParams params = new LayoutParams(initParams.width, initParams.height, initParams.weight);
// Check orientation for proper margins
if (getOrientation() == LinearLayout.HORIZONTAL) {
params.setMargins(0, 0, -mMarginDp, 0);
} else {
params.setMargins(0, 0, 0, -mMarginDp);
}
child.setLayoutParams(params);
}
}
private void updateBackground(View view) {
int checked = mLayoutSelector.getSelected();
int unchecked = mLayoutSelector.getUnselected();
//Set text color
ColorStateList colorStateList = new ColorStateList(new int[][]{
{android.R.attr.state_pressed},
{-android.R.attr.state_pressed, -android.R.attr.state_checked},
{-android.R.attr.state_pressed, android.R.attr.state_checked}},
new int[]{Color.GRAY, mTintColor, mCheckedTextColor});
((Button) view).setTextColor(colorStateList);
//Redraw with tint color
Drawable checkedDrawable = resources.getDrawable(checked).mutate();
Drawable uncheckedDrawable = resources.getDrawable(unchecked).mutate();
((GradientDrawable) checkedDrawable).setColor(mTintColor);
((GradientDrawable) checkedDrawable).setStroke(mMarginDp, mTintColor);
((GradientDrawable) uncheckedDrawable).setStroke(mMarginDp, mTintColor);
//Set proper radius
((GradientDrawable) checkedDrawable).setCornerRadii(mLayoutSelector.getChildRadii(view));
((GradientDrawable) uncheckedDrawable).setCornerRadii(mLayoutSelector.getChildRadii(view));
//Create drawable
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{-android.R.attr.state_checked}, uncheckedDrawable);
stateListDrawable.addState(new int[]{android.R.attr.state_checked}, checkedDrawable);
//Set button background
if (Build.VERSION.SDK_INT >= 16) {
view.setBackground(stateListDrawable);
} else {
view.setBackgroundDrawable(stateListDrawable);
}
}
/*
* This class is used to provide the proper layout based on the view.
* Also provides the proper radius for corners.
* The layout is the same for each selected left/top middle or right/bottom button.
* float tables for setting the radius via Gradient.setCornerRadii are used instead
* of multiple xml drawables.
*/
private class LayoutSelector {
private int children;
private int child;
private final int SELECTED_LAYOUT = R.drawable.shape_gaode_check;
private final int UNSELECTED_LAYOUT = R.drawable.shape_gaode_uncheck;
private float r; //this is the radios read by attributes or xml dimens
private final float r1 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
, 0.1f, getResources().getDisplayMetrics()); //0.1 dp to px
private final float[] rLeft; // left radio button
private final float[] rRight; // right radio button
private final float[] rMiddle; // middle radio button
private final float[] rDefault; // default radio button
private final float[] rTop; // top radio button
private final float[] rBot; // bot radio button
private float[] radii; // result radii float table
public LayoutSelector(float cornerRadius) {
children = -1; // Init this to force setChildRadii() to enter for the first time.
child = -1; // Init this to force setChildRadii() to enter for the first time
r = cornerRadius;
rLeft = new float[]{r, r, r1, r1, r1, r1, r, r};
rRight = new float[]{r1, r1, r, r, r, r, r1, r1};
rMiddle = new float[]{r1, r1, r1, r1, r1, r1, r1, r1};
rDefault = new float[]{r, r, r, r, r, r, r, r};
rTop = new float[]{r, r, r, r, r1, r1, r1, r1};
rBot = new float[]{r1, r1, r1, r1, r, r, r, r};
}
private int getChildren() {
return SegmentedGroup.this.getChildCount();
}
private int getChildIndex(View view) {
return SegmentedGroup.this.indexOfChild(view);
}
private void setChildRadii(int newChildren, int newChild) {
// If same values are passed, just return. No need to update anything
if (children == newChildren && child == newChild)
return;
// Set the new values
children = newChildren;
child = newChild;
// if there is only one child provide the default radio button
if (children == 1) {
radii = rDefault;
} else if (child == 0) { //left or top
radii = (getOrientation() == LinearLayout.HORIZONTAL) ? rLeft : rTop;
} else if (child == children - 1) { //right or bottom
radii = (getOrientation() == LinearLayout.HORIZONTAL) ? rRight : rBot;
} else { //middle
radii = rMiddle;
}
}
/* Returns the selected layout id based on view */
public int getSelected() {
return SELECTED_LAYOUT;
}
/* Returns the unselected layout id based on view */
public int getUnselected() {
return UNSELECTED_LAYOUT;
}
/* Returns the radii float table based on view for Gradient.setRadii()*/
public float[] getChildRadii(View view) {
int newChildren = getChildren();
int newChild = getChildIndex(view);
setChildRadii(newChildren, newChild);
return radii;
}
}
}
同時(shí)我們也需要去創(chuàng)建自定義控件的樣式文件,方便我們對(duì)其屬性進(jìn)行拓展,如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SegmentedGroup">
<attr name="sc_corner_radius" format="dimension"/>
<attr name="sc_border_width" format="dimension"/>
<attr name="sc_tint_color" format="color"/>
<attr name="sc_checked_text_color" format="color"/>
</declare-styleable>
</resources>
接下來(lái),去編寫(xiě)我們的布局文件,如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_while"
android:orientation="vertical"
tools:context="cn.hlq.gaodemapdemo.map.PoiSearchActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp_5"
android:focusable="true"
android:orientation="horizontal">
<AutoCompleteTextView
android:id="@+id/id_gaode_location_poi_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/shape_circle_while_bg"
android:completionThreshold="1"
android:dropDownVerticalOffset="1.0dip"
android:focusable="true"
android:gravity="center_vertical"
android:hint="@string/string_gaode_location_search_hint"
android:imeOptions="actionDone"
android:inputType="text|textAutoComplete"
android:maxLength="20"
android:padding="@dimen/dp_5"
android:singleLine="true"
android:textColor="@color/color_c6"
android:textColorHint="@color/color_c9"
android:textSize="@dimen/sp_14"/>
</LinearLayout>
<com.amap.api.maps.MapView
android:id="@+id/id_gaode_location_map"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_0"
android:layout_weight="1.2"/>
<cn.hlq.gaodemapdemo.weight.SegmentedGroup
xmlns:segmentedgroup="http://schemas.android.com/apk/res-auto"
android:id="@+id/id_gaode_location_segmented_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="4dp"
segmentedgroup:sc_border_width="1dp"
segmentedgroup:sc_corner_radius="2dp">
<RadioButton
android:id="@+id/id_gaode_location_uptown"
style="@style/style_gaode_search_title"
android:checked="true"
android:text="@string/string_gaode_location_title_uptown"/>
<RadioButton
android:id="@+id/id_gaode_location_school"
style="@style/style_gaode_search_title"
android:text="@string/string_gaode_location_title_school"/>
<RadioButton
android:id="@+id/id_gaode_location_building"
style="@style/style_gaode_search_title"
android:text="@string/string_gaode_location_title_building"/>
<RadioButton
android:id="@+id/id_gaode_location_shopping"
style="@style/style_gaode_search_title"
android:text="@string/string_gaode_location_title_shopping"/>
</cn.hlq.gaodemapdemo.weight.SegmentedGroup>
<ListView
android:id="@+id/id_gaode_location_list"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_0"
android:layout_weight="1"
android:overScrollMode="never"
android:scrollbars="none"/>
</LinearLayout>
最下方的搜索結(jié)果,大家可直接把其看定為一個(gè)ListView,既然是ListView了,肯定會(huì)有item布局,仔細(xì)觀看圖,item布局文件是不是so-easy呢?
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_while"
android:gravity="center_vertical"
android:orientation="vertical"
android:padding="@dimen/dp_15">
<ImageView
android:id="@+id/id_gaode_location_search_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@drawable/img_gaode_location_search_icon"
android:scaleType="fitXY"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_5"
android:layout_toEndOf="@+id/id_gaode_location_search_icon"
android:layout_toStartOf="@+id/id_gaode_location_search_confirm_icon"
android:orientation="vertical">
<TextView
android:id="@+id/id_gaode_location_search_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_c6"
android:textSize="@dimen/sp_14"/>
<TextView
android:id="@+id/id_gaode_location_search_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_c9"
android:textSize="@dimen/sp_14"/>
</LinearLayout>
<ImageView
android:id="@+id/id_gaode_location_search_confirm_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/icon_gaode_location_confirm_icon"
android:scaleType="fitXY"/>
</RelativeLayout>
item布局文件設(shè)置完畢后,緊接著,肯定要完善咱的Adapter類(lèi)嘍,不多說(shuō),上碼:
package cn.hlq.gaodemapdemo.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.amap.api.services.core.PoiItem;
import java.util.ArrayList;
import java.util.List;
import cn.hlq.gaodemapdemo.R;
/**
* Created by HLQ on 2017/5/15
*/
public class GaoDeSearchResultAdapter extends BaseAdapter {
private List<PoiItem> data;
private Context context;
private int selectedPosition = 0;
public GaoDeSearchResultAdapter(Context context) {
this.context = context;
data = new ArrayList<>();
}
public void setData(List<PoiItem> data) {
this.data = data;
}
public void setSelectedPosition(int selectedPosition) {
this.selectedPosition = selectedPosition;
}
public int getSelectedPosition() {
return selectedPosition;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_gaode_location_search_info, parent, false);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.bindView(position);
return convertView;
}
class ViewHolder {
TextView textTitle;
TextView textSubTitle;
ImageView imageCheck;
public ViewHolder(View view) {
textTitle = (TextView) view.findViewById(R.id.id_gaode_location_search_title);
textSubTitle = (TextView) view.findViewById(R.id.id_gaode_location_search_content);
imageCheck = (ImageView) view.findViewById(R.id.id_gaode_location_search_confirm_icon);
}
public void bindView(int position) {
if (position >= data.size())
return;
PoiItem poiItem = data.get(position);
textTitle.setText(poiItem.getTitle());
textSubTitle.setText(poiItem.getCityName() + poiItem.getAdName() + poiItem.getSnippet());
imageCheck.setVisibility(position == selectedPosition ? View.VISIBLE : View.INVISIBLE);
textSubTitle.setVisibility((position == 0 && poiItem.getPoiId().equals("regeo")) ? View.GONE : View.VISIBLE);
}
}
}
前期準(zhǔn)備工作完善之后,接下來(lái)就是我們的重中之重了。
開(kāi)始前,不妨讓我們?nèi)ダ砝硭悸罚米屛覀兏奖憧旖莸娜]碼。其實(shí)個(gè)人感覺(jué),重要的也就是下面?zhèn)z個(gè)部分,大體了解下,結(jié)合開(kāi)發(fā)文檔以及提供示例,差不多就可以開(kāi)搞了~
step 1:初始化地圖,包括初始化地圖相關(guān)屬性設(shè)置以及地圖的監(jiān)聽(tīng)事件;
step 2:輸入內(nèi)容自動(dòng)提示,我們所需要的步驟如下:
1、繼承 InputtipsListener 監(jiān)聽(tīng);
2、構(gòu)造 InputtipsQuery 對(duì)象,通過(guò)
InputtipsQuery(java.lang.String keyword, java.lang.String city)
3、構(gòu)造 Inputtips 對(duì)象,并設(shè)置監(jiān)聽(tīng);
4、調(diào)用 PoiSearch 的 requestInputtipsAsyn() 方法發(fā)送請(qǐng)求;
5、通過(guò)回調(diào)接口 onGetInputtips解析返回的結(jié)果,獲取輸入提示返回的信息。
其中我們需要注意的點(diǎn)如下:
<font color=#FF0000> a 、由于提示中會(huì)出現(xiàn)相同的關(guān)鍵字,但是這些關(guān)鍵字所在區(qū)域不同,使用時(shí)可以通過(guò) tipList.get(i).getDistrict() 獲得區(qū)域,也可以在提示時(shí)在關(guān)鍵字后加上區(qū)域。
b、當(dāng) Tip 的 getPoiID() 返回空,并且 getPoint() 也返回空時(shí),表示該提示詞不是一個(gè)真實(shí)存在的 POI,這時(shí)區(qū)域、經(jīng)緯度參數(shù)都是空的,此時(shí)可根據(jù)該提示詞進(jìn)行POI關(guān)鍵詞搜索
c、當(dāng) Tip 的 getPoiID() 返回不為空,但 getPoint() 返回空時(shí),表示該提示詞是一個(gè)公交線路名稱(chēng),此時(shí)用這個(gè)id進(jìn)行公交線路查詢(xún)。
d、當(dāng) Tip 的 getPoiID() 返回不為空,且 getPoint() 也不為空時(shí),表示該提示詞一個(gè)真實(shí)存在的POI,可直接顯示在地圖上。
package cn.hlq.gaodemapdemo.map;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.ListView;
import android.widget.RadioGroup;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.LocationSource;
import com.amap.api.maps.MapView;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.animation.Animation;
import com.amap.api.maps.model.animation.TranslateAnimation;
import com.amap.api.services.core.AMapException;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.core.PoiItem;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.amap.api.services.geocoder.RegeocodeQuery;
import com.amap.api.services.geocoder.RegeocodeResult;
import com.amap.api.services.help.Inputtips;
import com.amap.api.services.help.InputtipsQuery;
import com.amap.api.services.help.Tip;
import com.amap.api.services.poisearch.PoiResult;
import com.amap.api.services.poisearch.PoiSearch;
import java.util.ArrayList;
import java.util.List;
import cn.hlq.gaodemapdemo.R;
import cn.hlq.gaodemapdemo.adapter.GaoDeSearchResultAdapter;
import cn.hlq.gaodemapdemo.weight.SegmentedGroup;
/**
* create by heliquan at 2017年5月10日14:39:10
* 實(shí)現(xiàn)高德地圖POI檢索附近
*/
public class PoiSearchActivity extends Activity implements LocationSource,
AMapLocationListener, GeocodeSearch.OnGeocodeSearchListener, PoiSearch.OnPoiSearchListener {
private PoiSearchActivity self = this;
private SegmentedGroup mSegmentedGroup;
private AutoCompleteTextView searchText;
private AMap aMap;
private MapView mapView;
private Marker locationMarker;
private AMapLocationClient mlocationClient;
private LatLonPoint searchLatlonPoint;
private AMapLocationClientOption mLocationOption;
private GeocodeSearch geocoderSearch;
// Poi查詢(xún)條件類(lèi)
private PoiSearch.Query query;
private PoiSearch poiSearch;
private PoiItem firstItem;
private OnLocationChangedListener mListener;
// 當(dāng)前頁(yè)面,從0開(kāi)始計(jì)數(shù)
private int currentPage = 0;
private boolean isfirstinput = true;
private boolean isItemClickAction;
private boolean isInputKeySearch;
private String inputSearchKey;
private String searchKey = "";
private String[] items = {"住宅區(qū)", "學(xué)校", "樓宇", "商場(chǎng)"};
private String searchType = items[0];
// 記錄用戶(hù)點(diǎn)擊定位地址
private String saveClickLocationAddress = "";
// poi數(shù)據(jù)
private List<PoiItem> poiItems;
private List<Tip> autoTips;
private List<PoiItem> resultData;
private ListView listView;
private GaoDeSearchResultAdapter searchResultAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
setContentView(R.layout.activity_poi_search);
mapView = (MapView) findViewById(R.id.id_gaode_location_map);
mapView.onCreate(savedInstanceState);
initGaoDeMapListener();
initView();
resultData = new ArrayList<>();
}
protected void initView() {
listView = (ListView) findViewById(R.id.id_gaode_location_list);
searchResultAdapter = new GaoDeSearchResultAdapter(self);
listView.setAdapter(searchResultAdapter);
listView.setOnItemClickListener(onItemClickListener);
mSegmentedGroup = (SegmentedGroup) findViewById(R.id.id_gaode_location_segmented_group);
mSegmentedGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
searchType = items[0];
switch (checkedId) {
case R.id.id_gaode_location_uptown:
searchType = items[0];
break;
case R.id.id_gaode_location_school:
searchType = items[1];
break;
case R.id.id_gaode_location_building:
searchType = items[2];
break;
case R.id.id_gaode_location_shopping:
searchType = items[3];
break;
}
geoAddress();
}
});
searchText = (AutoCompleteTextView) findViewById(R.id.id_gaode_location_poi_search);
searchText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String newText = s.toString().trim();
if (newText.length() > 0) {
InputtipsQuery inputquery = new InputtipsQuery(newText, "北京");
Inputtips inputTips = new Inputtips(self, inputquery);
inputquery.setCityLimit(true);
inputTips.setInputtipsListener(inputtipsListener);
inputTips.requestInputtipsAsyn();
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
searchText.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (autoTips != null && autoTips.size() > position) {
Tip tip = autoTips.get(position);
searchPoi(tip);
}
}
});
geocoderSearch = new GeocodeSearch(this);
geocoderSearch.setOnGeocodeSearchListener(this);
hideSoftKey(searchText);
}
/**
* 初始化高德地圖監(jiān)聽(tīng)
*/
private void initGaoDeMapListener() {
if (aMap == null) {
aMap = mapView.getMap();
setUpMap();
}
aMap.setOnCameraChangeListener(new AMap.OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition cameraPosition) {
}
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
if (!isItemClickAction && !isInputKeySearch) {
geoAddress();
startJumpAnimation();
}
searchLatlonPoint = new LatLonPoint(cameraPosition.target.latitude, cameraPosition.target.longitude);
isInputKeySearch = false;
isItemClickAction = false;
}
});
aMap.setOnMapLoadedListener(new AMap.OnMapLoadedListener() {
@Override
public void onMapLoaded() {
addMarkerInScreenCenter(null);
}
});
}
/**
* 設(shè)置一些amap的屬性
*/
private void setUpMap() {
aMap.getUiSettings().setZoomControlsEnabled(false);
// 設(shè)置地圖默認(rèn)的指南針是否顯示
aMap.getUiSettings().setCompassEnabled(true);
// 設(shè)置定位監(jiān)聽(tīng)
aMap.setLocationSource(this);
// 設(shè)置默認(rèn)定位按鈕是否顯示
aMap.getUiSettings().setMyLocationButtonEnabled(true);
// 設(shè)置為true表示顯示定位層并可觸發(fā)定位,false表示隱藏定位層并不可觸發(fā)定位,默認(rèn)是false
aMap.setMyLocationEnabled(true);
aMap.setMyLocationType(AMap.LOCATION_TYPE_LOCATE);
}
@Override
protected void onResume() {
super.onResume();
mapView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mapView.onPause();
deactivate();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
if (null != mlocationClient) {
mlocationClient.onDestroy();
}
}
/**
* 定位成功后回調(diào)函數(shù)
*/
@Override
public void onLocationChanged(AMapLocation amapLocation) {
if (mListener != null && amapLocation != null) {
if (amapLocation != null
&& amapLocation.getErrorCode() == 0) {
mListener.onLocationChanged(amapLocation);
LatLng curLatlng = new LatLng(amapLocation.getLatitude(), amapLocation.getLongitude());
aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 17f));
searchLatlonPoint = new LatLonPoint(curLatlng.latitude, curLatlng.longitude);
isInputKeySearch = false;
searchText.setText("");
}
}
}
/**
* 激活定位
*/
@Override
public void activate(OnLocationChangedListener listener) {
mListener = listener;
if (mlocationClient == null) {
mlocationClient = new AMapLocationClient(this);
mLocationOption = new AMapLocationClientOption();
// 設(shè)置定位監(jiān)聽(tīng)
mlocationClient.setLocationListener(this);
// 設(shè)置為高精度定位模式
mLocationOption.setOnceLocation(true);
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
// 設(shè)置定位參數(shù)
mlocationClient.setLocationOption(mLocationOption);
// 此方法為每隔固定時(shí)間會(huì)發(fā)起一次定位請(qǐng)求,為了減少電量消耗或網(wǎng)絡(luò)流量消耗,
// 注意設(shè)置合適的定位時(shí)間的間隔(最小間隔支持為2000ms),并且在合適時(shí)間調(diào)用stopLocation()方法來(lái)取消定位請(qǐng)求
// 在定位結(jié)束后,在合適的生命周期調(diào)用onDestroy()方法
// 在單次定位情況下,定位無(wú)論成功與否,都無(wú)需調(diào)用stopLocation()方法移除請(qǐng)求,定位sdk內(nèi)部會(huì)移除
mlocationClient.startLocation();
}
}
/**
* 停止定位
*/
@Override
public void deactivate() {
mListener = null;
if (mlocationClient != null) {
mlocationClient.stopLocation();
mlocationClient.onDestroy();
}
mlocationClient = null;
}
/**
* 響應(yīng)逆地理編碼
*/
public void geoAddress() {
searchText.setText("");
// 第一個(gè)參數(shù)表示一個(gè)Latlng,第二參數(shù)表示范圍多少米,第三個(gè)參數(shù)表示是火系坐標(biāo)系還是GPS原生坐標(biāo)系
RegeocodeQuery query = new RegeocodeQuery(searchLatlonPoint, 200, GeocodeSearch.AMAP);
geocoderSearch.getFromLocationAsyn(query);
}
/**
* 開(kāi)始進(jìn)行poi搜索
*/
protected void doSearchQuery() {
currentPage = 0;
// 第一個(gè)參數(shù)表示搜索字符串,第二個(gè)參數(shù)表示poi搜索類(lèi)型,第三個(gè)參數(shù)表示poi搜索區(qū)域(空字符串代表全國(guó))
query = new PoiSearch.Query(searchKey, searchType, "");
query.setCityLimit(true);
// 設(shè)置每頁(yè)最多返回多少條poiitem
query.setPageSize(20);
query.setPageNum(currentPage);
if (searchLatlonPoint != null) {
poiSearch = new PoiSearch(this, query);
poiSearch.setOnPoiSearchListener(this);
poiSearch.setBound(new PoiSearch.SearchBound(searchLatlonPoint, 1000, true));
poiSearch.searchPOIAsyn();
}
}
@Override
public void onRegeocodeSearched(RegeocodeResult result, int rCode) {
if (rCode == AMapException.CODE_AMAP_SUCCESS) {
if (result != null && result.getRegeocodeAddress() != null
&& result.getRegeocodeAddress().getFormatAddress() != null) {
String address = result.getRegeocodeAddress().getProvince() + result.getRegeocodeAddress().getCity() + result.getRegeocodeAddress().getDistrict() + result.getRegeocodeAddress().getTownship();
firstItem = new PoiItem("regeo", searchLatlonPoint, address, address);
doSearchQuery();
}
}
}
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int i) {
}
/**
* POI搜索結(jié)果回調(diào)
*
* @param poiResult 搜索結(jié)果
* @param resultCode 錯(cuò)誤碼
*/
@Override
public void onPoiSearched(PoiResult poiResult, int resultCode) {
if (resultCode == AMapException.CODE_AMAP_SUCCESS) {
if (poiResult != null && poiResult.getQuery() != null) {
if (poiResult.getQuery().equals(query)) {
poiItems = poiResult.getPois();
if (poiItems != null && poiItems.size() > 0) {
updateListview(poiItems);
}
}
}
}
}
/**
* 更新列表中的item
*
* @param poiItems
*/
private void updateListview(List<PoiItem> poiItems) {
resultData.clear();
searchResultAdapter.setSelectedPosition(0);
resultData.add(firstItem);
resultData.addAll(poiItems);
searchResultAdapter.setData(resultData);
searchResultAdapter.notifyDataSetChanged();
}
@Override
public void onPoiItemSearched(PoiItem poiItem, int i) {
}
AdapterView.OnItemClickListener onItemClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (position != searchResultAdapter.getSelectedPosition()) {
PoiItem poiItem = (PoiItem) searchResultAdapter.getItem(position);
LatLng curLatlng = new LatLng(poiItem.getLatLonPoint().getLatitude(), poiItem.getLatLonPoint().getLongitude());
// 滯空
saveClickLocationAddress = "";
saveClickLocationAddress = poiItem.getCityName() + poiItem.getAdName() + poiItem.getSnippet();
isItemClickAction = true;
aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 16f));
searchResultAdapter.setSelectedPosition(position);
searchResultAdapter.notifyDataSetChanged();
}
}
};
private void addMarkerInScreenCenter(LatLng locationLatLng) {
LatLng latLng = aMap.getCameraPosition().target;
Point screenPosition = aMap.getProjection().toScreenLocation(latLng);
locationMarker = aMap.addMarker(new MarkerOptions()
.anchor(0.5f, 0.5f)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.img_gaode_location_purple_pin)));
// 設(shè)置Marker在屏幕上,不跟隨地圖移動(dòng)
locationMarker.setPositionByPixels(screenPosition.x, screenPosition.y);
}
public int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 屏幕中心marker 跳動(dòng)
*/
public void startJumpAnimation() {
if (locationMarker != null) {
// 根據(jù)屏幕距離計(jì)算需要移動(dòng)的目標(biāo)點(diǎn)
final LatLng latLng = locationMarker.getPosition();
Point point = aMap.getProjection().toScreenLocation(latLng);
point.y -= dip2px(this, 50);
LatLng target = aMap.getProjection()
.fromScreenLocation(point);
// 使用TranslateAnimation,填寫(xiě)一個(gè)需要移動(dòng)的目標(biāo)點(diǎn)
Animation animation = new TranslateAnimation(target);
animation.setInterpolator(new Interpolator() {
@Override
public float getInterpolation(float input) {
// 模擬重加速度的interpolator
if (input <= 0.5) {
return (float) (0.5f - 2 * (0.5 - input) * (0.5 - input));
} else {
return (float) (0.5f - Math.sqrt((input - 0.5f) * (1.5f - input)));
}
}
});
// 整個(gè)移動(dòng)所需要的時(shí)間
animation.setDuration(600);
// 設(shè)置動(dòng)畫(huà)
locationMarker.setAnimation(animation);
// 開(kāi)始動(dòng)畫(huà)
locationMarker.startAnimation();
}
}
Inputtips.InputtipsListener inputtipsListener = new Inputtips.InputtipsListener() {
@Override
public void onGetInputtips(List<Tip> list, int rCode) {
if (rCode == AMapException.CODE_AMAP_SUCCESS) {// 正確返回
autoTips = list;
List<String> listString = new ArrayList<String>();
for (int i = 0; i < list.size(); i++) {
listString.add(list.get(i).getName());
}
ArrayAdapter<String> aAdapter = new ArrayAdapter<String>(
getApplicationContext(),
R.layout.item_gaode_location_autotext, listString);
searchText.setAdapter(aAdapter);
aAdapter.notifyDataSetChanged();
if (isfirstinput) {
isfirstinput = false;
searchText.showDropDown();
}
}
}
};
/**
* POI查詢(xún)
*
* @param result
*/
private void searchPoi(Tip result) {
try {
isInputKeySearch = true;
inputSearchKey = result.getName();//getAddress(); // + result.getRegeocodeAddress().getCity() + result.getRegeocodeAddress().getDistrict() + result.getRegeocodeAddress().getTownship();
searchLatlonPoint = result.getPoint();
firstItem = new PoiItem("tip", searchLatlonPoint, inputSearchKey, result.getAddress());
firstItem.setCityName(result.getDistrict());
firstItem.setAdName("");
resultData.clear();
searchResultAdapter.setSelectedPosition(0);
aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(searchLatlonPoint.getLatitude(), searchLatlonPoint.getLongitude()), 16f));
hideSoftKey(searchText);
doSearchQuery();
} catch (Exception e) {
e.printStackTrace();
}
}
private void hideSoftKey(View view) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
/**
* 發(fā)送存儲(chǔ)的用戶(hù)點(diǎn)擊定位地址信息
*
* @return
*/
private Intent sendLocationAddress() {
Intent resultIntent = new Intent();
resultIntent.putExtra("saveClickLocationAddress", saveClickLocationAddress);
return resultIntent;
}
}
結(jié)束
其他具體使用詳情,大家可根據(jù)官方提供地址查閱:
http://lbs.amap.com/dev/demo#/?tags=android
個(gè)人感覺(jué)地圖開(kāi)發(fā),或者說(shuō)是類(lèi)似這樣采用第三方服務(wù)的時(shí)候,我們只需要嚴(yán)格按照開(kāi)發(fā)文檔進(jìn)行就差不多了,當(dāng)然有點(diǎn)三方服務(wù)比較坑,那就需要我們曲線救國(guó)了~
本篇適合快速實(shí)現(xiàn)功能,以及和LZ一樣的小白觀看~
祝編碼無(wú)bug~
github查看地址如下: