最近做地图相关的比较多,对照着高德开发者文档,发现如果要实现定位,mark,地图显示等多种功能的时候,很多代码都是重复的,而且每个部分的教程里用到的代码对象名称都不一样,比如同样是MapView,有的是aMapView,有的是mapView,这对于新手来说看起来很头疼啊,对着写到一半,发现同一个对象,名称换了,所以不如百度搜了一些资料。自己亲自测试过,这里整理编辑给大家,高德的地图显示,定位,再到根据后台给你的经纬度在地图上标记Marker,以及Marker的点击事件,以及Marker的InfoWindow,还有导航等都有,这篇文章主要是讲InfoWindow的。我会贴出全部代码,供大家参考。
先看效果:
首先,肯定是按照自己的文件包名去注册key,然后在清单文件Mainfest里面去注册service和key,以及权限啦!标红的为需要申请的部分
<!--//地图包、搜索包需要的基础权限-->
<!--允许程序打开网络套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序设置内置sd卡的写权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--允许程序获取网络状态-->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--允许程序访问WiFi网络信息-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--允许程序读写手机状态和身份-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允许程序访问CellID或WiFi热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<application
android:name=".base.BaseApplication"
>
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="XXXXXXXXXXXXXXXXXXXXXXXXXXX">//开发者申请的key
</meta-data>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--高德地图定位服务-->
<service android:name="com.amap.api.location.APSService"/>
</application>
</manifest>
然后先来写布局:view_infowindow
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="127dp"
android:background="#0000"
>
<LinearLayout
android:layout_width="209dp"
android:layout_height="127dp"
android:orientation="vertical"
android:background="@drawable/inforwindow_bg">
<TextView
android:id="@+id/name"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:layout_marginTop="11dp"
android:layout_width="match_parent"
android:layout_height="20dp"
android:textSize="14sp"
android:textColor="@color/black_text"/>
<TextView
android:id="@+id/addr"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:layout_marginTop="2dp"
android:layout_width="match_parent"
android:layout_height="34dp"
android:textSize="12sp"
android:lines="2"
android:ellipsize="end"
android:textColor="#3d3d3d"/>
<View
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="9dp"
android:background="#DFDFDF"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_marginTop="9dp"
android:id="@+id/navigation_LL"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
>
<ImageView
android:layout_marginLeft="27dp"
android:layout_width="17dp"
android:layout_height="18dp"
android:scaleType="centerCrop"
android:src="@drawable/inforwindow_navigation"/>
<TextView
android:layout_marginLeft="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/infowindow_navigation"
android:textSize="14sp"
android:textColor="@color/black_text"/>
</LinearLayout>
<View
android:layout_width="1dp"
android:layout_height="30dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="13dp"
android:background="#DFDFDF"/>
<LinearLayout
android:layout_marginTop="9dp"
android:id="@+id/call_LL"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
>
<ImageView
android:layout_marginLeft="23dp"
android:layout_width="19dp"
android:layout_height="18dp"
android:scaleType="centerCrop"
android:src="@drawable/inforwindow_call"/>
<TextView
android:layout_marginLeft="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/infowindow_call"
android:textSize="14sp"
android:textColor="@color/black_text"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<!--用来偏移inforwindow的位置-->
<TextView
android:layout_width="38dp"
android:layout_height="match_parent" />
</LinearLayout>
activity_main,这里要说明的是我导入的是高德2d的定位的包,不是仅仅显示地图的那个包哦……
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context="teprinciple.yang.amapinforwindowdemo.MainActivity">
<com.amap.api.maps2d.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
好了,下面进行写代码。
实体类Marker和Constant
public class Marker {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class Constant {
//武汉的经纬度
public static final LatLng WUHAN = new LatLng(30.49473, 114.163954);
}
BaseApplication
import android.app.Application;
public class BaseApplication extends Application {
private static BaseApplication mApplication;
@Override
public void onCreate() {
super.onCreate();
mApplication = this;
}
public static BaseApplication getIntance() {
return mApplication;
}
}
BaseActivity
package teprinciple.yang.amapinforwindowdemo.base;
import android.annotation.TargetApi;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
public class BaseActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public View initV(@IdRes int id) {
return findViewById(id);
}
public View initVclick(@IdRes int id) {
View view = initV(id);
view.setOnClickListener(this);
return view;
}
@Override
public void onClick(View view) {
}
}
动态申请权限类CheckPermissionsActivity
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import teprinciple.yang.amapinforwindowdemo.R;
import teprinciple.yang.amapinforwindowdemo.base.BaseActivity;
public class CheckPermissionsActivity extends BaseActivity
implements
ActivityCompat.OnRequestPermissionsResultCallback {
/**
* 需要进行检测的权限数组
*/
protected String[] needPermissions = {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE
};
private static final int PERMISSON_REQUESTCODE = 0;
/**
* 判断是否需要检测,防止不停的弹框
*/
private boolean isNeedCheck = true;
@Override
protected void onResume() {
super.onResume();
if(isNeedCheck){
checkPermissions(needPermissions);
}
}
/**
*
* @since 2.5.0
*
*/
private void checkPermissions(String... permissions) {
List<String> needRequestPermissonList = findDeniedPermissions(permissions);
if (null != needRequestPermissonList
&& needRequestPermissonList.size() > 0) {
ActivityCompat.requestPermissions(this,
needRequestPermissonList.toArray(
new String[needRequestPermissonList.size()]),
PERMISSON_REQUESTCODE);
}
}
/**
* 获取权限集中需要申请权限的列表
*
* @param permissions
* @return
* @since 2.5.0
*
*/
private List<String> findDeniedPermissions(String[] permissions) {
List<String> needRequestPermissonList = new ArrayList<String>();
for (String perm : permissions) {
if (ContextCompat.checkSelfPermission(this,
perm) != PackageManager.PERMISSION_GRANTED) {
needRequestPermissonList.add(perm);
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(
this, perm)) {
needRequestPermissonList.add(perm);
}
}
}
return needRequestPermissonList;
}
/**
* 检测是否说有的权限都已经授权
* @param grantResults
* @return
* @since 2.5.0
*
*/
private boolean verifyPermissions(int[] grantResults) {
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] paramArrayOfInt) {
if (requestCode == PERMISSON_REQUESTCODE) {
if (!verifyPermissions(paramArrayOfInt)) {
showMissingPermissionDialog();
isNeedCheck = false;
}
}
}
private void showMissingPermissionDialog() {
// AlertDialog.Builder builder = new AlertDialog.Builder(this);
// builder.setTitle(R.string.notifyTitle);
// builder.setMessage(R.string.notifyMsg);
//
// // 拒绝, 退出应用
// builder.setNegativeButton(R.string.cancel,
// new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// finish();
// }
// });
//
// builder.setPositiveButton(R.string.setting,
// new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// startAppSettings();
// }
// });
//
// builder.setCancelable(false);
//
// builder.show();
}
/** * 启动应用的设置 * * @since 2.5.0 * */private void startAppSettings() {Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);intent.setData(Uri.parse("package:" + getPackageName()));startActivity(intent);}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if(keyCode == KeyEvent.KEYCODE_BACK){this.finish();return true;}return super.onKeyDown(keyCode, event);}}
跳转到高德地图进行导航的工具类
NavigationUtils
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
import com.amap.api.maps2d.model.LatLng;
import java.io.File;
import teprinciple.yang.amapinforwindowdemo.base.BaseApplication;
/**
* Created by Teprinciple on 2016/8/22.
* 跳转到高德地图进行导航
*/
public class NavigationUtils {
private static Context mContext;
public static void Navigation(LatLng latLng){
mContext = BaseApplication.getIntance().getBaseContext();
if(isInstallPackage("com.autonavi.minimap")){
// Toast.makeText(getContext(), "安装有高德地图", Toast.LENGTH_SHORT).show();
SkipToGD(latLng);
}else {
Toast.makeText(mContext, "请下载安装高德地图", Toast.LENGTH_SHORT).show();
DownloadMapApp();
}
}
/**
* 到应用市场下载高德地图app
*/
private static void DownloadMapApp() {
//显示手机上所有的market商店
Uri uri = Uri.parse("market://details?id=com.autonavi.minimap");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
/**
* 判断是否安装 高德地图app
* @param packageName
* @return
*/
private static boolean isInstallPackage(String packageName) {
return new File("/data/data/" + packageName).exists();
}
/**
* 跳转到高德地图进行导航
*/
private static void SkipToGD(LatLng latLng) {
//跳转导航
//dev 是否偏移(0:lat 和 lon 是已经加密后的,不需要国测加密; 1:需要国测加密)
//style 导航方式(0 速度快; 1 费用少; 2 路程短; 3 不走高速;4 躲避拥堵;
// 5 不走高速且避免收费;6 不走高速且躲避拥堵;7 躲避收费和拥堵;8 不走高速躲避收费和拥堵))
Uri uri = Uri.parse("androidamap://navi?sourceApplication=CloudPatient&lat="+latLng.latitude+"&lon="+latLng.longitude+"&dev=1&style=2");
Intent intent = new Intent("android.intent.action.VIEW", uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
}
打电话工具类
PhoneCallUtils
package teprinciple.yang.amapinforwindowdemo.utils;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.TextUtils;
import teprinciple.yang.amapinforwindowdemo.base.BaseApplication;
/**
* Created by Teprinciple on 2016/8/22.
* 打电话工具类
*/
public class PhoneCallUtils {
private static Context mContext = BaseApplication.getIntance().getBaseContext();;
public static void call(String phoneNum){
if (TextUtils.isEmpty(phoneNum)){ //电话号码为空
return;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
Uri uri = Uri.parse("tel:"+phoneNum); //设置要操作界面的具体内容 拨打电话固定格式: tel:
intent.setData(uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
}
重点来了,InfoWinAdapter,用来显示这个infowindow的数据的
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.amap.api.maps2d.AMap;
import com.amap.api.maps2d.model.LatLng;
import com.amap.api.maps2d.model.Marker;
import teprinciple.yang.amapinforwindowdemo.base.BaseApplication;
import teprinciple.yang.amapinforwindowdemo.R;
import teprinciple.yang.amapinforwindowdemo.utils.NavigationUtils;
import teprinciple.yang.amapinforwindowdemo.utils.PhoneCallUtils;
/**
* Created by Teprinciple on 2016/8/23.
* 地图上自定义的infowindow的适配器
*/
public class InfoWinAdapter implements AMap.InfoWindowAdapter, View.OnClickListener {
private Context mContext = BaseApplication.getIntance().getBaseContext();
private LatLng latLng;
private LinearLayout call;
private LinearLayout navigation;
private TextView nameTV;
private String agentName;
private TextView addrTV;
private String snippet;
@Override
public View getInfoWindow(Marker marker) {
initData(marker);
View view = initView();
return view;
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
private void initData(Marker marker) {
latLng = marker.getPosition();
snippet = marker.getSnippet();
agentName = marker.getTitle();
}
@NonNull
private View initView() {
View view = LayoutInflater.from(mContext).inflate(R.layout.view_infowindow, null);
navigation = (LinearLayout) view.findViewById(R.id.navigation_LL);
call = (LinearLayout) view.findViewById(R.id.call_LL);
nameTV = (TextView) view.findViewById(R.id.name);
addrTV = (TextView) view.findViewById(R.id.addr);
nameTV.setText(agentName);
addrTV.setText(String.format(mContext.getString(R.string.agent_addr),snippet));
navigation.setOnClickListener(this);
call.setOnClickListener(this);
return view;
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.navigation_LL: //点击导航
NavigationUtils.Navigation(latLng);
break;
case R.id.call_LL: //点击打电话
PhoneCallUtils.call("10086"); //TODO 处理电话号码
break;
}
}
}
最后
MainActivity
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
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.maps2d.AMap;
import com.amap.api.maps2d.CameraUpdateFactory;
import com.amap.api.maps2d.LocationSource;
import com.amap.api.maps2d.MapView;
import com.amap.api.maps2d.UiSettings;
import com.amap.api.maps2d.model.BitmapDescriptorFactory;
import com.amap.api.maps2d.model.LatLng;
import com.amap.api.maps2d.model.Marker;
import com.amap.api.maps2d.model.MarkerOptions;
import com.amap.api.maps2d.model.MyLocationStyle;
import teprinciple.yang.amapinforwindowdemo.adapter.InfoWinAdapter;
import teprinciple.yang.amapinforwindowdemo.base.BaseActivity;
import teprinciple.yang.amapinforwindowdemo.entity.Constant;
import teprinciple.yang.amapinforwindowdemo.utils.CheckPermissionsActivity;
public class MainActivity extends CheckPermissionsActivity implements AMap.OnMapClickListener, AMap.OnMarkerClickListener, AMapLocationListener, LocationSource {
private MapView mapView;
//声明AMapLocationClient类对象
public AMapLocationClient mLocationClient;
//声明AMapLocationClientOption对象
private AMapLocationClientOption mLocationOption;
private AMap aMap;
private UiSettings uiSettings;
private InfoWinAdapter adapter;
private Marker oldMarker;
private OnLocationChangedListener mListener;
private LatLng myLatLng;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//在执行onCreateView时执行mMapView.onCreate(savedInstanceState),实现地图生命周期管理
mapView.onCreate(savedInstanceState);
initMap();
initLocation();
}
private void initView() {
mapView = (MapView) initV(R.id.mapView);
}
/**
* 初始化地图
*/
private void initMap() {
if (aMap == null) {
aMap = mapView.getMap();
uiSettings = aMap.getUiSettings();
aMap.setOnMapClickListener(this);
}
uiSettings.setZoomControlsEnabled(false); //隐藏缩放控件
//自定义InfoWindow
aMap.setOnMarkerClickListener(this);
adapter = new InfoWinAdapter();
aMap.setInfoWindowAdapter(adapter);
addMarkerToMap(Constant.WUHAN,"武汉","中国湖北省武汉市蔡甸区");
}
/**
* 初始化定位
*/
private void initLocation(){
// 自定义系统定位小蓝点--我的位置
MyLocationStyle myLocationStyle = new MyLocationStyle();
myLocationStyle.myLocationIcon(BitmapDescriptorFactory
.fromResource(R.drawable.mylocation));// 设置小蓝点的图标
myLocationStyle.strokeColor(getResources().getColor(R.color.blue));// 设置圆形的边框颜色
myLocationStyle.radiusFillColor(Color.argb(100, 29, 161, 242));// 设置圆形的填充颜色
myLocationStyle.strokeWidth(1.0f);// 设置圆形的边框粗细
// myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0));
// myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0));
aMap.setMyLocationStyle(myLocationStyle);
aMap.setLocationSource(this);// 设置定位资源。如果不设置此定位资源则定位按钮不可点击。并且实现activate激活定位,停止定位的回调方法
aMap.getUiSettings().setMyLocationButtonEnabled(true);// 设置默认定位按钮是否显示
aMap.setMyLocationEnabled(true);// 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false
}
//激活定位
//记得注册定位
//<service android:name="com.amap.api.location.APSService"/>
@Override
public void activate(OnLocationChangedListener onLocationChangedListener) {
mListener = onLocationChangedListener;
if (mLocationClient == null) {
mLocationClient = new AMapLocationClient(this);
mLocationOption = new AMapLocationClientOption();
//设置定位监听
mLocationClient.setLocationListener(this);
//设置为高精度定位模式
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
mLocationOption.setOnceLocation(true);
//设置是否返回地址信息(默认返回地址信息)
mLocationOption.setNeedAddress(true);
//设置定位参数
mLocationClient.setLocationOption(mLocationOption);
mLocationClient.startLocation();
}
}
//停止定位
@Override
public void deactivate() {
mListener = null;
if (mLocationClient != null) {
mLocationClient.stopLocation();
mLocationClient.onDestroy();
}
mLocationClient = null;
}
@Override
public void onResume() {
super.onResume();
mapView.onResume(); //管理地图的生命周期
}
@Override
public void onPause() {
super.onPause();
mapView.onPause(); //管理地图的生命周期
}
@Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy(); //管理地图的生命周期
}
//地图的点击事件
@Override
public void onMapClick(LatLng latLng) {
//点击地图上没marker 的地方,隐藏inforwindow
if (oldMarker != null) {
oldMarker.hideInfoWindow();
oldMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.marker_normal));
}
}
//maker的点击事件
@Override
public boolean onMarkerClick(Marker marker) {
if (!marker.getPosition().equals(myLatLng)){ //点击的marker不是自己位置的那个marker
if (oldMarker != null) {
oldMarker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.marker_normal));
}
oldMarker = marker;
marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.marker_selected));
}
return false; //返回 “false”,除定义的操作之外,默认操作也将会被执行
}
//定位成功的监听
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
if (aMapLocation != null) {
if(mListener != null){
// aMap.clear(); 清除之前的marker
mListener.onLocationChanged(aMapLocation);// 显示系统小蓝点-我的位置
}
if (aMapLocation.getErrorCode() == 0) {
myLatLng = new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude());
aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(myLatLng, 14));
String city = aMapLocation.getCity();
String address = aMapLocation.getAddress();
// addMarkerToMap(latLng,city,address);
} else {
//定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。
Log.e("AmapError", "location Error, ErrCode:" + aMapLocation.getErrorCode() + ", errInfo:"
+ aMapLocation.getErrorInfo());
}
}
}
private void addMarkerToMap(LatLng latLng, String title, String snippet) {
aMap.addMarker(new MarkerOptions().anchor(0.5f, 0.5f)
.position(latLng)
.title(title)
.snippet(snippet)
.icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_normal))
);
}
}
好了,所有代码都已经公布啦,这里感谢简书的Teprinciple老师
附上老师项目链接:https://github.com/teprinciple/AMapInfoWindowDemo