利用高德地图关键字检索POI-实现仿微信发朋友圈搜索附近位置

转载请注明出处:http://www.jianshu.com/p/363a8badb5eb

前言

由于项目中需要实现搜索附近位置作为收获地址,所以采用了高德地图的关键字检索POI来实现。官方Demo看了很复杂,估计很多人都不想看。但是看了我写的Demo你就会觉得So Easy啦!

先上效果图

高德提供了千万级别的 POI(Point of Interest,兴趣点)。在地图表达中,一个 POI 可代表一栋大厦、一家商铺、一处景点等等。通过POI搜索,完成找餐馆、找景点、找厕所等等的功能。地图 SDK 的搜索功能提供多种获取 POI 数据的接口,下面说的是根据关键字检索POI

1,要实现关键字搜索POI,当然要先集成高德地图到你的项目中去

集成高德地图见我的另一边文章:Android项目实战(一)——高德地图集成

2,下面来看实现关键字搜索POI的具体代码

1,创建activity_poi_keyword_search.xml布局文件,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/et_keyword"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@color/white"
        android:hint="请输入地址关键字搜索"
        android:paddingLeft="8dp"
        android:textColor="@color/deep_black"
        android:textSize="14sp"
        />

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@color/line_color"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>

</LinearLayout>

2,创建recyclerview的item布局item_poi_keyword_search.xml,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/ll_item_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    android:orientation="vertical"
    >

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:background="@color/line_color"/>

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingBottom="8dp"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="8dp">

        <TextView
            android:id="@+id/tv_detailAddress"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="世界之窗"
            android:textColor="@color/deep_black"
            android:textSize="16sp"/>

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:text="1号线"
            android:textColor="@color/shallow_black"
            android:textSize="16sp"/>
    </LinearLayout>
</LinearLayout>

3,创建实体类PoiAddressBean,如下:

package com.alpha58.poidemo.bean;

import java.io.Serializable;

/**
 * author           Alpha58
 * time             2017/2/25 10:48
 * desc             ${TODO}
 * <p>
 * upDateAuthor     $Author$
 * upDate           $Date$
 * upDateDesc       ${TODO}
 */
public class PoiAddressBean implements Serializable {

    private String longitude;//经度
    private String latitude;//纬度
    private String text;//信息内容
    public  String detailAddress;//详细地址 (搜索的关键字)
    public  String province;//省
    public  String city;//城市
    public  String district;//区域(宝安区)

    public PoiAddressBean(String lon, String lat, String detailAddress, String text, String province, String city, String district){
        this.longitude = lon;
        this.latitude = lat;
        this.text = text;
        this.detailAddress = detailAddress;
        this.province = province;
        this.city = city;
        this.district = district;


    }

    public String getLongitude() {
        return longitude;
    }

    public String getLatitude() {
        return latitude;
    }

    public String getText() {
        return text;
    }

    public String getDetailAddress() {
        return detailAddress;
    }

    public String getProvince() {
        return province;
    }

    public String getCity() {
        return city;
    }

    public String getDistrict() {
        return district;
    }

}

,4,创建适配器PoiKeywordSearchAdapter,如下:

package com.alpha58.poidemo.adapter;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.alpha58.poidemo.activity.PoiKeywordSearchActivity;
import com.alpha58.poidemo.R;
import com.alpha58.poidemo.bean.PoiAddressBean;
import java.util.List;

/**
 * author           Alpha58
 * time             2017/2/25 10:48
 * desc             ${TODO}
 * <p>
 * upDateAuthor     $Author$
 * upDate           $Date$
 * upDateDesc       ${TODO}
 */
public class PoiKeywordSearchAdapter extends RecyclerView.Adapter<PoiKeywordSearchAdapter.MyViewHolder> {

    List<PoiAddressBean> poiAddressBean;
    Context mContext;
    public PoiKeywordSearchAdapter(Context context, List<PoiAddressBean> poiAddressBean) {
        this.poiAddressBean  = poiAddressBean;
        this.mContext = context;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        view = LayoutInflater.from(mContext).inflate(R.layout.item_poi_keyword_search, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {

        final PoiAddressBean poiAddressBean = this.poiAddressBean.get(position);
        holder.tv_detailAddress.setText(poiAddressBean.getDetailAddress());
        holder.tv_content.setText(poiAddressBean.getText());
        holder.ll_item_layout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ((PoiKeywordSearchActivity)mContext).setDetailAddress(poiAddressBean.getDetailAddress());
            }
        });
    }

    @Override
    public int getItemCount() {
        if (poiAddressBean != null) {
            return poiAddressBean.size();
        } else {
            return 0;
        }
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView     tv_content;
        TextView     tv_detailAddress;
        LinearLayout ll_item_layout;

        public MyViewHolder(View itemView) {
            super(itemView);
            tv_detailAddress = (TextView) itemView.findViewById(R.id.tv_detailAddress);
            tv_content = (TextView) itemView.findViewById(R.id.tv_content);
            ll_item_layout = (LinearLayout) itemView.findViewById(R.id.ll_item_layout);
        }
    }
}

