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

前言

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

先上效果图:

实现

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

要实现关键字搜索POI,当然要先集成高德地图到你的项目中去:
集成高德地图见我的另一边文章:Android项目实战(一)——高德地图集成

下面来看实现关键字搜索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,如下:

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,如下:

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 (主要代码都在这里),如下:

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,如下:

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 如果对你有帮助star支持下哈~

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


   转载规则


《利用高德地图关键字检索 POI-实现仿微信发朋友圈搜索附近位置》 wildma 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
Android 接入多盟广告 SDK--让你的 APP 通过广告平台赚钱 Android 接入多盟广告 SDK--让你的 APP 通过广告平台赚钱
前言个人开发者可以通过在自己的APP里面嵌入广告,用户下载你的APP,点击了你APP里的广告,您就可以在多盟平台获得相应的收益。如果是公司,那就不是为了赚那么点钱了,主要是为了与用户有更多的互动,就想着在应用里面嵌入广告。我们公司的场景是这
下一篇 
Glide 中 centerCrop() 与 transform() 共用问题(包括 4.0.0 以上解决方法) Glide 中 centerCrop() 与 transform() 共用问题(包括 4.0.0 以上解决方法)
问题当我们在显示一张图片的时候,想让他等比例缩放到ImageView的大小,又想这张图片实现矩形圆角。 我们一开始会想到这么设置: .transform(new GlideRoundImage(getActivity())) .center
  目录