Android 腾讯地图定位功能(以3D地图为例)


前言

总结一下之前用过的腾讯地图定位功能,百度了很多,都是相对零散的东西。今天空自己总结了一下,如有不当,还望各位大佬不吝赐教!!!废话不多说,开始

一、准备环境

1,获取API Key
点击网址https://lbs.qq/console/key.html,申请API Key。
2,导入库文件
点击网址https://lbs.qq/android_v1/log.html,下载.so文件和jar包,放入libs文件夹下,如图

jar文件需手动添加到工程中,右键点击jar文件,Add AsLibrary…
并在app下的build.gradle中引入libs(如果已经引入则可忽略)

android {

...

	sourceSets {
	    main {
	        jniLibs.srcDir 'libs'
	    }
	}
}

3,配置 manifest
在 AndroidManifest.xml 中添加使用权限:

<!-- 通过GPS得到精确位置 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 通过网络得到粗略位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 访问网络,某些位置信息需要从网络服务器获取 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 访问WiFi状态,需要WiFi信息用于网络定位 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 修改WiFi状态,发起WiFi扫描, 需要WiFi信息用于网络定位 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 访问网络状态, 检测网络的可用性,需要网络运营商相关信息用于网络定位 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 访问网络的变化, 需要某些信息用于网络定位 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- 访问手机当前状态, 需要某些信息用于网络定位 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

在application标签中配置API Key

<application>  
  ...   
   <meta-data android:name="TencentMapSDK" android:value="您申请的Key" />
</application>

ok,至此前期准备工作已完成,接下来到了实践阶段

二、实现功能

权限申请

Android 6.0系统在原有的AndroidManifest.xml声明权限的基础上新增了运行时权限动态检测,定位等权限也包含在其中。如果您的应用程序设置了 targetSdkVersion ≥ 23,则需要在调用定位功能前进行权限检查,权限检查的示例代码如下:

private void initPremis(){
    if (Build.VERSION.SDK_INT >= 23) {
        String[] permissions = {
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        if (checkSelfPermission(permissions[0]) != PackageManager.PERMISSION_GRANTED)
        {
            requestPermissions(permissions, 0);
        }
    }
}

@Override

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    //可在此继续其他操作。
    // 开始定位

}

在展示地图之前完成权限的申请

xml地图显示

<com.tencent.tencentmap.mapsdk.maps.MapView    
	android:id="@+id/map"    
	android:layout_width="match_parent"    
	android:layout_height="match_parent" />

Activity
在MainActivity.java中使用地图的时候需要注意,由于SDK并没有提供用于管理地图生命周期的Activity,因此需要用户继承Activity后管理地图的生命周期,防止内存泄露,示例代码如下:

public class MainActivity extends Activity {   
	 private MapView mMapView;     

	@Override    
	protected void onCreate(Bundle savedInstanceState) {       
 		// TODO Auto-generated method stub       
  		super.onCreate(savedInstanceState);       
  		setContentView(R.layout.activity_main);        
  		init();
 	 }     
  	@Override    
  	protected void onStart() {       
  		 // TODO Auto-generated method stub        
   		super.onStart();        
   		mMapView.onStart();   
   	 }     

	@Override   
 	protected void onResume() {        
		 // TODO Auto-generated method stub        
 		super.onResume();        
 		mMapView.onResume();   
 	 }    

 	@Override   
 	 protected void onPause() {        
  		// TODO Auto-generated method stub        
  		super.onPause();        
 		 mMapView.onPause();    
 	 }     

	@Override   
	 protected void onStop() {       
 		 // TODO Auto-generated method stub        
  		super.onStop();        
  		mMapView.onStop();   
   	}     

	@Override    
	protected void onRestart() {        
		// TODO Auto-generated method stub        
		super.onRestart();        
		mMapView.onRestart();   
	 }     

	@Override    
	protected void onDestroy() {       
		 // TODO Auto-generated method stub       
		  super.onDestroy();        
		  mMapView.onDestroy();    
	}
}

