一、引言
這幾天有些忙,耽誤了寫日志,但我的學習始終沒有落下,有空我就會研究《?Android App 開發進階與項目實戰》一書中定位導航方面的內容。在我的手機上先后實現了“獲取經緯度及地理位置描述信息”和“獲取導航衛星信息”功能后,這兩天,我又參照這書中的內容,實現了通過騰訊地圖的Android定位SDK實現定位的功能,并有所擴展。書文以記之。
二、騰訊位置服務平臺創建應用
要使用騰訊地圖的位置服務,首先要到騰訊位置服務平臺(https://lbs.qq.com/)注冊一個用戶,登錄后進入控制臺,在應用管理下創建自己的應用,然后添加Key。具體步驟見以下截圖:
1.登錄打開控制臺
2.找到應用管理->我的應用,點擊“創建應用”按鈕。
3.輸入應用的名稱,并選擇應用類型,確認后生成應用。
4.回到我的應用頁面,點擊“添加Key”。
5.輸入Key名稱、描述,勾選“SDK"和”導航SDK“,并在下方的文本框中輸入你在Android Studio中要創建應用的包名。
6.完成,生成的Key在Android Studio中集成騰旭地圖時要用到。
三、集成騰訊地圖
完成上面的操作后,在Android Studio中要對需要使用騰訊位置服務的應用進行設置才能使用騰訊地圖的SDK。
1.打開已創建好的Module下的build.gradle文件,在文的依賴中添加騰訊地圖組件。
// 騰訊定位
implementation 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.2.8'
// 騰訊地圖
implementation 'com.tencent.map:tencent-map-vector-sdk:4.3.9.9'
// 地圖組件庫
implementation 'com.tencent.map:sdk-utilities:1.0.6'
上述組件的版本并不是最新的,我是照著?《?Android App 開發進階與項目實戰》上輸入的,組件的最新版本請到官網查詢。
2.在Module的AndroidManifest.xml文件中添加權限配置。
我添加的權限是參考了 《?Android App 開發進階與項目實戰》中的內容,官網給的添加權限(見下方)和我添加的有些不同,但我這邊能正常定位,說明有些權限應該是可由可無的。
<!-- 通過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.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- 前臺service權限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- 后臺定位權限 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<!-- A-GPS輔助定位權限,方便GPS快速準確定位 -->
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
3. 在AndroidManifest.xml文件的application節點中添加一行屬性配置:
android:usesCleartextTraffic="true"
4.最后,在AndroidManifest.xml文件的application節點末尾添加名為TencentMapSDK的元數據,之前在官網平臺上生成的Key就填在這里。
以上就是我的設置步驟,全部是參考《?Android App 開發進階與項目實戰》中的內容,與官網的步驟有所區別(官網設置連接),但我的應用實現騰訊地圖定位沒有遇到問題。
四、功能實現
我是參照《?Android App 開發進階與項目實戰》書中9.3.2 顯示地圖面板 的內容,創建的Activity可以在騰訊地圖上顯示手機所在的位置,并能在普通地圖和衛星地圖之間切換,還可以顯示交通情況。另外,可以通過手勢操作移動地圖和縮放地圖。其功能與微信中共享位置功能差不多。
?
????????????????(普通地圖模式)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (衛星地圖+顯示交通情況模式)
對書中的源碼研究后,我決定再擴展一下,增加SDK自帶的指南針圖標,自己自定義的放大按鈕、縮小按鈕、回到手機所在位置按鈕,另外還自定義一個顯示地圖中心圖標且顯示中心經緯度的功能。最終效果如下:
1. 普通地圖模式
這是照搬的書中的源碼,點擊“普通”單選按鈕,將地圖類型設置為TencentMap.MAP_TYPE_NORMAL。進入地圖后默認也是這個類型。關鍵代碼:
private TencentMap mTencentMap; // 聲明一個騰訊地圖對象
private MapView mMapView; // 聲明一個地圖視圖對象// onCreate方法中初始化
mMapView = findViewById(R.id.mapView);
mTencentMap = mMapView.getMap(); // 獲取騰訊地圖對象// 點擊“普通地圖”單選按鈕后,執行以下語句
mTencentMap.setMapType(TencentMap.MAP_TYPE_NORMAL); // 設置普通地圖
2.衛星地圖模式
通過點擊“衛星”單選按鈕觸發。關鍵代碼:
// 點擊“衛星地圖”單選按鈕后,執行以下語句
mTencentMap.setMapType(TencentMap.MAP_TYPE_SATELLITE); // 設置衛星地圖
3. 顯示交通情況
通過點擊“交通情況”復選按鈕觸發,關鍵代碼:
// 點擊“交通情況”復選按鈕觸發
mTencentMap.setTrafficEnabled(isChecked); // 是否顯示交通擁堵狀況
4. 顯示/隱藏中心點標記
這是我自己添加的功能,紅色十字圖標是一個TextView組件,這個組件與騰訊地圖組件MapView都放在一個RelativeLayout布局中,MapView充滿整個布局,TextView放在RelativeLayout中心位置。在onCreate方法初始化時,將這個TextView組件隱藏,在勾選了復選框后,才顯示,另外“中心點”按鈕,也與這個TextView聯動,在TextView組件顯示時“中心點”按鈕可用,在TextView組件隱藏時“中心點”按鈕不可用。
關鍵代碼:
<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" ><com.tencent.tencentmap.mapsdk.maps.MapViewandroid:id="@+id/mapView"android:layout_width="match_parent"android:layout_height="fill_parent" /><TextViewandroid:id="@+id/tv_centerPoint"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:layout_centerInParent="true"android:textSize="48sp"android:textColor="@color/red"android:text="+" /></RelativeLayout>
private Button btn_getCenter; // 獲取中心點按鈕
private TextView tv_centerPoint; // 中心點標記// 點“中心點”按鈕左側的復選框后,顯示
tv_centerPoint.setVisibility(View.VISIBLE); // 顯示
btn_getCenter.setEnabled(true); // 可用// 取消“中心點”按鈕左側的復選框后,隱藏
tv_centerPoint.setVisibility(View.INVISIBLE); // 隱藏
btn_getCenter.setEnabled(false); // 不可用
5. 獲取中心點位置
在“中心點”按鈕左側的復選框勾選的情況下,點擊“中心點”按鈕,會顯示中心點標識,且獲取地圖中心點經緯度,并顯示在界面中。關鍵代碼如下:
CameraPosition cameraPosition = mTencentMap.getCameraPosition();
mCenterLatLng = cameraPosition.target;
Toast.makeText(this, "中心點坐標:" + mCenterLatLng.latitude + ";" + mCenterLatLng.longitude, Toast.LENGTH_LONG).show();
6. 放大/縮小地圖
(點擊放大按鈕效果)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (點擊縮小按鈕效果)
放大和縮小按鈕時參考了其它地圖軟件上的效果,我自己用帶圓角樣式的LinearLayout布局包裹了兩個TextView組件實現的。這個LinearLayout布局也是放在前面說到的RelativeLayout布局中。LinearLayout布局位于地圖組件的右下方向。兩個TextView組件設置了OnClick監聽器,點擊后執行放大和縮小地圖操作。關鍵代碼:
<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" ><com.tencent.tencentmap.mapsdk.maps.MapViewandroid:id="@+id/mapView"android:layout_width="match_parent"android:layout_height="fill_parent" /><LinearLayoutandroid:layout_width="55dp"android:layout_height="wrap_content"android:layout_above="@+id/mapView"android:layout_alignEnd="@+id/mapView"android:layout_alignBottom="@+id/mapView"android:layout_marginEnd="20dp"android:layout_marginBottom="110dp"android:background="@drawable/radius_border_15"android:orientation="vertical"><TextViewandroid:id="@+id/tv_enlarge"android:layout_width="50dp"android:layout_height="50dp"android:layout_gravity="center_horizontal"android:layout_marginTop="15dp"android:layout_marginBottom="15dp"android:gravity="center"android:text="+"android:textColor="@color/black"android:textSize="32sp"android:textStyle="bold" /><TextViewandroid:id="@+id/tv_narrow"android:layout_width="50dp"android:layout_height="50dp"android:layout_gravity="center_horizontal"android:layout_marginTop="15dp"android:layout_marginBottom="15dp"android:gravity="center"android:text="-"android:textColor="@color/black"android:textSize="32sp"android:textStyle="bold" /></LinearLayout></RelativeLayout>
// 點擊放大按鈕后執行
mTencentMap.moveCamera(CameraUpdateFactory.zoomIn()); // 放大一級
// 點擊縮小按鈕后執行
mTencentMap.moveCamera(CameraUpdateFactory.zoomOut()); // 縮小一級
7. 回到手機定位處
在地圖中通過手指拖動地圖,使手機定位脫離地圖中心點后,通過點擊“回到手機處”按鈕就可以將地圖的中心點重新設置為手機定位處,從而讓地圖中心點快速回到定位處。
這個圖標是我從手機的百度地圖上截圖后,通過PS處理后得到的,保存為PNG格式后,放在項目的res/drawable-xhdpi文件夾下。在xml文件中這個圖標放在ImageButton組件中,這個組件外套了一個帶圓角的LinearLayout布局。LinearLayout布局放在RelativeLayout布局中,設置在mapView組件的左下方。
關鍵代碼:
<RelativeLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" ><com.tencent.tencentmap.mapsdk.maps.MapViewandroid:id="@+id/mapView"android:layout_width="match_parent"android:layout_height="fill_parent" /><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignStart="@+id/mapView"android:layout_alignBottom="@+id/mapView"android:background="@drawable/radius_border_15"android:layout_marginStart="20dp"android:layout_marginBottom="50dp"android:orientation="horizontal"><ImageButtonandroid:id="@+id/img_btn_myPlace"android:layout_width="54dp"android:layout_height="54dp"android:backgroundTint="@color/white"android:src="@drawable/ic_my_place"tools:ignore="ContentDescription" /></LinearLayout></RelativeLayout>
// 點擊“中心點”按鈕后,執行
CameraPosition cameraPosition = mTencentMap.getCameraPosition();
mCenterLatLng = cameraPosition.target;
Toast.makeText(this, "中心點坐標:" + mCenterLatLng.latitude + ";" + mCenterLatLng.longitude, Toast.LENGTH_LONG).show();
8.添加指南針
這是騰訊地圖自帶的組件,只需要在onCreate方法中添加幾行代碼就可以:
mTencentMap = mMapView.getMap(); // 獲取騰訊地圖對象// 在上面的語句獲取到地圖對象后,添加以下兩行代碼
UiSettings mysetting = mTencentMap.getUiSettings();
mysetting.setCompassEnabled(true); // 開啟指南針
五、代碼展示
最后上完整的代碼,希望對大家有用:
MapBasicActivity.Java文件
import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;import com.tencent.map.geolocation.TencentLocation;
import com.tencent.map.geolocation.TencentLocationListener;
import com.tencent.map.geolocation.TencentLocationManager;
import com.tencent.map.geolocation.TencentLocationRequest;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdate;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory;
import com.tencent.tencentmap.mapsdk.maps.MapView;
import com.tencent.tencentmap.mapsdk.maps.TencentMap;
import com.tencent.tencentmap.mapsdk.maps.UiSettings;
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor;
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory;
import com.tencent.tencentmap.mapsdk.maps.model.CameraPosition;
import com.tencent.tencentmap.mapsdk.maps.model.LatLng;
import com.tencent.tencentmap.mapsdk.maps.model.MarkerOptions;public class MapBasicActivity extends AppCompatActivity implements TencentLocationListener, View.OnClickListener {private final static String TAG = "MapBasicActivity";private TencentLocationManager mLocationManager; // 聲明一個騰訊定位管理器對象private MapView mMapView; // 聲明一個地圖視圖對象private TencentMap mTencentMap; // 聲明一個騰訊地圖對象private boolean isFirstLoc = true; // 是否首次定位private Button btn_getCenter; // 獲取中心點按鈕private TextView tv_centerPoint; // 中心點標記private LatLng mMyLatLng, mCenterLatLng; // 我的位置, 地圖中心位置@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_map_basic);initLocation(); // 初始化定位服務initView(); // 初始化視圖}// 初始化視圖private void initView() {tv_centerPoint = findViewById(R.id.tv_centerPoint);tv_centerPoint.setVisibility(View.INVISIBLE);btn_getCenter = findViewById(R.id.btn_getCenter);btn_getCenter.setEnabled(false);RadioGroup rg_type = findViewById(R.id.rg_type);rg_type.setOnCheckedChangeListener((group, checkedId) -> {if (checkedId == R.id.rb_common) {mTencentMap.setMapType(TencentMap.MAP_TYPE_NORMAL); // 設置普通地圖} else if (checkedId == R.id.rb_satellite) {mTencentMap.setMapType(TencentMap.MAP_TYPE_SATELLITE); // 設置衛星地圖}});CheckBox ck_traffic = findViewById(R.id.ck_traffic);ck_traffic.setOnCheckedChangeListener((buttonView, isChecked) -> {mTencentMap.setTrafficEnabled(isChecked); // 是否顯示交通擁堵狀況});CheckBox ck_centerPoint = findViewById(R.id.ck_centerPoint);ck_centerPoint.setOnCheckedChangeListener((buttonView, isChecked) -> {if (isChecked) {tv_centerPoint.setVisibility(View.VISIBLE);btn_getCenter.setEnabled(true);} else {tv_centerPoint.setVisibility(View.INVISIBLE);btn_getCenter.setEnabled(false);}});// 設置按鈕監聽器findViewById(R.id.tv_enlarge).setOnClickListener(this); // 放大地圖findViewById(R.id.tv_narrow).setOnClickListener(this); // 縮小地圖findViewById(R.id.img_btn_myPlace).setOnClickListener(this); // 回到我的位置findViewById(R.id.btn_getCenter).setOnClickListener(this); // 獲取中心點btn_getCenter.setOnClickListener(this); // 獲取中心點}// 初始化定位服務private void initLocation() {mMapView = findViewById(R.id.mapView);mTencentMap = mMapView.getMap(); // 獲取騰訊地圖對象UiSettings mysetting = mTencentMap.getUiSettings();mysetting.setCompassEnabled(true); // 開啟指南針mLocationManager = TencentLocationManager.getInstance(this);// 創建騰訊定位請求對象TencentLocationRequest request = TencentLocationRequest.create();request.setInterval(30000).setAllowGPS(true);request.setRequestLevel(TencentLocationRequest.REQUEST_LEVEL_ADMIN_AREA);mLocationManager.requestLocationUpdates(request, this); // 開始定位監聽}@Overridepublic void onLocationChanged(TencentLocation location, int resultCode, String resultDesc) {if (resultCode == TencentLocation.ERROR_OK) { // 定位成功if (location != null && isFirstLoc) { // 首次定位isFirstLoc = false;// 創建一個經緯度對象mMyLatLng = new LatLng(location.getLatitude(), location.getLongitude());CameraUpdate update = CameraUpdateFactory.newLatLngZoom(mMyLatLng, 12);mTencentMap.moveCamera(update); // 把相機視角移動到指定地點// 從指定圖片中獲取位圖描述BitmapDescriptor bitmapDesc = BitmapDescriptorFactory.fromResource(R.drawable.icon_locate);MarkerOptions ooMarker = new MarkerOptions(mMyLatLng).draggable(false) // 不可拖動.visible(true).icon(bitmapDesc).snippet("這是您的當前位置");mTencentMap.addMarker(ooMarker); // 往地圖添加標記}} else { // 定位失敗Log.d(TAG, "定位失敗,錯誤代碼為"+resultCode+",錯誤描述為"+resultDesc);}}@Overridepublic void onStatusUpdate(String s, int i, String s1) {}// 綁定生命周期@Overrideprotected void onStart() {super.onStart();mMapView.onStart();}@Overrideprotected void onStop() {super.onStop();mMapView.onStop();}@Overridepublic void onPause() {super.onPause();mMapView.onPause();}@Overridepublic void onResume() {super.onResume();mMapView.onResume();}@Overrideprotected void onDestroy() {super.onDestroy();mLocationManager.removeUpdates(this); // 移除定位監聽mMapView.onDestroy();}@Overridepublic void onClick(View v) {if (v.getId() == R.id.tv_enlarge) { // 放大地圖mTencentMap.moveCamera(CameraUpdateFactory.zoomIn()); // 放大一級} else if (v.getId() == R.id.tv_narrow) { // 縮小地圖mTencentMap.moveCamera(CameraUpdateFactory.zoomOut()); // 縮小一級} else if (v.getId() == R.id.img_btn_myPlace) { // 回到我的位置CameraUpdate update = CameraUpdateFactory.newLatLng(mMyLatLng);mTencentMap.moveCamera(update); // 把相機視角移動到指定地點} else if (v.getId() == R.id.btn_getCenter) { // 獲取中心點坐標CameraPosition cameraPosition = mTencentMap.getCameraPosition();mCenterLatLng = cameraPosition.target;Toast.makeText(this, "中心點坐標:" + mCenterLatLng.latitude + ";" + mCenterLatLng.longitude, Toast.LENGTH_LONG).show();}}
}
activity_map_basic.xml文件
<?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:orientation="vertical"tools:context=".MapBasicActivity"><RadioGroupandroid:id="@+id/rg_type"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" ><RadioButtonandroid:id="@+id/rb_common"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:checked="true"android:text="普通"android:textColor="@color/black"android:textSize="17sp" /><RadioButtonandroid:id="@+id/rb_satellite"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:checked="false"android:text="衛星"android:textColor="@color/black"android:textSize="17sp" /><CheckBoxandroid:id="@+id/ck_traffic"android:layout_width="wrap_content"android:layout_height="wrap_content"android:checked="false"android:text="交通情況"android:textColor="@color/black"android:textSize="17sp" /><CheckBoxandroid:id="@+id/ck_centerPoint"android:layout_width="35dp"android:layout_height="wrap_content"android:layout_marginStart="10dp"android:checked="false"android:text=""android:textColor="@color/black"android:textSize="17sp" /><Buttonandroid:id="@+id/btn_getCenter"android:layout_width="80dp"android:layout_height="wrap_content"android:text="中心點"/></RadioGroup><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1" ><com.tencent.tencentmap.mapsdk.maps.MapViewandroid:id="@+id/mapView"android:layout_width="match_parent"android:layout_height="fill_parent" /><LinearLayoutandroid:layout_width="55dp"android:layout_height="wrap_content"android:layout_above="@+id/mapView"android:layout_alignEnd="@+id/mapView"android:layout_alignBottom="@+id/mapView"android:layout_marginEnd="20dp"android:layout_marginBottom="110dp"android:background="@drawable/radius_border_15"android:orientation="vertical"><TextViewandroid:id="@+id/tv_enlarge"android:layout_width="50dp"android:layout_height="50dp"android:layout_gravity="center_horizontal"android:layout_marginTop="15dp"android:layout_marginBottom="15dp"android:gravity="center"android:text="+"android:textColor="@color/black"android:textSize="32sp"android:textStyle="bold" /><TextViewandroid:id="@+id/tv_narrow"android:layout_width="50dp"android:layout_height="50dp"android:layout_gravity="center_horizontal"android:layout_marginTop="15dp"android:layout_marginBottom="15dp"android:gravity="center"android:text="-"android:textColor="@color/black"android:textSize="32sp"android:textStyle="bold" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignStart="@+id/mapView"android:layout_alignBottom="@+id/mapView"android:background="@drawable/radius_border_15"android:layout_marginStart="20dp"android:layout_marginBottom="50dp"android:orientation="horizontal"><ImageButtonandroid:id="@+id/img_btn_myPlace"android:layout_width="54dp"android:layout_height="54dp"android:backgroundTint="@color/white"android:src="@drawable/ic_my_place"tools:ignore="ContentDescription" /></LinearLayout><TextViewandroid:id="@+id/tv_centerPoint"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:gravity="center"android:layout_centerInParent="true"android:textSize="48sp"android:textColor="@color/red"android:text="+" /></RelativeLayout></LinearLayout>