5,创建搜索界面类PoiKeywordSearchActivity (主要代码都在这里),如下:

package com.alpha58.poidemo.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;

import com.alpha58.poidemo.R;
import com.alpha58.poidemo.adapter.PoiKeywordSearchAdapter;
import com.alpha58.poidemo.bean.PoiAddressBean;
import com.alpha58.poidemo.util.ToastUtil;
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.core.SuggestionCity;
import com.amap.api.services.poisearch.PoiResult;
import com.amap.api.services.poisearch.PoiSearch;

import java.util.ArrayList;
import java.util.List;

/**
 * author           Alpha58
 * time             2017/2/25 10:48
 * desc             ${Poi关键字搜索}
 * <p>
 * upDateAuthor     $Author$
 * upDate           $Date$
 * upDateDesc       ${TODO}
 */
public class PoiKeywordSearchActivity extends AppCompatActivity implements PoiSearch.OnPoiSearchListener {


    private RecyclerView mRecyclerView;
    private EditText mEt_keyword;
    private String keyWord = "";// 要输入的poi搜索关键字
    private PoiResult poiResult; // poi返回的结果
    private int currentPage = 0;// 当前页面,从0开始计数
    private PoiSearch.Query query;// Poi查询条件类
    private PoiSearch       poiSearch;// POI搜索

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_poi_keyword_search);

        initView();
        initListener();
        initData();
    }


    private void initView() {
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
        mEt_keyword = (EditText) findViewById(R.id.et_keyword);
    }

    private void initListener() {
        mEt_keyword.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }

            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                keyWord = String.valueOf(charSequence);
                if ("".equals(keyWord)) {
                    ToastUtil.show(PoiKeywordSearchActivity.this,"请输入搜索关键字");
                    return;
                } else {
                    doSearchQuery();
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }

    /**
     * 开始进行poi搜索
     */
    protected void doSearchQuery() {
        currentPage = 0;
        //不输入城市名称有些地方搜索不到
        query = new PoiSearch.Query(keyWord, "", "深圳");// 第一个参数表示搜索字符串,第二个参数表示poi搜索类型,第三个参数表示poi搜索区域(空字符串代表全国)
        //这里没有做分页加载了,默认给50条数据
        query.setPageSize(50);// 设置每页最多返回多少条poiitem
        query.setPageNum(currentPage);// 设置查第一页

        poiSearch = new PoiSearch(this, query);
        poiSearch.setOnPoiSearchListener(this);
        poiSearch.searchPOIAsyn();
    }

    private void initData() {
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));

    }


    /**
     * POI信息查询回调方法
     */
    @Override
    public void onPoiSearched(PoiResult result, int rCode) {
        if (rCode == AMapException.CODE_AMAP_SUCCESS) {
            if (result != null && result.getQuery() != null) {  // 搜索poi的结果
                if (result.getQuery().equals(query)) {  // 是否是同一条
                    poiResult = result;
                    ArrayList<PoiAddressBean> data = new ArrayList<PoiAddressBean>();//自己创建的数据集合
                    // 取得搜索到的poiitems有多少页
                    List<PoiItem> poiItems = poiResult.getPois();// 取得第一页的poiitem数据,页数从数字0开始
                    List<SuggestionCity> suggestionCities = poiResult
                            .getSearchSuggestionCitys();// 当搜索不到poiitem数据时,会返回含有搜索关键字的城市信息
                    for(PoiItem item : poiItems){
                        //获取经纬度对象
                        LatLonPoint llp = item.getLatLonPoint();
                        double lon = llp.getLongitude();
                        double lat = llp.getLatitude();

                        String title = item.getTitle();
                        String text = item.getSnippet();
                        String provinceName = item.getProvinceName();
                        String cityName = item.getCityName();
                        String adName = item.getAdName();
                        data.add(new PoiAddressBean(String.valueOf(lon), String.valueOf(lat), title, text,provinceName,
                                cityName,adName));
                    }
                    PoiKeywordSearchAdapter adapter = new PoiKeywordSearchAdapter(PoiKeywordSearchActivity.this,data);
                    mRecyclerView.setAdapter(adapter);
                }
            } else {
                ToastUtil.show(PoiKeywordSearchActivity.this,
                        getString(R.string.no_result));
            }
        } else {
            ToastUtil.showerror(this, rCode);
        }

    }

    /**
     * POI信息查询回调方法
     */
    @Override
    public void onPoiItemSearched(PoiItem item, int rCode) {
        // TODO Auto-generated method stub

    }


    /**
     * 设置详情地址
     * @param detailAddress
     */
    public void setDetailAddress(String detailAddress) {
        mEt_keyword.setText(detailAddress);
    }
}

6,最后附上官方Demo中的吐司工具类ToastUtil,如下:

/**
 * 
 */
package com.alpha58.poidemo.util;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import com.amap.api.services.core.AMapException;

public class ToastUtil {

    public static void show(Context context, String info) {
        Toast.makeText(context, info, Toast.LENGTH_LONG).show();
    }

    public static void show(Context context, int info) {
        Toast.makeText(context, info, Toast.LENGTH_LONG).show();
    }
    
    public static void showerror(Context context, int rCode){

        try {
            switch (rCode) {
            //服务错误码
            case 1001:
                throw new AMapException(AMapException.AMAP_SIGNATURE_ERROR);
            case 1002:
                throw new AMapException(AMapException.AMAP_INVALID_USER_KEY);
            case 1003:
                throw new AMapException(AMapException.AMAP_SERVICE_NOT_AVAILBALE);
            case 1004:
                throw new AMapException(AMapException.AMAP_DAILY_QUERY_OVER_LIMIT);
            case 1005:
                throw new AMapException(AMapException.AMAP_ACCESS_TOO_FREQUENT);
            case 1006:
                throw new AMapException(AMapException.AMAP_INVALID_USER_IP);
            case 1007:
                throw new AMapException(AMapException.AMAP_INVALID_USER_DOMAIN);
            case 1008:
                throw new AMapException(AMapException.AMAP_INVALID_USER_SCODE);
            case 1009:
                throw new AMapException(AMapException.AMAP_USERKEY_PLAT_NOMATCH);
            case 1010:
                throw new AMapException(AMapException.AMAP_IP_QUERY_OVER_LIMIT);
            case 1011:
                throw new AMapException(AMapException.AMAP_NOT_SUPPORT_HTTPS);
            case 1012:
                throw new AMapException(AMapException.AMAP_INSUFFICIENT_PRIVILEGES);
            case 1013:
                throw new AMapException(AMapException.AMAP_USER_KEY_RECYCLED);
            case 1100:
                throw new AMapException(AMapException.AMAP_ENGINE_RESPONSE_ERROR);
            case 1101:
                throw new AMapException(AMapException.AMAP_ENGINE_RESPONSE_DATA_ERROR);
            case 1102:
                throw new AMapException(AMapException.AMAP_ENGINE_CONNECT_TIMEOUT);
            case 1103:
                throw new AMapException(AMapException.AMAP_ENGINE_RETURN_TIMEOUT);
            case 1200:
                throw new AMapException(AMapException.AMAP_SERVICE_INVALID_PARAMS);
            case 1201:
                throw new AMapException(AMapException.AMAP_SERVICE_MISSING_REQUIRED_PARAMS);
            case 1202:
                throw new AMapException(AMapException.AMAP_SERVICE_ILLEGAL_REQUEST);
            case 1203:
                throw new AMapException(AMapException.AMAP_SERVICE_UNKNOWN_ERROR);
            //sdk返回错误
            case 1800:
                throw new AMapException(AMapException.AMAP_CLIENT_ERRORCODE_MISSSING);
            case 1801:
                throw new AMapException(AMapException.AMAP_CLIENT_ERROR_PROTOCOL);
            case 1802:
                throw new AMapException(AMapException.AMAP_CLIENT_SOCKET_TIMEOUT_EXCEPTION);
            case 1803:
                throw new AMapException(AMapException.AMAP_CLIENT_URL_EXCEPTION);
            case 1804:
                throw new AMapException(AMapException.AMAP_CLIENT_UNKNOWHOST_EXCEPTION);
            case 1806:
                throw new AMapException(AMapException.AMAP_CLIENT_NETWORK_EXCEPTION);
            case 1900:
                throw new AMapException(AMapException.AMAP_CLIENT_UNKNOWN_ERROR);
            case 1901:
                throw new AMapException(AMapException.AMAP_CLIENT_INVALID_PARAMETER);
            case 1902:
                throw new AMapException(AMapException.AMAP_CLIENT_IO_EXCEPTION);
            case 1903:
                throw new AMapException(AMapException.AMAP_CLIENT_NULLPOINT_EXCEPTION);
              //云图和附近错误码  
            case 2000:
                throw new AMapException(AMapException.AMAP_SERVICE_TABLEID_NOT_EXIST);
            case 2001:
                throw new AMapException(AMapException.AMAP_ID_NOT_EXIST);
            case 2002:
                throw new AMapException(AMapException.AMAP_SERVICE_MAINTENANCE);
            case 2003:
                throw new AMapException(AMapException.AMAP_ENGINE_TABLEID_NOT_EXIST);
            case 2100:
                throw new AMapException(AMapException.AMAP_NEARBY_INVALID_USERID);
            case 2101:
                throw new AMapException(AMapException.AMAP_NEARBY_KEY_NOT_BIND);
            case 2200:
                throw new AMapException(AMapException.AMAP_CLIENT_UPLOADAUTO_STARTED_ERROR);
            case 2201:
                throw new AMapException(AMapException.AMAP_CLIENT_USERID_ILLEGAL);
            case 2202:
                throw new AMapException(AMapException.AMAP_CLIENT_NEARBY_NULL_RESULT);
            case 2203:
                throw new AMapException(AMapException.AMAP_CLIENT_UPLOAD_TOO_FREQUENT);
            case 2204:
                throw new AMapException(AMapException.AMAP_CLIENT_UPLOAD_LOCATION_ERROR);
            //路径规划   
            case 3000:
                throw new AMapException(AMapException.AMAP_ROUTE_OUT_OF_SERVICE);
            case 3001:
                throw new AMapException(AMapException.AMAP_ROUTE_NO_ROADS_NEARBY);
            case 3002:
                throw new AMapException(AMapException.AMAP_ROUTE_FAIL);
            case 3003:
                throw new AMapException(AMapException.AMAP_OVER_DIRECTION_RANGE);
            //短传分享
            case 4000:
                throw new AMapException(AMapException.AMAP_SHARE_LICENSE_IS_EXPIRED);
            case 4001:
                throw new AMapException(AMapException.AMAP_SHARE_FAILURE);
            default:
                Toast.makeText(context,"查询失败:"+rCode , Toast.LENGTH_LONG).show();
                logError("查询失败", rCode);
                break;
            }
        } catch (Exception e) {
            Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show();
            logError(e.getMessage(), rCode);
        }
    }

