Android 基于高德 SDK开发的导航项目(入门级)
项目实现了一个功能完备的导航体系,包含:
- 地图显示和控制
- 门路规划和导航
- 语音播报功能
- 实时路况监控
- 安全提醒体系
- 气候信息和预警
- 智能出行建议
一、高德地图KEY的申请
①、进入高德的控制台:https://tengyun-console.amap.com
②、完成注册后,点击右边的 应用管理→我的应用→创建新应用→获取KEY (不会的就跟着我下面的步骤来,包会的)
如何获取自己的测试版安全码SHA1:
上述的命令:keytool -list -v -keystore debug.keystore
二、配置安卓干系权限和接入高德KEY
通过上述的操纵我们已经获取了高德地图的KEY,接下来就是对KEY的接入和对项目的权限配置
1、应用高德KEY
在AndroidManifest.xml中标签里面但不要在activity 标签下粘贴:
- <meta-data
- android:name="com.amap.api.v2.apikey"
- android:value="换成自己的高德KEY" />
复制代码 2、配置权限
这些是大概会用到的权限,请在AndroidManifest.xml中粘贴(不要在标签里面粘贴,会报错):
- <!-- 允许程序访问互联网 -->
- <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" />
- <!-- 允许程序通过GPS芯片接收卫星的定位信息 -->
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <!-- 允许程序修改全局音频设置 -->
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
- <!-- 允许程序录制音频,用于语音识别功能 -->
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
- <!-- 允许程序控制手机振动器 -->
- <uses-permission android:name="android.permission.VIBRATE" />
复制代码 三、Android 地图SDK 干系下载与引入
①登录高德地图站点:https://lbs.amap.com/api/android-sdk/download
②将下载的压缩包进行压缩(最好存放在一起我们等会要用)
③引入 JAR包到Android
创建一个libs:
选中 JAR包并复制粘贴到libs下,右击这个包点击菜单栏里的“Add AS Library…”
这一步实在就是修改App应用下的“build.gradle”文件,添加了一个依赖 JAR包
在build.gradle(app)里应该会有这一行代码:implementation(files(“libs\AMap3DMap_10.1.200_AMapNavi_10.1.200_AMapSearch_9.7.4_AMapLocation_6.4.9_20241226.jar”))
④在Android应用的app/src/main目录下新建一个jniLibs子目录
末了就是这样的,到这里就算完成了配置,我们要正式开始写程序了
四、开发地图导航功能
1、地图的初始化创建(导入地图)
MainActivity里的代码
- import android.Manifest;
- import android.content.pm.PackageManager;
- import android.graphics.Color;
- import android.os.Bundle;
- import android.widget.Toast;
- import androidx.annotation.NonNull;
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.core.app.ActivityCompat;
- import androidx.core.content.ContextCompat;
- import com.amap.api.maps.AMap;
- import com.amap.api.maps.MapView;
- import com.amap.api.maps.MapsInitializer;
- import com.amap.api.maps.model.MyLocationStyle;
- import com.amap.api.maps.CameraUpdateFactory;
- /**
- * 主活动类
- * 用于展示高德地图的主界面
- */
- public class MainActivity extends AppCompatActivity {
- // 地图视图组件
- private MapView mapView;
- // 地图控制器对象
- private AMap aMap;
- // 定位权限请求码
- private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
- // 需要进行检测的权限数组
- private static final String[] NEEDED_PERMISSIONS = {
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_COARSE_LOCATION
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // 设置高德地图隐私政策
- // 更新同意隐私状态,需要在初始化地图之前完成
- //如果没有这两行代码,你的地图可能会不显示
- MapsInitializer.updatePrivacyShow(this, true, true); // 展示隐私合规对话框
- MapsInitializer.updatePrivacyAgree(this, true); // 同意隐私合规政策
-
- setContentView(R.layout.activity_main);
-
- // 初始化地图组件
- mapView = findViewById(R.id.map);
- // 必须回调MapView的onCreate方法,否则地图无法显示
- mapView.onCreate(savedInstanceState);
-
- // 初始化地图控制器
- // 只有在aMap为空时才重新获取,避免重复初始化
- if (aMap == null) {
- aMap = mapView.getMap();
- // 设置地图UI控件
- setupMapUI();
- }
- // 检查并请求定位权限
- checkAndRequestPermissions();
- }
- /**
- * 设置地图UI和定位样式
- */
- private void setupMapUI() {
- // 设置定位蓝点的样式
- MyLocationStyle myLocationStyle = new MyLocationStyle();
- // 连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动
- myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);
- // 设置定位蓝点的Style
- myLocationStyle.strokeColor(Color.argb(180, 3, 145, 255));// 设置圆形的边框颜色
- myLocationStyle.radiusFillColor(Color.argb(10, 0, 0, 180));// 设置圆形的填充颜色
- myLocationStyle.strokeWidth(5.0f);// 设置圆形的边框粗细
- // 设置定位蓝点的 Style
- aMap.setMyLocationStyle(myLocationStyle);
- // 设置定位监听
- aMap.setOnMyLocationChangeListener(location -> {
- // 当位置发生变化时,将地图中心点移动到当前位置
- aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
- new com.amap.api.maps.model.LatLng(location.getLatitude(), location.getLongitude()),
- 17)); // 设置缩放级别为17
- });
- }
- /**
- * 检查并请求定位权限
- */
- private void checkAndRequestPermissions() {
- if (lacksPermissions(NEEDED_PERMISSIONS)) {
- ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, LOCATION_PERMISSION_REQUEST_CODE);
- } else {
- // 已经有权限,启用定位图层
- enableMyLocation();
- }
- }
- /**
- * 检查是否缺少权限
- */
- private boolean lacksPermissions(String... permissions) {
- for (String permission : permissions) {
- if (ContextCompat.checkSelfPermission(this, permission) !=
- PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
- /**
- * 处理权限请求结果
- */
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
- if (grantResults.length > 0) {
- boolean allGranted = true;
- for (int grantResult : grantResults) {
- if (grantResult != PackageManager.PERMISSION_GRANTED) {
- allGranted = false;
- break;
- }
- }
- if (allGranted) {
- // 获得权限,启用定位图层
- enableMyLocation();
- } else {
- Toast.makeText(this, "需要定位权限才能显示当前位置", Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
- /**
- * 启用定位图层
- */
- private void enableMyLocation() {
- // 设置为true表示启动显示定位蓝点
- aMap.setMyLocationEnabled(true);
- }
- /**
- * Activity生命周期方法 - 恢复
- * 必须回调MapView的onResume方法,否则地图会处于暂停状态
- */
- @Override
- protected void onResume() {
- super.onResume();
- mapView.onResume();
- }
- /**
- * Activity生命周期方法 - 暂停
- * 必须回调MapView的onPause方法,暂停地图的绘制
- */
- @Override
- protected void onPause() {
- super.onPause();
- mapView.onPause();
- }
- /**
- * Activity生命周期方法 - 保存状态
- * 保存地图当前的状态,在重建Activity时恢复状态
- */
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
- /**
- * Activity生命周期方法 - 销毁
- * 必须回调MapView的onDestroy方法,销毁地图,释放资源
- */
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
- }
复制代码 activity_main.xml
- <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity">
- <com.amap.api.maps.MapView
- android:id="@+id/map"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent" />
- </androidx.constraintlayout.widget.ConstraintLayout>
复制代码 通过上面的代码,我们能实现地图的导入,也乐成的实现了这个项目的第一步(用真机进行测试,虚拟机会报错)
2、实现门路规划和导航
①类定义和接口实现
- public class MainActivity extends AppCompatActivity implements
- RouteSearch.OnRouteSearchListener, // 路线搜索监听
- AMapNaviListener // 导航监听
复制代码 写完以后会有这样的报错:那是因为接口里的方法没有写全
解决步骤:
将鼠标放在这行代码上,点击Alt+Enter,Android会自动告诉你解决方法
这样Android就会帮我补全缺失的方法了
②创建一个可以拉动的半透明的面板
在res/layout下创建一个layout Resource File文件 名字叫 bottom_sheet_layout
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/bottom_sheet"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#F5FFFFFF"
- android:orientation="vertical"
- android:padding="16dp"
- app:behavior_hideable="false"
- app:behavior_peekHeight="180dp"
- app:behavior_draggable="true"
- app:behavior_fitToContents="true"
- app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
- <!-- 拖动条指示器 -->
- <View
- android:layout_width="40dp"
- android:layout_height="4dp"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="16dp"
- android:background="#CCCCCC" />
- </LinearLayout>
复制代码 ③创建一个搜索框
在res/drawable下创建一个search_background
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="#F5F5F5" />
- <corners android:radius="24dp" />
- <stroke
- android:width="1dp"
- android:color="#E0E0E0" />
- </shape>
复制代码 在Activity_main.xml文件
- <!-- 搜索框区域 -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="8dp"
- android:layout_marginHorizontal="16dp"
- android:layout_marginBottom="8dp"
- android:background="@drawable/search_background"
- android:elevation="2dp">
- <!-- 搜索图标 -->
- <ImageView
- android:id="@+id/search_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="8dp"
- android:src="@android:drawable/ic_search_category_default"
- app:tint="#666666" />
- <!-- 搜索输入框 -->
- <EditText
- android:id="@+id/search_input"
- android:layout_width="0dp"
- android:layout_height="40dp"
- android:layout_weight="1"
- android:background="@null"
- android:hint="搜索目的地"
- android:textColorHint="#999999"
- android:textColor="#333333"
- android:textSize="16sp"
- android:singleLine="true"
- android:imeOptions="actionSearch"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp" />
- </LinearLayout>
复制代码 ④规划和导航功能完备的代码
- 导航初始化 :
在 MainActivity.java 中,通过 initNaviAndRoute 方法初始化了导航功能,使用了高德地图的AMapNavi SDK。
- 门路规划 :
通过 planRoute 方法进行门路规划,支持驾车导航。
- 导航控制 :
在底部面板中有一个"开始导航"按钮,点击后会调用 startNavi 方法启动真实导航。
- 导航事件监听 :
实现了 AMapNaviListener 接口,处理惩罚各种导航事件,如:
- 导航初始化乐成/失败
- 开始导航
- 到达目的地
- 门路盘算乐成/失败
- 位置变革等
- 门路绘制 :
通过 drawRouteLine 方法在地图上绘制导航门路。
- 导航信息展示 :
在底部面板中显示导航干系信息,包括目的地、距离和预计时间,通过 updateBottomSheetInfo 方法更新。
- import android.Manifest;
- import android.content.Context;
- import android.content.pm.PackageManager;
- import android.graphics.Color;
- import android.os.Bundle;
- import android.text.TextUtils;
- import android.view.View;
- import android.view.inputmethod.EditorInfo;
- import android.view.inputmethod.InputMethodManager;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.TextView;
- import android.widget.Toast;
- import androidx.annotation.NonNull;
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.core.app.ActivityCompat;
- import androidx.core.content.ContextCompat;
- import com.amap.api.maps.AMap;
- import com.amap.api.maps.MapView;
- import com.amap.api.maps.MapsInitializer;
- import com.amap.api.maps.model.MyLocationStyle;
- import com.amap.api.maps.CameraUpdateFactory;
- import com.amap.api.maps.model.LatLng;
- import com.amap.api.maps.model.Marker;
- import com.amap.api.maps.model.MarkerOptions;
- import com.amap.api.navi.AMapNavi;
- import com.amap.api.navi.AMapNaviListener;
- import com.amap.api.navi.enums.PathPlanningStrategy;
- import com.amap.api.navi.model.AMapCalcRouteResult;
- import com.amap.api.navi.model.AMapLaneInfo;
- import com.amap.api.navi.model.AMapModelCross;
- import com.amap.api.navi.model.AMapNaviCameraInfo;
- import com.amap.api.navi.model.AMapNaviCross;
- import com.amap.api.navi.model.AMapNaviLocation;
- import com.amap.api.navi.model.AMapNaviPath;
- import com.amap.api.navi.model.AMapNaviRouteNotifyData;
- import com.amap.api.navi.model.AMapNaviTrafficFacilityInfo;
- import com.amap.api.navi.model.AMapServiceAreaInfo;
- import com.amap.api.navi.model.AimLessModeCongestionInfo;
- import com.amap.api.navi.model.AimLessModeStat;
- import com.amap.api.navi.model.NaviInfo;
- import com.amap.api.navi.model.NaviLatLng;
- 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.route.BusRouteResult;
- import com.amap.api.services.route.DriveRouteResult;
- import com.amap.api.services.route.RideRouteResult;
- import com.amap.api.services.route.RouteSearch;
- import com.amap.api.services.route.WalkRouteResult;
- import com.google.android.material.bottomsheet.BottomSheetBehavior;
- import com.amap.api.services.poisearch.PoiSearch;
- import com.amap.api.services.poisearch.PoiResult;
- import com.amap.api.maps.model.PolylineOptions;
- import com.amap.api.maps.model.LatLngBounds;
- import java.util.ArrayList;
- import java.util.List;
- public class MainActivity extends AppCompatActivity implements RouteSearch.OnRouteSearchListener, AMapNaviListener, PoiSearch.OnPoiSearchListener {
- // 地图视图组件
- private MapView mapView;
- // 地图控制器对象
- private AMap aMap;
- // 导航控制器对象
- private AMapNavi mAMapNavi;
- // 路线搜索对象
- private RouteSearch routeSearch;
- // 目的地标记
- private Marker destinationMarker;
- // 定位权限请求码
- private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
- // 需要进行检测的权限数组
- private static final String[] NEEDED_PERMISSIONS = {
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_COARSE_LOCATION
- };
- private BottomSheetBehavior<View> bottomSheetBehavior;
- private TextView tvDestination, tvDistance, tvTime;
- private Button btnStartNavi;
- private LatLonPoint destinationPoint; // 保存目的地坐标
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- try {
- MapsInitializer.updatePrivacyShow(this, true, true);
- MapsInitializer.updatePrivacyAgree(this, true);
-
- setContentView(R.layout.activity_main);
-
- // 初始化底部面板
- initBottomSheet();
-
- mapView = findViewById(R.id.map);
- mapView.onCreate(savedInstanceState);
-
- if (aMap == null) {
- aMap = mapView.getMap();
- setupMapUI();
- }
- initNaviAndRoute();
- checkAndRequestPermissions();
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "初始化失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- /**
- * 设置地图UI和定位样式
- */
- private void setupMapUI() {
- try {
- // 设置定位蓝点的样式
- MyLocationStyle myLocationStyle = new MyLocationStyle();
- // 连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动
- myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);
- // 设置定位蓝点的Style
- myLocationStyle.strokeColor(Color.argb(180, 3, 145, 255));// 设置圆形的边框颜色
- myLocationStyle.radiusFillColor(Color.argb(10, 0, 0, 180));// 设置圆形的填充颜色
- myLocationStyle.strokeWidth(5.0f);// 设置圆形的边框粗细
- // 设置定位蓝点的 Style
- aMap.setMyLocationStyle(myLocationStyle);
- // 设置定位监听
- aMap.setOnMyLocationChangeListener(location -> {
- // 当位置发生变化时,将地图中心点移动到当前位置
- aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
- new com.amap.api.maps.model.LatLng(location.getLatitude(), location.getLongitude()),
- 17)); // 设置缩放级别为17
- });
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "设置地图UI失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- /**
- * 初始化导航和路线搜索
- */
- private void initNaviAndRoute() {
- try {
- // 初始化路线规划
- routeSearch = new RouteSearch(this);
- routeSearch.setRouteSearchListener(this);
- // 初始化导航
- mAMapNavi = AMapNavi.getInstance(getApplicationContext());
- mAMapNavi.addAMapNaviListener(this);
- // 设置地图点击监听,用于选择目的地
- aMap.setOnMapClickListener(latLng -> {
- try {
- // 清除之前的标记
- if (destinationMarker != null) {
- destinationMarker.remove();
- }
- // 添加新的目的地标记
- MarkerOptions markerOptions = new MarkerOptions()
- .position(latLng)
- .title("目的地");
- destinationMarker = aMap.addMarker(markerOptions);
- // 开始路线规划
- planRoute(latLng);
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "设置目的地失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "初始化导航失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- /**
- * 检查并请求定位权限
- */
- private void checkAndRequestPermissions() {
- if (lacksPermissions(NEEDED_PERMISSIONS)) {
- ActivityCompat.requestPermissions(this, NEEDED_PERMISSIONS, LOCATION_PERMISSION_REQUEST_CODE);
- } else {
- // 已经有权限,启用定位图层
- enableMyLocation();
- }
- }
- /**
- * 检查是否缺少权限
- */
- private boolean lacksPermissions(String... permissions) {
- for (String permission : permissions) {
- if (ContextCompat.checkSelfPermission(this, permission) !=
- PackageManager.PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
- /**
- * 处理权限请求结果
- */
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
- if (grantResults.length > 0) {
- boolean allGranted = true;
- for (int grantResult : grantResults) {
- if (grantResult != PackageManager.PERMISSION_GRANTED) {
- allGranted = false;
- break;
- }
- }
- if (allGranted) {
- // 获得权限,启用定位图层
- enableMyLocation();
- } else {
- Toast.makeText(this, "需要定位权限才能显示当前位置", Toast.LENGTH_SHORT).show();
- }
- }
- }
- }
- /*启用定位图层*/
- private void enableMyLocation() {
- // 设置为true表示启动显示定位蓝点
- aMap.setMyLocationEnabled(true);
- }
- @Override
- protected void onResume() {
- super.onResume();
- mapView.onResume();
- }
- @Override
- protected void onPause() {
- super.onPause();
- mapView.onPause();
- }
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- try {
- if (mapView != null) {
- mapView.onDestroy();
- }
- if (mAMapNavi != null) {
- mAMapNavi.destroy();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onInitNaviFailure() {
- // 导航初始化失败
- Toast.makeText(this, "导航初始化失败", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onInitNaviSuccess() {
- // 导航初始化成功
- Toast.makeText(this, "导航初始化成功", Toast.LENGTH_SHORT).show();
- }
- @Override
- public void onStartNavi(int type) {
- // 开始导航回调
- Toast.makeText(this, "开始导航", Toast.LENGTH_SHORT).show();
- }
- @Override public void onTrafficStatusUpdate() {}
- @Override
- public void onLocationChange(AMapNaviLocation location) {
- // 导航过程中位置变化
- }
- @Override public void onGetNavigationText(int i, String s) {}
- @Override public void onGetNavigationText(String s) {}
- @Override public void onEndEmulatorNavi() {}
- @Override
- public void onArriveDestination() {
- // 到达目的地
- Toast.makeText(this, "到达目的地", Toast.LENGTH_SHORT).show();
- }
- @Override public void onCalculateRouteFailure(int i) {}
- @Override public void onReCalculateRouteForYaw() {}
- @Override public void onReCalculateRouteForTrafficJam() {}
- @Override
- public void onArrivedWayPoint(int i) {}
- @Override public void onGpsOpenStatus(boolean b) {}
- @Override public void onNaviInfoUpdate(NaviInfo naviInfo) {}
- @Override
- public void updateCameraInfo(AMapNaviCameraInfo[] aMapNaviCameraInfos) {}
- @Override
- public void updateIntervalCameraInfo(AMapNaviCameraInfo aMapNaviCameraInfo, AMapNaviCameraInfo aMapNaviCameraInfo1, int i) {}
- @Override public void onServiceAreaUpdate(AMapServiceAreaInfo[] aMapServiceAreaInfos) {}
- @Override public void showCross(AMapNaviCross aMapNaviCross) {}
- @Override public void hideCross() {}
- @Override public void showModeCross(AMapModelCross aMapModelCross) {}
- @Override public void hideModeCross() {}
- @Override public void showLaneInfo(AMapLaneInfo[] aMapLaneInfos, byte[] bytes, byte[] bytes1) {}
- @Override public void showLaneInfo(AMapLaneInfo aMapLaneInfo) {}
- @Override public void hideLaneInfo() {}
- @Override public void onCalculateRouteSuccess(int[] ints) {}
- @Override public void notifyParallelRoad(int i) {}
- @Override public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] aMapNaviTrafficFacilityInfos) {}
- @Override public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo aMapNaviTrafficFacilityInfo) {}
- @Override public void updateAimlessModeStatistics(AimLessModeStat aimLessModeStat) {}
- @Override public void updateAimlessModeCongestionInfo(AimLessModeCongestionInfo aimLessModeCongestionInfo) {}
- @Override public void onPlayRing(int i) {}
- @Override
- public void onCalculateRouteSuccess(AMapCalcRouteResult result) {
- Toast.makeText(this, "路线规划成功", Toast.LENGTH_SHORT).show();
- AMapNaviPath path = mAMapNavi.getNaviPath();
- if (path != null) {
- // 更新底部面板信息
- updateBottomSheetInfo(path);
- // 绘制路线
- drawRouteLine(path);
- }
- }
- @Override
- public void onCalculateRouteFailure(AMapCalcRouteResult result) {
- // 路线规划失败
- Toast.makeText(this, "路线规划失败:" + result.getErrorDetail(), Toast.LENGTH_SHORT).show();
- }
- @Override public void onNaviRouteNotify(AMapNaviRouteNotifyData aMapNaviRouteNotifyData) {}
- @Override public void onGpsSignalWeak(boolean b) {}
- @Override public void onBusRouteSearched(BusRouteResult result, int errorCode) {}
- @Override public void onDriveRouteSearched(DriveRouteResult result, int errorCode) {}
- @Override public void onWalkRouteSearched(WalkRouteResult result, int errorCode) {}
- @Override public void onRideRouteSearched(RideRouteResult result, int errorCode) {}
- private void planRoute(LatLng destination) {
- try {
- if (aMap.getMyLocation() == null) {
- Toast.makeText(this, "等待定位信息...", Toast.LENGTH_SHORT).show();
- return;
- }
- // 获取起点(当前位置)
- LatLonPoint startPoint = new LatLonPoint(
- aMap.getMyLocation().getLatitude(),
- aMap.getMyLocation().getLongitude()
- );
- // 终点
- LatLonPoint endPoint = new LatLonPoint(destination.latitude, destination.longitude);
- // 构建导航数据
- List<NaviLatLng> startList = new ArrayList<>();
- List<NaviLatLng> endList = new ArrayList<>();
- startList.add(new NaviLatLng(startPoint.getLatitude(), startPoint.getLongitude()));
- endList.add(new NaviLatLng(endPoint.getLatitude(), endPoint.getLongitude()));
- // 开始算路
- mAMapNavi.calculateDriveRoute(startList, endList, null, 2);
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "路线规划失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- private void initBottomSheet() {
- try {
- View bottomSheet = findViewById(R.id.bottom_sheet);
- bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
-
- // 设置初始状态为折叠
- bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
-
- // 初始化视图
- tvDestination = findViewById(R.id.tv_destination);
- tvDistance = findViewById(R.id.tv_distance);
- tvTime = findViewById(R.id.tv_time);
- btnStartNavi = findViewById(R.id.btn_start_navi);
- // 初始化搜索框
- EditText searchInput = findViewById(R.id.search_input);
- searchInput.setOnEditorActionListener((v, actionId, event) -> {
- if (actionId == EditorInfo.IME_ACTION_SEARCH) {
- String keyword = searchInput.getText().toString().trim();
- if (!TextUtils.isEmpty(keyword)) {
- // 隐藏键盘
- InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
-
- // 开始搜索
- searchLocation(keyword);
- return true;
- }
- }
- return false;
- });
- // 设置底部面板的状态改变监听
- bottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
- @Override
- public void onStateChanged(@NonNull View bottomSheet, int newState) {
- switch (newState) {
- case BottomSheetBehavior.STATE_COLLAPSED:
- // 面板折叠状态
- break;
- case BottomSheetBehavior.STATE_EXPANDED:
- // 面板展开状态
- break;
- }
- }
- @Override
- public void onSlide(@NonNull View bottomSheet, float slideOffset) {
- // 滑动时的处理
- }
- });
- // 设置开始导航按钮点击事件
- btnStartNavi.setOnClickListener(v -> {
- if (mAMapNavi != null) {
- mAMapNavi.startNavi(1); // 1表示真实导航
- bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "初始化底部面板失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- /**搜索位置*/
- private void searchLocation(String keyword) {
- try {
- Toast.makeText(this, "正在搜索: " + keyword, Toast.LENGTH_SHORT).show();
-
- // 创建POI搜索对象
- PoiSearch.Query query = new PoiSearch.Query(keyword, "", "全国"); // 设置搜索范围为全国
- query.setPageSize(10); // 设置每页最多返回多少条数据
- query.setPageNum(1); // 设置查询第几页,从1开始
- PoiSearch poiSearch = new PoiSearch(this, query);
- poiSearch.setOnPoiSearchListener(this);
-
- // 如果当前位置可用,设置搜索中心点
- if (aMap != null && aMap.getMyLocation() != null) {
- poiSearch.setBound(new PoiSearch.SearchBound(
- new LatLonPoint(aMap.getMyLocation().getLatitude(),
- aMap.getMyLocation().getLongitude()),
- 50000)); // 设置搜索半径为50公里
- }
-
- poiSearch.searchPOIAsyn(); // 开始异步搜索
- } catch (AMapException e) {
- e.printStackTrace();
- Toast.makeText(this, "搜索初始化失败:" + e.getErrorMessage(), Toast.LENGTH_SHORT).show();
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(this, "搜索失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
- }
- @Override
- public void onPoiSearched(PoiResult result, int rCode) {
- if (rCode == AMapException.CODE_AMAP_SUCCESS) {
- if (result != null && result.getPois() != null && !result.getPois().isEmpty()) {
- // 获取第一个POI点
- PoiItem poi = result.getPois().get(0);
- // 更新目的地信息
- updateDestinationInfo(poi);
- // 移动地图到搜索位置
- moveMapToLocation(poi.getLatLonPoint());
- // 规划路线
- planRouteToDestination(poi.getLatLonPoint());
- } else {
- Toast.makeText(this, "未找到相关地点,请尝试其他关键词", Toast.LENGTH_SHORT).show();
- }
- } else {
- String errorMessage;
- switch (rCode) {
- case AMapException.CODE_AMAP_ENGINE_RESPONSE_ERROR:
- errorMessage = "服务响应错误";
- break;
- case AMapException.CODE_AMAP_ENGINE_CONNECT_TIMEOUT:
- errorMessage = "连接超时";
- break;
- case AMapException.CODE_AMAP_ENGINE_RETURN_TIMEOUT:
- errorMessage = "请求超时";
- break;
- default:
- errorMessage = "未知错误(错误码:" + rCode + ")";
- }
- Toast.makeText(this, "搜索失败:" + errorMessage, Toast.LENGTH_SHORT).show();
- }
- }
- @Override
- public void onPoiItemSearched(PoiItem poiItem, int rCode) {
- // 处理单个POI搜索结果
- }
- private void updateDestinationInfo(PoiItem poi) {
- tvDestination.setText("目的地:" + poi.getTitle());
- // 保存目的地信息,用于后续导航
- destinationPoint = poi.getLatLonPoint();
- // 展开面板
- bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
- }
- private void moveMapToLocation(LatLonPoint point) {
- if (aMap != null) {
- // 清除之前的标记
- aMap.clear();
- // 添加新的标记
- LatLng latLng = new LatLng(point.getLatitude(), point.getLongitude());
- aMap.addMarker(new MarkerOptions()
- .position(latLng)
- .title("目的地"));
- // 移动地图
- aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
- }
- }
- private void planRouteToDestination(LatLonPoint destination) {
- if (aMap.getMyLocation() == null) {
- Toast.makeText(this, "无法获取当前位置", Toast.LENGTH_SHORT).show();
- return;
- }
- // 构建导航起终点
- List<NaviLatLng> startList = new ArrayList<>();
- List<NaviLatLng> endList = new ArrayList<>();
- // 起点为当前位置
- startList.add(new NaviLatLng(aMap.getMyLocation().getLatitude(),
- aMap.getMyLocation().getLongitude()));
- // 终点为搜索的位置
- endList.add(new NaviLatLng(destination.getLatitude(),
- destination.getLongitude()));
- // 开始算路
- mAMapNavi.calculateDriveRoute(startList, endList, null,
- PathPlanningStrategy.DRIVING_DEFAULT);
- }
- private void drawRouteLine(AMapNaviPath path) {
- if (aMap != null) {
- // 清除之前的路线
- aMap.clear();
- // 绘制新路线
- AMapNaviPath naviPath = mAMapNavi.getNaviPath();
- if (naviPath != null) {
- // 获取路线坐标点
- List<NaviLatLng> coordinates = naviPath.getCoordList();
- if (coordinates != null && coordinates.size() > 0) {
- List<LatLng> latLngs = new ArrayList<>();
- for (NaviLatLng coordinate : coordinates) {
- latLngs.add(new LatLng(coordinate.getLatitude(), coordinate.getLongitude()));
- }
- // 绘制路线
- aMap.addPolyline(new PolylineOptions()
- .addAll(latLngs)
- .width(20)
- .color(Color.BLUE));
-
- // 添加起点和终点标记
- LatLng startPoint = latLngs.get(0);
- LatLng endPoint = latLngs.get(latLngs.size() - 1);
-
- // 添加起点标记
- aMap.addMarker(new MarkerOptions()
- .position(startPoint)
- .title("起点"));
-
- // 添加终点标记
- aMap.addMarker(new MarkerOptions()
- .position(endPoint)
- .title("终点"));
- // 调整地图视野以显示整条路线
- LatLngBounds bounds = new LatLngBounds.Builder()
- .include(startPoint)
- .include(endPoint)
- .build();
- aMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
- }
- }
- }
- }
- private void updateBottomSheetInfo(AMapNaviPath path) {
- if (path != null) {
- // 更新距离信息(转换为公里)
- String distance = String.format("距离:%.1f公里", path.getAllLength() / 1000.0);
- tvDistance.setText(distance);
- // 更新时间信息(转换为分钟)
- String time = String.format("预计用时:%d分钟", path.getAllTime() / 60);
- tvTime.setText(time);
- // 如果有目的地标记,更新目的地信息
- if (destinationMarker != null) {
- String destination = "目的地:" + destinationMarker.getTitle();
- tvDestination.setText(destination);
- }
- }
- }
- }
复制代码 XML文件
- <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:context=".MainActivity"> <!-- 地图容器 --> <com.amap.api.maps.MapView android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" /> <!-- 底部导航面板 --> <LinearLayout android:id="@+id/bottom_sheet" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#F5FFFFFF" android:orientation="vertical" android:elevation="8dp" app:behavior_peekHeight="180dp" app:behavior_hideable="false" app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"> <!-- 拖动条指示器 --> <View android:layout_width="40dp" android:layout_height="4dp" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:layout_marginBottom="16dp" android:background="#CCCCCC" /> <!-- 搜索框区域 -->
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="8dp"
- android:layout_marginHorizontal="16dp"
- android:layout_marginBottom="8dp"
- android:background="@drawable/search_background"
- android:elevation="2dp">
- <!-- 搜索图标 -->
- <ImageView
- android:id="@+id/search_icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="center_vertical"
- android:layout_marginStart="8dp"
- android:src="@android:drawable/ic_search_category_default"
- app:tint="#666666" />
- <!-- 搜索输入框 -->
- <EditText
- android:id="@+id/search_input"
- android:layout_width="0dp"
- android:layout_height="40dp"
- android:layout_weight="1"
- android:background="@null"
- android:hint="搜索目的地"
- android:textColorHint="#999999"
- android:textColor="#333333"
- android:textSize="16sp"
- android:singleLine="true"
- android:imeOptions="actionSearch"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="8dp" />
- </LinearLayout>
- <!-- 导航信息区域 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <!-- 目的地信息 --> <TextView android:id="@+id/tv_destination" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="目的地:" android:textColor="#333333" android:textSize="16sp" android:textStyle="bold" /> <!-- 距离信息 --> <TextView android:id="@+id/tv_distance" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="距离:" android:textColor="#666666" android:textSize="16sp" /> <!-- 预计时间 --> <TextView android:id="@+id/tv_time" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:text="预计时间:" android:textColor="#666666" android:textSize="16sp" /> <!-- 导航按钮 --> <Button android:id="@+id/btn_start_navi" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:background="@android:color/holo_blue_light" android:elevation="4dp" android:padding="12dp" android:text="开始导航" android:textColor="#FFFFFF" android:textSize="16sp" /> </LinearLayout> </LinearLayout></androidx.coordinatorlayout.widget.CoordinatorLayout>
复制代码 末了实现的效果:

3、处理惩罚偏航
①onReCalculateRouteForYaw 方法(核心偏航处理惩罚方法)
- @Override
- public void onReCalculateRouteForYaw() {
- // 偏航重新计算路线回调
- requireActivity().runOnUiThread(() -> {
- String message = "您已偏离规划路线,正在重新规划";
- Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(message, TextToSpeech.QUEUE_FLUSH, null, null);
- }
- });
- }
复制代码 ②、calculateRoute 方法(门路重新规划)
- private void calculateRoute() {
- if (mStartPoint == null) {
- // 如果没有起点,使用当前位置
- Location location = aMap.getMyLocation();
- if (location != null) {
- mStartPoint = new LatLng(location.getLatitude(), location.getLongitude());
- } else {
- Toast.makeText(requireContext(), "无法获取当前位置", Toast.LENGTH_SHORT).show();
- return;
- }
- }
- try {
- // 清除之前的路线
- aMap.clear();
- // 准备导航起终点
- List<NaviLatLng> startPoints = new ArrayList<>();
- startPoints.add(new NaviLatLng(mStartPoint.latitude, mStartPoint.longitude));
- List<NaviLatLng> endPoints = new ArrayList<>();
- endPoints.add(new NaviLatLng(mEndPoint.latitude, mEndPoint.longitude));
- // 根据出行方式计算路线
- switch (currentNaviMode) {
- case 0: // 驾车
- mAMapNavi.calculateDriveRoute(startPoints, endPoints, null,
- PathPlanningStrategy.DRIVING_DEFAULT);
- break;
-
- }
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(requireContext(), "路线规划失败:" + e.getMessage(),
- Toast.LENGTH_SHORT).show();
- }
- }
复制代码 ③、onCalculateRouteSuccess 方法(门路重新规划乐成回调)
- @Override
- public void onCalculateRouteSuccess(AMapCalcRouteResult result) {
- if (result != null) {
- try {
- // 清除之前的标记和路线
- aMap.clear();
- mapPolylines.clear();
- // 添加起点和终点标记
- if (mStartPoint != null) {
- MarkerOptions startMarker = new MarkerOptions()
- .position(mStartPoint)
- .title("起点")
- .snippet("当前位置")
- .icon(BitmapDescriptorFactory.defaultMarker(
- BitmapDescriptorFactory.HUE_GREEN));
- aMap.addMarker(startMarker);
- }
- if (mEndPoint != null) {
- MarkerOptions endMarker = new MarkerOptions()
- .position(mEndPoint)
- .title("终点")
- .snippet("目的地")
- .icon(BitmapDescriptorFactory.defaultMarker(
- BitmapDescriptorFactory.HUE_RED));
- aMap.addMarker(endMarker);
- }
- // 获取所有路线ID并绘制路线
- int[] routeIds = result.getRouteid();
- if (routeIds != null && routeIds.length > 0) {
- currentRouteIds = routeIds;
- updateRouteOptions();
- drawSelectedRoute();
- }
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(requireContext(), "路线显示失败:" + e.getMessage(),
- Toast.LENGTH_SHORT).show();
- }
- }
- }
复制代码 ④、drawSelectedRoute 方法(绘制新门路)
- private void drawSelectedRoute() {
- try {
- // 选择路线
- mAMapNavi.selectRouteId(currentRouteIds[selectedRouteIndex]);
- // 清除之前的路线
- for (Polyline line : mapPolylines) {
- line.remove();
- }
- mapPolylines.clear();
- // 获取路线信息
- List<NaviLatLng> pathPoints = mAMapNavi.getNaviPath().getCoordList();
- if (pathPoints != null && !pathPoints.isEmpty()) {
- // 设置路线颜色
- int pathColor;
- switch (currentNaviMode) {
- case 1: // 步行
- pathColor = Color.GREEN;
- break;
- }
- // 转换坐标点并绘制路线
- List<LatLng> mapPoints = new ArrayList<>();
- for (NaviLatLng point : pathPoints) {
- mapPoints.add(new LatLng(point.getLatitude(), point.getLongitude()));
- }
- // 添加新路线
- Polyline polyline = aMap.addPolyline(new PolylineOptions()
- .addAll(mapPoints)
- .width(20)
- .color(pathColor)
- .zIndex(1)
- .setDottedLine(false));
- mapPolylines.add(polyline);
- // 调整地图视野以显示整条路线
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
- for (LatLng point : mapPoints) {
- builder.include(point);
- }
- aMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
- builder.build(), 100));
- }
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(requireContext(), "路线绘制失败:" + e.getMessage(),
- Toast.LENGTH_SHORT).show();
- }
- }
复制代码 五、其他功能
1、语音播报功能
① TextToSpeech 基本先容与用法
TextToSpeech 是 Android 提供的文字转语音引擎,可以将文本转换为语音输出。
1)初始化方式
- private TextToSpeech textToSpeech;
- // 方式一:基础初始化
- textToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
- @Override
- public void onInit(int status) {
- if (status == TextToSpeech.SUCCESS) {
- // 设置语言
- int result = textToSpeech.setLanguage(Locale.CHINESE);
-
- // 检查语言是否支持
- if (result == TextToSpeech.LANG_MISSING_DATA ||
- result == TextToSpeech.LANG_NOT_SUPPORTED) {
- Toast.makeText(context, "语言不支持", Toast.LENGTH_SHORT).show();
- }
- } else {
- Toast.makeText(context, "初始化失败", Toast.LENGTH_SHORT).show();
- }
- }
- });
- // 方式二:Lambda表达式(简化写法)
- textToSpeech = new TextToSpeech(requireContext(), status -> {
- if (status == TextToSpeech.SUCCESS) {
- textToSpeech.setLanguage(Locale.CHINESE);
- }
- });
复制代码 2)主要配置方法
- // 设置语言
- textToSpeech.setLanguage(Locale.CHINESE);
- // 设置语速(1.0为正常速度)
- textToSpeech.setSpeechRate(1.0f);
- // 设置音调(1.0为正常音调)
- textToSpeech.setPitch(1.0f);
- // 设置音量(0.0-1.0)
- textToSpeech.setVolume(1.0f);
- // 设置音频流类型
- textToSpeech.setAudioAttributes(new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
- .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
- .build());
复制代码 3)语音播报方法
- // 1. 基本播报方法
- textToSpeech.speak(
- "要播报的文本", // 文本内容
- TextToSpeech.QUEUE_FLUSH, // 播报模式
- null, // Bundle参数
- "utteranceId" // 话语ID
- );
- // 2. 立即播报(清除队列)
- textToSpeech.speak(
- "紧急提示信息",
- TextToSpeech.QUEUE_FLUSH,
- null,
- null
- );
- // 3. 加入播报队列
- textToSpeech.speak(
- "普通提示信息",
- TextToSpeech.QUEUE_ADD,
- null,
- null
- );
- // 4. 带参数的播报
- Bundle params = new Bundle();
- params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME, 0.8f);
- params.putFloat(TextToSpeech.Engine.KEY_PARAM_PITCH, 1.2f);
- textToSpeech.speak(
- "自定义参数播报",
- TextToSpeech.QUEUE_FLUSH,
- params,
- "customId"
- );
复制代码 4)播报状态监听
- textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
- @Override
- public void onStart(String utteranceId) {
- // 开始播报
- }
- @Override
- public void onDone(String utteranceId) {
- // 播报完成
- }
- @Override
- public void onError(String utteranceId) {
- // 播报错误
- }
- });
复制代码 5)权限要求
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
复制代码 ②使用示例(步骤分析)
1). 初始化语音体系
- // 在类成员变量中声明
- private TextToSpeech textToSpeech;
- // 在 onCreateView 中初始化
- textToSpeech = new TextToSpeech(requireContext(), status -> {
- if (status == TextToSpeech.SUCCESS) {
- // 设置语音为中文
- textToSpeech.setLanguage(Locale.CHINESE);
- }
- });
复制代码 2).导航语音播报实现
- //开始导航
- private void startNavi() {
- if (mStartPoint == null || mEndPoint == null) {
- Toast.makeText(requireContext(), "请先规划路线", Toast.LENGTH_SHORT).show();
- return;
- }
- try {
- mAMapNavi.selectRouteId(currentRouteIds[selectedRouteIndex]);
- mAMapNavi.startNavi(NaviType.GPS);
- isNavigating = true;
- updateNavigationButtons(true);
- showNavigationInfoPanel();
- // 这里可以添加开始导航的语音提示
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak("开始导航", TextToSpeech.QUEUE_FLUSH, null, null);
- }
-
- // ... 其他代码
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- //导航过程中的语音提示:
- @Override
- public void onGetNavigationText(String text) {
- try {
- // 这是导航过程中的语音播报核心方法
- if (!isMuted && textToSpeech != null && text != null) {
- textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, null);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- public void onGetNavigationText(int type, String text) {
- onGetNavigationText(text);
- }
- // 到达目的地提醒
- @Override
- public void onArriveDestination() {
- String message = "已到达目的地,本次导航结束";
- Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
- // 播放语音提醒
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(message, TextToSpeech.QUEUE_FLUSH, null, null);
- }
- // 震动提醒
- if (ContextCompat.checkSelfPermission(requireContext(),
- android.Manifest.permission.VIBRATE) == PackageManager.PERMISSION_GRANTED) {
- android.os.Vibrator vibrator = (android.os.Vibrator)
- requireContext().getSystemService(Context.VIBRATOR_SERVICE);
- if (vibrator != null && vibrator.hasVibrator()) {
- vibrator.vibrate(500); // 震动500毫秒
- }
- }
- }
复制代码 3). 特殊情况的语音提醒(按需选择)
- // 偏航重新规划提醒
- @Override
- public void onReCalculateRouteForYaw() {
- requireActivity().runOnUiThread(() -> {
- String message = "您已偏离规划路线,正在重新规划";
- Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(message, TextToSpeech.QUEUE_FLUSH, null, null);
- }
- });
- }
- // 拥堵重新规划提醒
- @Override
- public void onReCalculateRouteForTrafficJam() {
- requireActivity().runOnUiThread(() -> {
- String message = "前方道路拥堵,正在重新规划路线";
- Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(message, TextToSpeech.QUEUE_FLUSH, null, null);
- }
- });
- }
复制代码 4). 交通办法语音提醒
- @Override
- public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo facility) {
- if (facility == null || !isTrafficEnabled) return;
- String message = "";
- switch (facility.getBroadcastType()) {
- case 0: // 测速摄像头
- message = String.format("前方%d米有测速摄像头,限速%d公里/小时",
- facility.getDistance(), facility.getLimitSpeed());
- break;
- case 1: // 监控摄像头
- message = String.format("前方%d米有监控摄像头",
- facility.getDistance());
- break;
- case 2: // 事故
- message = String.format("前方%d米发生交通事故,请谨慎驾驶",
- facility.getDistance());
- break;
- case 3: // 施工
- message = String.format("前方%d米正在施工,请减速慢行",
- facility.getDistance());
- break;
- case 4: // 交通拥堵
- message = String.format("前方%d米出现拥堵,建议绕行",
- facility.getDistance());
- break;
- }
- if (!TextUtils.isEmpty(message)) {
- final String finalMessage = message;
- requireActivity().runOnUiThread(() -> {
- // 显示提示信息
- Toast.makeText(requireContext(), finalMessage, Toast.LENGTH_LONG).show();
- // 播放语音提醒
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(finalMessage, TextToSpeech.QUEUE_ADD, null, null);
- }
- });
- }
- }
复制代码 5). 速率超限提醒
- private void showSpeedWarning(int currentSpeed) {
- String speedWarning = String.format("当前速度%d公里/小时,请注意行车安全",
- currentSpeed);
- Toast.makeText(requireContext(), speedWarning, Toast.LENGTH_SHORT).show();
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak("您的车速较快,请注意行车安全",
- TextToSpeech.QUEUE_ADD, null, null);
- }
- }
复制代码 6). 资源释放
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (textToSpeech != null) {
- textToSpeech.stop(); // 停止播报
- textToSpeech.shutdown(); // 关闭引擎
- }
- }
复制代码 2、实时路况监控
①路况开关控制
- //MainActivity
- private ImageButton btnTraffic;
- private boolean isTrafficEnabled = true; // 默认开启实时路况
- // 初始化实时路况按钮
- btnTraffic = rootView.findViewById(R.id.btn_traffic);
- // 实时路况按钮点击事件
- btnTraffic.setOnClickListener(v -> {
- isTrafficEnabled = !isTrafficEnabled;
- aMap.setTrafficEnabled(isTrafficEnabled); // 设置地图是否显示实时路况
- updateTrafficButton();
- // 显示提示
- String message = isTrafficEnabled ? "已开启实时路况" : "已关闭实时路况";
- Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show();
- });
- // 更新路况按钮状态
- private void updateTrafficButton() {
- btnTraffic.setSelected(isTrafficEnabled);
- btnTraffic.setAlpha(isTrafficEnabled ? 1.0f : 0.5f);
- }
复制代码 XML文件
- <!-- 实时路况按钮 -->
- <ImageButton
- android:id="@+id/btn_traffic"
- android:layout_width="40dp"
- android:layout_height="40dp"
- android:layout_margin="8dp"
- android:background="@drawable/bg_map_button"
- android:contentDescription="@string/traffic_button"
- android:src="@drawable/ic_traffic" />
复制代码 按钮样式: res/drawable/bg_map_button.xml
- <?xml version="1.0" encoding="utf-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true">
- <shape android:shape="oval">
- <solid android:color="#E0E0E0" />
- </shape>
- </item>
- <item android:state_selected="true">
- <shape android:shape="oval">
- <solid android:color="#E3F2FD" />
- </shape>
- </item>
- <item>
- <shape android:shape="oval">
- <solid android:color="#FFFFFF" />
- </shape>
- </item>
- </selector>
复制代码 路况图标样式:res/drawable/ic_traffic.xml
- <?xml version="1.0" encoding="utf-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true"
- android:drawable="@drawable/ic_traffic_on" />
- <item
- android:drawable="@drawable/ic_traffic_off" />
- </selector>
复制代码 字符串资源:在values/strings
- <string name="traffic_button">实时路况</string>
复制代码 ②路况状态更新
- @Override
- public void onTrafficStatusUpdate() {
- // 路况更新时的处理
- if (isTrafficEnabled) {
- requireActivity().runOnUiThread(() -> {
- // 可以在这里更新UI
- });
- }
- }
- @Override
- public void onNaviInfoUpdate(NaviInfo naviInfo) {
- if (isNavigating && navigationInfoPanel.getVisibility() == View.VISIBLE && naviInfo != null) {
- requireActivity().runOnUiThread(() -> {
- // 更新路况状态(根据当前速度判断)
- int currentSpeed = naviInfo.getCurrentSpeed();
- String trafficText;
- int trafficColor;
- // 根据当前速度判断路况状态
- if (currentSpeed < 15) { // 低于15km/h认为是拥堵
- trafficText = "拥堵";
- trafficColor = Color.parseColor("#F44336"); // 红色
- } else if (currentSpeed < 30) { // 低于30km/h认为是缓行
- trafficText = "缓行";
- trafficColor = Color.parseColor("#FF9800"); // 橙色
- } else { // 高于30km/h认为是畅通
- trafficText = "畅通";
- trafficColor = Color.parseColor("#4CAF50"); // 绿色
- }
- tvTrafficStatus.setText("当前路况:" + trafficText);
- tvTrafficStatus.setTextColor(trafficColor);
- });
- }
- }
复制代码 ③拥堵重新规划
- @Override
- public void onReCalculateRouteForTrafficJam() {
- requireActivity().runOnUiThread(() -> {
- String message = "前方道路拥堵,正在重新规划路线";
- Toast.makeText(requireContext(), message, Toast.LENGTH_LONG).show();
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(message, TextToSpeech.QUEUE_FLUSH, null, null);
- }
- });
- }
复制代码 3、安全提醒体系
①OnUpdateTrafficFacility 方法(交通办法安全提醒)
- @Override
- public void OnUpdateTrafficFacility(AMapNaviTrafficFacilityInfo[] facilities) {
- // 批量交通设施提醒
- if (facilities == null || facilities.length == 0 || !isTrafficEnabled) return;
- for (AMapNaviTrafficFacilityInfo facility : facilities) {
- String message = "";
- switch (facility.getBroadcastType()) {
- case 0: // 测速摄像头
- message = String.format("前方%d米有测速摄像头,限速%d公里/小时",
- facility.getDistance(), facility.getLimitSpeed());
- break;
- case 1: // 监控摄像头
- message = String.format("前方%d米有监控摄像头", facility.getDistance());
- break;
- case 2: // 事故
- message = String.format("前方%d米发生交通事故,请谨慎驾驶", facility.getDistance());
- break;
- case 3: // 施工
- message = String.format("前方%d米正在施工,请减速慢行", facility.getDistance());
- break;
- case 4: // 交通拥堵
- message = String.format("前方%d米出现拥堵,建议绕行", facility.getDistance());
- break;
- }
- // 发送提醒
- if (!TextUtils.isEmpty(message)) {
- final String finalMessage = message;
- requireActivity().runOnUiThread(() -> {
- // 显示提示信息
- Toast.makeText(requireContext(), finalMessage, Toast.LENGTH_LONG).show();
- // 语音播报
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak(finalMessage, TextToSpeech.QUEUE_ADD, null, null);
- }
- });
- }
- }
- }
复制代码 ②onNaviInfoUpdate 方法中的速率监控
- @Override
- public void onNaviInfoUpdate(NaviInfo naviInfo) {
- if (isNavigating && navigationInfoPanel.getVisibility() == View.VISIBLE && naviInfo != null) {
- requireActivity().runOnUiThread(() -> {
- // ... 其他代码 ...
- // 速度监控
- int currentSpeed = naviInfo.getCurrentSpeed();
- if (currentSpeed > 80) {
- String speedWarning = String.format("当前速度%d公里/小时,请注意行车安全",
- currentSpeed);
- Toast.makeText(requireContext(), speedWarning, Toast.LENGTH_SHORT).show();
- if (!isMuted && textToSpeech != null) {
- textToSpeech.speak("您的车速较快,请注意行车安全",
- TextToSpeech.QUEUE_ADD, null, null);
- }
- }
- });
- }
- }
复制代码 ③getWarningMessage 方法(气候安全提醒)
- private String getWarningMessage(String weatherType) {
- if (weatherType.contains("雨")) {
- return "注意:当前降雨可能影响出行安全,请携带雨具,谨慎驾驶。";
- } else if (weatherType.contains("雪")) {
- return "注意:当前降雪可能导致路面结冰,建议减少出行。";
- } else if (weatherType.contains("雾")) {
- return "注意:空气质量较差,建议戴口罩出行。";
- } else if (weatherType.contains("霾")) {
- return "注意:空气质量较差,建议戴口罩出行。";
- }
- return "";
- }
复制代码 4、气候信息和预警
①获取高德气候服务KEY
大要的流程与上面获取高德地图KEY相同,只有一些渺小的差别:
将获取的KEY复制到AndroidManifest里
②调用气候服务的方法
1)updateWeatherInfo 方法(核心方法)
- private void updateWeatherInfo(LatLng destination) {
- try {
- // 清除旧的天气信息
- clearWeatherInfo();
- // 计算并显示距离
- float distance = calculateDistance(null, destination);
- if (distance > 0) {
- final float finalDistance = distance;
- requireActivity().runOnUiThread(() -> {
- tvDistance.setText(String.format("距离: %.1fkm", finalDistance));
- });
- }
- // 使用逆地理编码获取城市名称
- GeocodeSearch geocodeSearch = new GeocodeSearch(requireContext());
- geocodeSearch.setOnGeocodeSearchListener(new GeocodeSearch.OnGeocodeSearchListener() {
- @Override
- public void onRegeocodeSearched(RegeocodeResult result, int rCode) {
- if (rCode == AMapException.CODE_AMAP_SUCCESS) {
- // 获取城市名称
- String city = result.getRegeocodeAddress().getCity();
- if (TextUtils.isEmpty(city)) {
- city = result.getRegeocodeAddress().getDistrict();
- }
- // 使用城市名称查询天气
- WeatherSearchQuery query = new WeatherSearchQuery(
- city,
- WeatherSearchQuery.WEATHER_TYPE_LIVE
- );
- WeatherSearch weatherSearch = new WeatherSearch(requireContext());
- weatherSearch.setOnWeatherSearchListener(NavigationFragment.this);
- weatherSearch.setQuery(query);
- weatherSearch.searchWeatherAsyn();
- }
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
复制代码 2)onWeatherLiveSearched 方法(气候查询回调)
- @Override
- public void onWeatherLiveSearched(LocalWeatherLiveResult weatherLiveResult, int rCode) {
- requireActivity().runOnUiThread(() -> {
- if (rCode == AMapException.CODE_AMAP_SUCCESS) {
- LocalWeatherLive weatherLive = weatherLiveResult.getLiveResult();
-
- // 更新天气信息UI
- tvWeather.setText(String.format("%s %s°C",
- weatherLive.getWeather(),
- weatherLive.getTemperature()));
- tvHumidity.setText(weatherLive.getHumidity() + "%");
- tvWind.setText(String.format("%s风 %s级",
- weatherLive.getWindDirection(),
- weatherLive.getWindPower()));
- // 更新空气质量
- String airQuality = getAirQualityByWeather(weatherLive.getWeather());
- tvAqi.setText("空气质量:" + airQuality);
- tvAqi.setTextColor(getAirQualityColor(airQuality));
- // 更新天气图标
- updateWeatherIcon(weatherLive.getWeather());
- // 生成出行建议
- String recommendation = generateRecommendation(
- weatherLive.getWeather(),
- calculateDistance(mStartPoint, mEndPoint)
- );
- tvRecommendation.setText(recommendation);
- // 显示天气警告
- if (shouldShowWarning(weatherLive.getWeather())) {
- tvWeatherWarning.setVisibility(View.VISIBLE);
- tvWeatherWarning.setText(getWarningMessage(weatherLive.getWeather()));
- }
- }
- });
- }
复制代码 3)getAirQualityByWeather 方法(获取氛围质量)
- private String getAirQualityByWeather(String weather) {
- if (weather.contains("霾") || weather.contains("沙尘")) {
- return "重度污染";
- } else if (weather.contains("雾")) {
- return "中度污染";
- } else if (weather.contains("阴") || weather.contains("多云")) {
- return "良";
- } else if (weather.contains("雨") || weather.contains("雪")) {
- return "良";
- } else if (weather.contains("晴")) {
- return "优";
- } else {
- return "良";
- }
- }
- //获取空气质量对应颜色
- private int getAirQualityColor(String quality) {
- switch (quality) {
- case "优":
- return Color.parseColor("#00C853");
- case "良":
- return Color.parseColor("#FFB300");
- case "轻度污染":
- return Color.parseColor("#FB8C00");
- case "中度污染":
- return Color.parseColor("#F4511E");
- case "重度污染":
- return Color.parseColor("#C62828");
- case "严重污染":
- return Color.parseColor("#6A1B9A");
- default:
- return Color.GRAY;
- }
- }
复制代码 4)clearWeatherInfo 方法(扫除气候信息)
- private void clearWeatherInfo() {
- requireActivity().runOnUiThread(() -> {
- if (tvWeather != null) tvWeather.setText("正在获取天气...");
- if (tvHumidity != null) tvHumidity.setText("");
- if (tvWind != null) tvWind.setText("");
- if (tvDistance != null) tvDistance.setText("");
- if (tvRecommendation != null) tvRecommendation.setText("");
- if (tvWeatherWarning != null) {
- tvWeatherWarning.setText("");
- tvWeatherWarning.setVisibility(View.GONE);
- }
- });
- }
复制代码 上述只是调用气候服务的方法,你还要创建一个显示气候信息的结构weather_info,例如:
对控件进行初始化:
- // 在 onCreateView 方法中初始化天气信息相关的控件
- View weatherInfo = rootView.findViewById(R.id.weather_info);
- weatherIcon = weatherInfo.findViewById(R.id.weather_icon);
- tvDestination = weatherInfo.findViewById(R.id.tv_destination);
- tvWeather = weatherInfo.findViewById(R.id.tv_weather);
- tvDistance = weatherInfo.findViewById(R.id.tv_distance);
- tvHumidity = weatherInfo.findViewById(R.id.tv_humidity);
- tvWind = weatherInfo.findViewById(R.id.tv_wind);
- tvAqi = weatherInfo.findViewById(R.id.tv_aqi);
复制代码 气候信息面板的显示控制
- View weatherInfo = rootView.findViewById(R.id.weather_info);
- weatherInfo.setVisibility(View.VISIBLE); // 显示天气
- // weatherInfo.setVisibility(View.GONE); // 隐藏天气
复制代码 5、智能出行建议
①、generateRecommendation 方法(核心方法)
- private String generateRecommendation(String weatherType, float distance) {
- // 根据天气类型给出建议
- if (weatherType.contains("暴雨") || weatherType.contains("大雨") || weatherType.contains("雷")) {
- return "当前天气恶劣,建议选择驾车出行或推迟行程,注意道路积水和打滑。";
- } else if (weatherType.contains("中雨") || weatherType.contains("小雨")) {
- return "当前有降雨,建议携带雨具,选择驾车出行较为安全。";
- } else if (weatherType.contains("雪")) {
- return "当前降雪天气,路面可能结冰,建议驾车出行并保持低速行驶。";
- } else if (weatherType.contains("雾") || weatherType.contains("霾")) {
- return "当前能见度较低,建议戴好防护口罩,驾车出行时开启雾灯并保持安全车距。";
- }
-
- // 根据天气和距离组合给出建议
- else if (weatherType.contains("阴")) {
- if (distance > 5) {
- return "天气阴沉,距离较远,建议驾车出行。";
- } else if (distance > 2) {
- return "虽然天气阴沉,但温度适宜,可以考虑骑行或步行。";
- } else {
- return "距离较近,阴天适合运动,建议步行前往。";
- }
- } else if (weatherType.contains("晴")) {
- if (distance > 5) {
- return "天气晴朗,但距离较远,建议驾车出行。";
- } else if (distance > 2) {
- return "阳光明媚,距离适中,非常适合骑行,记得防晒。";
- } else {
- return "天气晴好,距离不远,建议步行,享受阳光。";
- }
- } else if (weatherType.contains("多云")) {
- if (distance > 5) {
- return "天气较好,但距离较远,建议驾车出行。";
- } else if (distance > 2) {
- return "云朵遮阳,温度舒适,是骑行的好天气。";
- } else {
- return "天气舒适,距离不远,非常适合步行。";
- }
- }
-
- // 默认建议
- if (distance > 5) {
- return "距离较远,建议选择驾车出行。";
- } else if (distance > 2) {
- return "距离适中,可以考虑骑行方式。";
- } else {
- return "距离较近,建议步行前往。";
- }
- }
复制代码 ②、calculateDistance 方法(盘算距离)
- private float calculateDistance(LatLng start, LatLng end) {
- try {
- if (end == null) {
- return 0;
- }
- // 获取当前位置作为起点
- Location currentLocation = aMap.getMyLocation();
- if (currentLocation == null) {
- if (start == null) {
- return 0;
- }
- } else {
- // 使用当前实际位置作为起点
- start = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
- }
- // 计算距离
- if (start != null && end != null) {
- float distance = AMapUtils.calculateLineDistance(start, end);
- return distance / 1000.0f; // 转换为公里
- }
- return 0;
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
复制代码 ③、onWeatherLiveSearched 方法中的智能建议部分
- @Override
- public void onWeatherLiveSearched(LocalWeatherLiveResult weatherLiveResult, int rCode) {
- requireActivity().runOnUiThread(() -> {
- if (rCode == AMapException.CODE_AMAP_SUCCESS) {
- LocalWeatherLive weatherLive = weatherLiveResult.getLiveResult();
-
- // 生成出行建议
- String recommendation = generateRecommendation(
- weatherLive.getWeather(),
- calculateDistance(mStartPoint, mEndPoint)
- );
- tvRecommendation.setText(recommendation);
- // 更新推荐的出行方式
- updateRecommendedMode(weatherLive.getWeather(),
- calculateDistance(mStartPoint, mEndPoint));
- }
- });
- }
复制代码 总结:
上述的方法与代码仅供参考,当然这个项目并不完善,能修改的地方尚有很多,例如:我们还可以添加一个出行方式的选择,驾车、骑车、步行、公交等等,由于篇幅有限这里就不赘述了。
} else if (weatherType.contains(“晴”)) {
if (distance > 5) {
return “气候晴朗,但距离较远,建议驾车出行。”;
} else if (distance > 2) {
return “阳光妖冶,距离适中,非常得当骑行,记得防晒。”;
} else {
return “气候晴好,距离不远,建议步行,享受阳光。”;
}
} else if (weatherType.contains(“多云”)) {
if (distance > 5) {
return “气候较好,但距离较远,建议驾车出行。”;
} else if (distance > 2) {
return “云朵遮阳,温度舒适,是骑行的好气候。”;
} else {
return “气候舒适,距离不远,非常得当步行。”;
}
}
- // 默认建议
- if (distance > 5) {
- return "距离较远,建议选择驾车出行。";
- } else if (distance > 2) {
- return "距离适中,可以考虑骑行方式。";
- } else {
- return "距离较近,建议步行前往。";
- }
复制代码 }
- ##### ②、calculateDistance 方法(盘算距离)```Javaprivate float calculateDistance(LatLng start, LatLng end) {
- try {
- if (end == null) {
- return 0;
- }
- // 获取当前位置作为起点
- Location currentLocation = aMap.getMyLocation();
- if (currentLocation == null) {
- if (start == null) {
- return 0;
- }
- } else {
- // 使用当前实际位置作为起点
- start = new LatLng(currentLocation.getLatitude(), currentLocation.getLongitude());
- }
- // 计算距离
- if (start != null && end != null) {
- float distance = AMapUtils.calculateLineDistance(start, end);
- return distance / 1000.0f; // 转换为公里
- }
- return 0;
- } catch (Exception e) {
- e.printStackTrace();
- return 0;
- }
- }
复制代码 ③、onWeatherLiveSearched 方法中的智能建议部分
- @Override
- public void onWeatherLiveSearched(LocalWeatherLiveResult weatherLiveResult, int rCode) {
- requireActivity().runOnUiThread(() -> {
- if (rCode == AMapException.CODE_AMAP_SUCCESS) {
- LocalWeatherLive weatherLive = weatherLiveResult.getLiveResult();
-
- // 生成出行建议
- String recommendation = generateRecommendation(
- weatherLive.getWeather(),
- calculateDistance(mStartPoint, mEndPoint)
- );
- tvRecommendation.setText(recommendation);
- // 更新推荐的出行方式
- updateRecommendedMode(weatherLive.getWeather(),
- calculateDistance(mStartPoint, mEndPoint));
- }
- });
- }
复制代码 总结:
上述的方法与代码仅供参考,当然这个项目并不完善,能修改的地方尚有很多,例如:我们还可以添加一个出行方式的选择,驾车、骑车、步行、公交等等,由于篇幅有限这里就不赘述了。
通过这个项目,对于高德地图SDK的使用,气候服务和数据处理惩罚,Android生命周期管理,异步编程和线程处理惩罚,错误处理惩罚和异常管理都有不小的劳绩,对于这些不认识的可以跟着做一下。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |