Android Study 之玩转高德地图二部曲[实现POI检索附近]

LZ-Says:前段时间更新了一篇关于高德地图的简单使用,其中主要包含实现地图显示以及定位简单功能,具体文章如下:

Android Study 之玩转高德地图一部曲[实现显示地图以及定位功能]

写这个的<font color=#FF0000>目的</font>有俩个,一个是对自己的一个<font color=#FF0000>记录,想看的时候随时能点击看看</font>;二就是针对和LZ一样的人群,<font color=#FF0000>简单快捷高效实现地图开发,我走过的坑,在坑上浪费的时间,大家看了之后就能多少避免一些。</font>

写文章,方便自己,共享他人,相互学习,相互交流,共同进步,何乐不为?

本文意在快速实现高德地图POI检索附近,具体官方文档以及官方GitHub都已经有相关demo,LZ只是把自己的开发过程叙述下而已,希望大家都有自己的收获~

下面为大家展示先最终实现效果:

这里写图片描述

POI简介

POI,又称Point of Interest(兴趣点)。在地图表达中,一个 POI 可代表一栋大厦、一家商铺、一处景点等等。通过POI搜索,完成找餐馆、找景点、找厕所等等的功能。

主要细化为4个部分,如下:

  • 关键字检索POI
    根据关键字检索适用于在某个城市搜索某个名称相关的POI,例如:查找北京市的“肯德基”。
    <font color=#FF0000>注意:
    1、关键字未设置城市信息(默认为全国搜索)时,如果涉及多个城市数据返回,仅会返回建议城市,请根据APP需求,选取城市进行搜索。
    2、不设置POI的类别,默认返回“餐饮服务”、“商务住宅”、“生活服务”这三种类别的POI,下方提供了POI分类码表,请按照列表内容设置希望检索的POI类型。(建议使用POI类型的代码进行检索)

  • 周边检索POI
    适用于搜索某个位置附近的POI,可设置POI的类别,具体查询所在位置的餐饮类、住宅类POI,例如:查找天安门附近的厕所等等场景。
    <font color=#FF0000>与关键字检索的唯一区别需要通过 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解析返回的结果,获取输入提示返回的信息。
其中我们需要注意的点如下:
<font color=#FF0000> 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

推荐阅读更多精彩内容