Android Study 之玩转高德地图二部曲[实现POI检索附近]
2017-05-19 00:14
746 查看
LZ-Says:前段时间更新了一篇关于高德地图的简单使用,其中主要包含实现地图显示以及定位简单功能,具体文章如下:
Android Study 之玩转高德地图一部曲[实现显示地图以及定位功能]
写这个的目的有俩个,一个是对自己的一个记录,想看的时候随时能点击看看;二就是针对和LZ一样的人群,简单快捷高效实现地图开发,我走过的坑,在坑上浪费的时间,大家看了之后就能多少避免一些。
写文章,方便自己,共享他人,相互学习,相互交流,共同进步,何乐不为?
本文意在快速实现高德地图POI检索附近,具体官方文档以及官方GitHub都已经有相关demo,LZ只是把自己的开发过程叙述下而已,希望大家都有自己的收获~
下面为大家展示先最终实现效果:
主要细化为4个部分,如下:
关键字检索POI
根据关键字检索适用于在某个城市搜索某个名称相关的POI,例如:查找北京市的“肯德基”。
注意:
1、关键字未设置城市信息(默认为全国搜索)时,如果涉及多个城市数据返回,仅会返回建议城市,请根据APP需求,选取城市进行搜索。
2、不设置POI的类别,默认返回“餐饮服务”、“商务住宅”、“生活服务”这三种类别的POI,下方提供了POI分类码表,请按照列表内容设置希望检索的POI类型。(建议使用POI类型的代码进行检索)
周边检索POI
适用于搜索某个位置附近的POI,可设置POI的类别,具体查询所在位置的餐饮类、住宅类POI,例如:查找天安门附近的厕所等等场景。
与关键字检索的唯一区别需要通过 PoiSearch 的 setBound 方法设置圆形查询范围
多边形内检索的POI
不同于周边搜索,周边搜索是一个圆形范围,而多边形搜索的范围是一个多边形,适用于在搜索某个不规则区域的POI查询,例如:查找中关村范围内的停车场。
ID检索POI
通过关键字检索、周边检索以及多边形检索,或者任意形式得到的高德POI ID信息,可通过ID检索来获取POI完整详细信息。
输入内容自动提示
输入提示是指根据用户输入的关键词,给出相应的提示信息,将最有可能的搜索词呈现给用户,以减少用户输入信息,提升用户体验。如:输入“方恒”,提示“方恒国际中心A座”,“方恒购物中心”等
道路沿途检索POI
从搜索 SDK 3.5.0 版本开始支持,可查询路径沿途的加油站、ATM、汽修店、厕所
接下来,带大家一起玩转POI检索附近~让你欲罢不能,so easy~
compile ‘com.amap.api:search:latest.integration’
接下来,为大家展示下我们实现后的界面效果,方便我们去写我们的layout文件,如下图:
在中间部分,也就是上图中 住宅区 学校 楼宇 商场 部分,高德官方自定义了一个控件,直接继承于RadioGroup,代码如下:
同时我们也需要去创建自定义控件的样式文件,方便我们对其属性进行拓展,如下:
接下来,去编写我们的布局文件,如下:
最下方的搜索结果,大家可直接把其看定为一个ListView,既然是ListView了,肯定会有item布局,仔细观看图,item布局文件是不是so-easy呢?
item布局文件设置完毕后,紧接着,肯定要完善咱的Adapter类喽,不多说,上码:
前期准备工作完善之后,接下来就是我们的重中之重了。
开始前,不妨让我们去理理思路,好让我们更方便快捷的去撸码。其实个人感觉,重要的也就是下面俩个部分,大体了解下,结合开发文档以及提供示例,差不多就可以开搞了~
step 1:初始化地图,包括初始化地图相关属性设置以及地图的监听事件;
step 2:输入内容自动提示,我们所需要的步骤如下:
1、继承 InputtipsListener 监听;
2、构造 InputtipsQuery 对象,通过
InputtipsQuery(java.lang.String keyword, java.lang.String city)
3、构造 Inputtips 对象,并设置监听;
4、调用 PoiSearch 的 requestInputtipsAsyn() 方法发送请求;
5、通过回调接口 onGetInputtips解析返回的结果,获取输入提示返回的信息。
其中我们需要注意的点如下:
a 、由于提示中会出现相同的关键字,但是这些关键字所在区域不同,使用时可以通过 tipList.get(i).getDistrict() 获得区域,也可以在提示时在关键字后加上区域。
b、当 Tip 的 getPoiID() 返回空,并且 getPoint() 也返回空时,表示该提示词不是一个真实存在的 POI,这时区域、经纬度参数都是空的,此时可根据该提示词进行POI关键词搜索
c、当 Tip 的 getPoiID() 返回不为空,但 getPoint() 返回空时,表示该提示词是一个公交线路名称,此时用这个id进行公交线路查询。
d、当 Tip 的 getPoiID() 返回不为空,且 getPoint() 也不为空时,表示该提示词一个真实存在的POI,可直接显示在地图上。
http://lbs.amap.com/dev/demo#/?tags=android
个人感觉地图开发,或者说是类似这样采用第三方服务的时候,我们只需要严格按照开发文档进行就差不多了,当然有点三方服务比较坑,那就需要我们曲线救国了~
本篇适合快速实现功能,以及和LZ一样的小白观看~
祝编码无bug~
github查看地址如下:
https://github.com/HLQ-Struggle/GaoDeMapDemo
Android Study 之玩转高德地图一部曲[实现显示地图以及定位功能]
写这个的目的有俩个,一个是对自己的一个记录,想看的时候随时能点击看看;二就是针对和LZ一样的人群,简单快捷高效实现地图开发,我走过的坑,在坑上浪费的时间,大家看了之后就能多少避免一些。
写文章,方便自己,共享他人,相互学习,相互交流,共同进步,何乐不为?
本文意在快速实现高德地图POI检索附近,具体官方文档以及官方GitHub都已经有相关demo,LZ只是把自己的开发过程叙述下而已,希望大家都有自己的收获~
下面为大家展示先最终实现效果:
POI简介
POI,又称Point of Interest(兴趣点)。在地图表达中,一个 POI 可代表一栋大厦、一家商铺、一处景点等等。通过POI搜索,完成找餐馆、找景点、找厕所等等的功能。主要细化为4个部分,如下:
关键字检索POI
根据关键字检索适用于在某个城市搜索某个名称相关的POI,例如:查找北京市的“肯德基”。
注意:
1、关键字未设置城市信息(默认为全国搜索)时,如果涉及多个城市数据返回,仅会返回建议城市,请根据APP需求,选取城市进行搜索。
2、不设置POI的类别,默认返回“餐饮服务”、“商务住宅”、“生活服务”这三种类别的POI,下方提供了POI分类码表,请按照列表内容设置希望检索的POI类型。(建议使用POI类型的代码进行检索)
周边检索POI
适用于搜索某个位置附近的POI,可设置POI的类别,具体查询所在位置的餐饮类、住宅类POI,例如:查找天安门附近的厕所等等场景。
与关键字检索的唯一区别需要通过 PoiSearch 的 setBound 方法设置圆形查询范围
多边形内检索的POI
不同于周边搜索,周边搜索是一个圆形范围,而多边形搜索的范围是一个多边形,适用于在搜索某个不规则区域的POI查询,例如:查找中关村范围内的停车场。
ID检索POI
通过关键字检索、周边检索以及多边形检索,或者任意形式得到的高德POI ID信息,可通过ID检索来获取POI完整详细信息。
输入内容自动提示
输入提示是指根据用户输入的关键词,给出相应的提示信息,将最有可能的搜索词呈现给用户,以减少用户输入信息,提升用户体验。如:输入“方恒”,提示“方恒国际中心A座”,“方恒购物中心”等
道路沿途检索POI
从搜索 SDK 3.5.0 版本开始支持,可查询路径沿途的加油站、ATM、汽修店、厕所
接下来,带大家一起玩转POI检索附近~让你欲罢不能,so easy~
话不多说,开干
既然要实现POI检索附近,那首要肯定是引入依赖,如下:compile ‘com.amap.api:search:latest.integration’
接下来,为大家展示下我们实现后的界面效果,方便我们去写我们的layout文件,如下图:
在中间部分,也就是上图中 住宅区 学校 楼宇 商场 部分,高德官方自定义了一个控件,直接继承于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; } } }
同时我们也需要去创建自定义控件的样式文件,方便我们对其属性进行拓展,如下:
<?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>
接下来,去编写我们的布局文件,如下:
<?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>
最下方的搜索结果,大家可直接把其看定为一个ListView,既然是ListView了,肯定会有item布局,仔细观看图,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布局文件设置完毕后,紧接着,肯定要完善咱的Adapter类喽,不多说,上码:
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); } } }
前期准备工作完善之后,接下来就是我们的重中之重了。
开始前,不妨让我们去理理思路,好让我们更方便快捷的去撸码。其实个人感觉,重要的也就是下面俩个部分,大体了解下,结合开发文档以及提供示例,差不多就可以开搞了~
step 1:初始化地图,包括初始化地图相关属性设置以及地图的监听事件;
step 2:输入内容自动提示,我们所需要的步骤如下:
1、继承 InputtipsListener 监听;
2、构造 InputtipsQuery 对象,通过
InputtipsQuery(java.lang.String keyword, java.lang.String city)
3、构造 Inputtips 对象,并设置监听;
4、调用 PoiSearch 的 requestInputtipsAsyn() 方法发送请求;
5、通过回调接口 onGetInputtips解析返回的结果,获取输入提示返回的信息。
其中我们需要注意的点如下:
a 、由于提示中会出现相同的关键字,但是这些关键字所在区域不同,使用时可以通过 tipList.get(i).getDistrict() 获得区域,也可以在提示时在关键字后加上区域。
b、当 Tip 的 getPoiID() 返回空,并且 getPoint() 也返回空时,表示该提示词不是一个真实存在的 POI,这时区域、经纬度参数都是空的,此时可根据该提示词进行POI关键词搜索
c、当 Tip 的 getPoiID() 返回不为空,但 getPoint() 返回空时,表示该提示词是一个公交线路名称,此时用这个id进行公交线路查询。
d、当 Tip 的 getPoiID() 返回不为空,且 getPoint() 也不为空时,表示该提示词一个真实存在的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 * 实现高德地图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查询条件类 private PoiSearch.Query query; private PoiSearch poiSearch; private PoiItem firstItem; private OnLocationChangedListener mListener; // 当前页面,从0开始计数 private int currentPage = 0; private boolean isfirstinput = true; private boolean isItemClickAction; private boolean isInputKeySearch; private String inputSearchKey; private String searchKey = ""; private String[] items = {"住宅区", "学校", "楼宇", "商场"}; private String searchType = items[0]; // 记录用户点击定位地址 private String saveClickLocationAddress = ""; // poi数据 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); } /** * 初始化高德地图监听 */ 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); } }); } /** * 设置一些amap的属性 */ private void setUpMap() { aMap.getUiSettings().setZoomControlsEnabled(false); // 设置地图默认的指南针是否显示 aMap.getUiSettings().setCompassEnabled(true); // 设置定位监听 aMap.setLocationSource(this); // 设置默认定位按钮是否显示 aMap.getUiSettings().setMyLocationButtonEnabled(true); // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是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(); } } /** * 定位成功后回调函数 */ @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(); // 设置定位监听 mlocationClient.setLocationListener(this); // 设置为高精度定位模式 mLocationOption.setOnceLocation(true); mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); // 设置定位参数 mlocationClient.setLocationOption(mLocationOption); // 此方法为每隔固定时间会发起一次定位请求,为了减少电量消耗或网络流量消耗, // 注意设置合适的定位时间的间隔(最小间隔支持为2000ms),并且在合适时间调用stopLocation()方法来取消定位请求 // 在定位结束后,在合适的生命周期调用onDestroy()方法 // 在单次定位情况下,定位无论成功与否,都无需调用stopLocation()方法移除请求,定位sdk内部会移除 mlocationClient.startLocation(); } } /** * 停止定位 */ @Override public void deactivate() { mListener = null; if (mlocationClient != null) { mlocationClient.stopLocation(); mlocationClient.onDestroy(); } mlocationClient = null; } /** * 响应逆地理编码 */ public void geoAddress() { searchText.setText(""); // 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系 RegeocodeQuery query = new RegeocodeQuery(searchLatlonPoint, 200, GeocodeSearch.AMAP); geocoderSearch.getFromLocationAsyn(query); } /** * 开始进行poi搜索 */ protected void doSearchQuery() { currentPage = 0; // 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国) query = new PoiSearch.Query(searchKey, searchType, ""); query.setCityLimit(true); // 设置每页最多返回多少条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搜索结果回调 * * @param poiResult 搜索结果 * @param resultCode 错误码 */ @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))); // 设置Marker在屏幕上,不跟随地图移动 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 跳动 */ public void startJumpAnimation() { if (locationMarker != null) { // 根据屏幕距离计算需要移动的目标点 final LatLng latLng = locationMarker.getPosition(); Point point = aMap.getProjection().toScreenLocation(latLng); point.y -= dip2px(this, 50); LatLng target = aMap.getProjection() .fromScreenLocation(point); // 使用TranslateAnimation,填写一个需要移动的目标点 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))); } } }); // 整个移动所需要的时间 animation.setDuration(600); // 设置动画 locationMarker.setAnimation(animation); // 开始动画 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查询 * * @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); } /** * 发送存储的用户点击定位地址信息 * * @return */ private Intent sendLocationAddress() { Intent resultIntent = new Intent(); resultIntent.putExtra("saveClickLocationAddress", saveClickLocationAddress); return resultIntent; } }
结束
其他具体使用详情,大家可根据官方提供地址查阅:http://lbs.amap.com/dev/demo#/?tags=android
个人感觉地图开发,或者说是类似这样采用第三方服务的时候,我们只需要严格按照开发文档进行就差不多了,当然有点三方服务比较坑,那就需要我们曲线救国了~
本篇适合快速实现功能,以及和LZ一样的小白观看~
祝编码无bug~
github查看地址如下:
https://github.com/HLQ-Struggle/GaoDeMapDemo
相关文章推荐
- Android Study 之玩转高德地图一部曲[实现显示地图以及定位功能]
- [置顶] 利用高德地图关键字检索POI-实现仿微信发朋友圈搜索附近位置
- Android最新版高德地图poi检索仿微信发送位置
- Android高德地图poi检索仿微信发送位置实例代码
- Android利用高德地图关键字检索POI,显示用户MD5安全码未通过
- Android高德地图poi检索仿微信发送位置
- Android Study 之分分钟让你玩转EditText右下角实时显示输入字数
- (转)Android开发实现高德地图定位详解
- android POI搜索,附近搜索,周边搜索定位介绍
- Android 玩转IOC,Retfotit源码解析,教你徒手实现自定义的Retrofit框架
- Android高德地图检索显示
- android 玩转ContentProvider之一--实现ContentProvider操作数据库
- Android POI 百度地图——周边检索
- android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索
- 高德地图api接口poi检索示例----并在信息框显示经纬度
- Android百度地图Poi检索开发总结
- Android简单实现 高德地图的定位与显示,点击按钮切换地图图层
- android ---------高德地图实现定位和3D地图显示
- Android百度地图(六):百度地图POI检索,行政区边界、公交、线路规划查询,地理编码介绍
- android使用最新版百度地图实现城市切换和城市poi搜索