private void init(){

    mapView = (MapView) findViewById(R.id.map);
    tencentMap = mapView.getMap();
    tencentMap.setMapType(TencentMap.MAP_TYPE_NORMAL);

    locationSource = new DemoLocationSource(this);
    tencentMap.setLocationSource(locationSource);
    
    /**
     * 腾讯地图提供了UiSettings类以方便开发者对地图手势及SDK提供的控件的控制,以定制自己想要的视图效果。
     * UiSettings类的实例化也是通过TencentMap来获取。
     */
    UiSettings mapUiSettings = tencentMap.getUiSettings();
    
    /**
     * 可以控制地图的缩放级别,每次点击改变1个级别,此控件默认打开,
     * 可以通过UiSettings.setZoomControlsEnabled(boolean)接口控制此控件的显示和隐藏。
     */
    mapUiSettings.setZoomControlsEnabled(true);
    
    /**
     * 此控件可以指示地图的南北方向,默认的视图状态下不显示,只有在地图的偏航角或俯仰角不为0时才会显示,
     *  并且该控件的默认点击事件会将地图视图的俯仰角和偏航角动画到0的位置。
     *  可以通过UiSettings.setCompassEnabled(boolean)接口控制此控件的显示和隐藏。
     */
    mapUiSettings.setCompassEnabled(true);
    
    /**
     * 当通过TencentMap.setLocationSource(locationSource)设置好地图的定位源后,
     * 点击此按钮可以在地图上标注一个蓝点指示用户的当前位置。
     * 可以通过UiSettings.setMyLocationButtonEnabled()接口设置此控件的显示和隐藏。
     */
    mapUiSettings.setMyLocationButtonEnabled(true);
    
    /**
     * 旋转手势
     */
    mapUiSettings.setRotateGesturesEnabled(false);
    
    tencentMap.setMyLocationEnabled(true);
    
    //当前位置的style样式,有兴趣的同学可自行研究
    MyLocationStyle myLocationStyle = new MyLocationStyle();
    myLocationStyle.anchor(0.5f, 0.5f);
    myLocationStyle.icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_launcher));
    myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);
    tencentMap.setMyLocationStyle(myLocationStyle);

    //设置地图点击时间
    tencentMap.setOnMapClickListener(clickListener);

}

TencentMap.OnMapClickListener clickListener = new TencentMap.OnMapClickListener() {
    @Override
    public void onMapClick(LatLng latLng) {
        showInfo(latLng);
    }
};

 private void showInfo(LatLng latLng){

        if (marker != null){
            marker.remove();
            i = i+1;
        }
      
        marker = tencentMap.addMarker(new MarkerOptions().
                position(latLng).
                title("北京" + i).
                snippet("DefaultMarker"));
        //设置点击图标动画
        ScaleAnimation scaleAnimation = new ScaleAnimation(1.5f,1,1.5f,1);//x轴0倍,x轴1倍,y轴0倍,y轴1倍
        scaleAnimation.setDuration(300);
        marker.setAnimation(scaleAnimation);
        marker.startAnimation();
        //显示信息窗
        marker.showInfoWindow();
        marker.setInfoWindowEnable(true);
    }

位置监听

class DemoLocationSource implements LocationSource, TencentLocationListener {

    private Context mContext;
    private OnLocationChangedListener mChangedListener;
    private TencentLocationManager locationManager;
    private TencentLocationRequest locationRequest;

    public DemoLocationSource(Context context) {
        // TODO Auto-generated constructor stub
        mContext = context;
        locationManager = TencentLocationManager.getInstance(mContext);
        locationRequest = TencentLocationRequest.create()
                .setInterval(1000*30) // 定位周期 (毫秒)
                .setRequestLevel(TencentLocationRequest.REQUEST_LEVEL_POI) // 定位要求水准
                .setAllowCache(true) // 是否使用缓存
                .setQQ(""); // 设置QQ号
    }