    private static void logError(String info, int errorCode) {
        print(LINE);//start
        print("                                   错误信息                                     ");
        print(LINE);//title
        print(info);
        print("错误码: " + errorCode);
        print("                                                                               ");
        print("如果需要更多信息,请根据错误码到以下地址进行查询");
        print("  http://lbs.amap.com/api/android-sdk/guide/map-tools/error-code/");
        print("如若仍无法解决问题,请将全部log信息提交到工单系统,多谢合作");
        print(LINE);//end
    }

    //log
    public static final String TAG = "AMAP_ERROR";
    static final String LINE_CHAR="=";
    static final String BOARD_CHAR="|";
    static final int LENGTH = 80;
    static String LINE;
    static{
        StringBuilder sb = new StringBuilder();
        for(int i = 0;i<LENGTH;i++){
            sb .append(LINE_CHAR);
        }
        LINE = sb.toString();
    }


    private static void printLog(String s){
        if(s.length()<LENGTH-2){
            StringBuilder sb = new StringBuilder();
            sb.append(BOARD_CHAR).append(s);
            for(int i = 0 ;i <LENGTH-2-s.length();i++){
                sb.append(" ");
            }
            sb.append(BOARD_CHAR);
            print(sb.toString());
        }else{
            String line = s.substring(0,LENGTH-2);
            print(BOARD_CHAR+line+BOARD_CHAR);
            printLog(s.substring(LENGTH-2));
        }
    }

    private static void print(String s) {
        Log.i(TAG,s);
    }
}

大功告成!利用高德地图关键字检索POI就是这么简单!

源码地址:https://github.com/wildma/PoiDemo
注意:下载后直接运行会报“用户MD5安全码未通过”,因为该Demo用的是我自己在高德平台申请的KEY,
如果真的想要在我的Demo上测试你的Key,则需要替换清单文件中的key为你的。并且将项目的包名改为你申请key时的包名。

如果对你有帮助记得点赞,star哈~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容