    @Override
    public void onLocationChanged(TencentLocation arg0, int arg1,
                                  String arg2) {
        // TODO Auto-generated method stub
        if (arg1 == TencentLocation.ERROR_OK && mChangedListener != null) {
            Log.e("maplocation", "location: " + arg0.getCity() + " " + arg0.getProvider() + " " + arg0.getBearing());
            //当前位置信息
            Location location = new Location(arg0.getProvider());
            location.setLatitude(arg0.getLatitude());
            location.setLongitude(arg0.getLongitude());
            location.setAccuracy(arg0.getAccuracy());
            // 定位 sdk 只有 gps 返回的值才有可能获取到偏向角
            location.setBearing(arg0.getBearing());
            mChangedListener.onLocationChanged(location);

            mDestinationPoint = new LatLng(39.010737 * 1.0001, 115.469589 * 1.0001);//假设公司坐标
	   //计算两个坐标点的距离(单位m)
            mDistance = TencentLocationUtils.distanceBetween(location.getLatitude(), location.getLongitude(),40.007499,116.472209);

            Log.d("jl---",mDistance + "" + "\n" + location.getLatitude() + "\n" + location.getLongitude());

        }
    }

    @Override
    public void onStatusUpdate(String arg0, int arg1, String arg2) {
        // TODO Auto-generated method stub

    }

    @Override
    public void activate(OnLocationChangedListener arg0) {
        // TODO Auto-generated method stub
        mChangedListener = arg0;
        int err = locationManager.requestLocationUpdates(locationRequest, this);
        switch (err) {
            case 1:
                setTitle("设备缺少使用腾讯定位服务需要的基本条件");
                break;
            case 2:
                setTitle("manifest 中配置的 key 不正确");
                break;
            case 3:
                setTitle("自动加载libtencentloc.so失败");
                break;

            default:
                break;
        }
    }

    @Override
    public void deactivate() {
        // TODO Auto-generated method stub
        locationManager.removeUpdates(this);
        mContext = null;
        locationManager = null;
        locationRequest = null;
        mChangedListener = null;
    }

    public void onPause() {
        locationManager.removeUpdates(this);
    }

    public void onResume() {
        locationManager.requestLocationUpdates(locationRequest, this);
    }

}

还有一个逆向地址解析的功能,通过位置坐标,获取当前地标建筑和周边地理位置信息

String s = "/ws/geocoder/v1/?key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&location=" + location +
        "kRAKpzQ5GhVRxbomUmaMWxDyzVdigDH";
String str = md5(s);
String request = "https://apis.map.qq/ws/geocoder/v1/?key=OB4BZ-D4W3U-B7VVO-4PJWW-6TKDJ-WPB77&" +
        "location=" + location + "&sig=" + str;

//这里拼接完成的request信息可以直接访问,返回值根据您的联网框架自行解析


public static String md5(String string) {
    if (TextUtils.isEmpty(string)) {
        return "";
    }
    MessageDigest md5 = null;
    try {
        md5 = MessageDigest.getInstance("MD5");
        byte[] bytes = md5.digest(string.getBytes());
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            String temp = Integer.toHexString(b & 0xff);
            if (temp.length() == 1) {
                temp = "0" + temp;
            }
            result.append(temp);
        }
        return result.toString();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return "";
}

联网成功后的返回值根据您的联网框架自行解析。具体返回信息请参考:
https://lbs.qq/webservice_v1/guide-gcoder.html
PS
值得注意的是,上面所使用到的key值,需要设置WebServiceAPI。
登陆进入腾讯位置服务个人中心,Key管理,选择使用的key进入设置页面,如下图

选中WebServiceAPI,选中签名校验,生成新的sk,点击保存。
开发中就使用此处生成的key。

ok,完事,简单的地图定位功能就实现了,虽说此功能不难,但是还有许多细节需要注意的。在此我只是做了一些简单的定位功能,很多其他功能,比如手势的各种监听,样式等等,给位老哥可以移步:https://lbs.qq/android_v1/guide-3d.html查看,还有官网上的参考手册供各位老哥参考,更加复杂的功能有待您去挖掘。


拜类个拜~多多支持哟


更多推荐

Android 腾讯地图定位功能一二三