commit 59b796a4a18e20eb745b981154778d7ea6c6f912 Author: MADAO-LUV <3335075714@qq.com> Date: Tue Apr 29 16:12:29 2025 +0800 最初的app diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..eaf91e2 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..8fabff5 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml new file mode 100644 index 0000000..4fe9fa8 --- /dev/null +++ b/.idea/deploymentTargetDropDown.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..de8d7f4 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0b0534c --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..48052b2 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5954a2b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..356b2e9 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,76 @@ +plugins { + alias(libs.plugins.androidApplication) +} + +android { + namespace 'com.example.mangowalking' + compileSdk 34 + + defaultConfig { + applicationId "com.example.mangowalking" + minSdk 24 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + ndk { + //设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so) + abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86","x86_64" + } + + } + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + } + debug.setRoot('build-types/debug') + release.setRoot('build-types/release') + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + buildFeatures { + viewBinding true + } + +} + +// 构建时间 +def generateTime(){ + return new Date().format("yyyy_MM_dd_HH_mm_ss") +} + + +// 自定义打包名称 +android.applicationVariants.all { variant -> + variant.outputs.all { + outputFileName = "ManGoWalk_v${versionName}_${generateTime()}.apk" + } +} + + + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation libs.appcompat + implementation libs.material + implementation libs.activity + implementation libs.constraintlayout +// implementtation 'cn.shanyaliux.serialport:serialport:4.2.0' +// implementation 'com.amap.api:3dmap-location-search:latest.integration' +// implementation 'com.amap.api:3dmap:latest.integration' +// implementation 'com.amap.api:location:6.4.9' + testImplementation libs.junit + androidTestImplementation libs.ext.junit + androidTestImplementation libs.espresso.core +} \ No newline at end of file diff --git a/app/libs/AMap3DMap_10.1.201_AMapSearch_9.7.4_AMapLocation_6.4.9_20250317.jar b/app/libs/AMap3DMap_10.1.201_AMapSearch_9.7.4_AMapLocation_6.4.9_20250317.jar new file mode 100644 index 0000000..123b4eb Binary files /dev/null and b/app/libs/AMap3DMap_10.1.201_AMapSearch_9.7.4_AMapLocation_6.4.9_20250317.jar differ diff --git a/app/libs/arm64-v8a/libAMapSDK_MAP_v10_1_200.so b/app/libs/arm64-v8a/libAMapSDK_MAP_v10_1_200.so new file mode 100644 index 0000000..910ba6d Binary files /dev/null and b/app/libs/arm64-v8a/libAMapSDK_MAP_v10_1_200.so differ diff --git a/app/libs/armeabi-v7a/libAMapSDK_MAP_v10_1_200.so b/app/libs/armeabi-v7a/libAMapSDK_MAP_v10_1_200.so new file mode 100644 index 0000000..af3d0ee Binary files /dev/null and b/app/libs/armeabi-v7a/libAMapSDK_MAP_v10_1_200.so differ diff --git a/app/libs/build.properties b/app/libs/build.properties new file mode 100644 index 0000000..b3b8338 --- /dev/null +++ b/app/libs/build.properties @@ -0,0 +1,10 @@ +iid=11600 +sid=3528001 +bid=1350002 +version=13.08.0.10009081 +time=2025-03-17 18:00:51 +FEATURE_LOCATION=1 +FEATURE_ROUTE_OVERLAY=1 +FEATURE_MVT=1 +FEATURE_3DTiles=1 +FEATURE_GLTF=1 diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..cf50408 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,22 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + diff --git a/app/release/ManGoWalk_v1.0_2025_04_17_19_59_18.apk b/app/release/ManGoWalk_v1.0_2025_04_17_19_59_18.apk new file mode 100644 index 0000000..cb1be24 Binary files /dev/null and b/app/release/ManGoWalk_v1.0_2025_04_17_19_59_18.apk differ diff --git a/app/release/baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm b/app/release/baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm new file mode 100644 index 0000000..22c3047 Binary files /dev/null and b/app/release/baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm differ diff --git a/app/release/baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm b/app/release/baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm new file mode 100644 index 0000000..d8a6ebd Binary files /dev/null and b/app/release/baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..6cfef45 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,37 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.example.mangowalking", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0", + "outputFile": "ManGoWalk_v1.0_2025_04_17_19_59_18.apk" + } + ], + "elementType": "File", + "baselineProfiles": [ + { + "minApi": 28, + "maxApi": 30, + "baselineProfiles": [ + "baselineProfiles/1/ManGoWalk_v1.0_2025_04_17_19_59_18.dm" + ] + }, + { + "minApi": 31, + "maxApi": 2147483647, + "baselineProfiles": [ + "baselineProfiles/0/ManGoWalk_v1.0_2025_04_17_19_59_18.dm" + ] + } + ], + "minSdkVersionForDexing": 24 +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/mangowalking/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/example/mangowalking/ExampleInstrumentedTest.java new file mode 100644 index 0000000..8931820 --- /dev/null +++ b/app/src/androidTest/java/com/example/mangowalking/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.example.mangowalking; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.example.mangowalking", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ed80cb6 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/mangowalking/App.java b/app/src/main/java/com/example/mangowalking/App.java new file mode 100644 index 0000000..7232271 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/App.java @@ -0,0 +1,27 @@ +package com.example.mangowalking; + +import android.app.Application; +import android.content.Context; + +import com.amap.api.location.AMapLocationClient; +import com.amap.api.maps.MapsInitializer; +import com.amap.api.services.core.ServiceSettings; + +public class App extends Application { + + @Override + public void onCreate() { + super.onCreate(); + Context mContext = this; + // 定位隐私政策同意 + AMapLocationClient.updatePrivacyShow(mContext,true,true); + AMapLocationClient.updatePrivacyAgree(mContext,true); + // 地图隐私政策同意 + MapsInitializer.updatePrivacyShow(mContext,true,true); + MapsInitializer.updatePrivacyAgree(mContext,true); + // 搜索隐私政策同意 + ServiceSettings.updatePrivacyShow(mContext,true,true); + ServiceSettings.updatePrivacyAgree(mContext,true); + } +} + diff --git a/app/src/main/java/com/example/mangowalking/MainActivity.java b/app/src/main/java/com/example/mangowalking/MainActivity.java new file mode 100644 index 0000000..6aa4f33 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/MainActivity.java @@ -0,0 +1,635 @@ +package com.example.mangowalking; + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Color; +import android.os.Bundle; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.util.Log; +import android.Manifest; // 针对于ACCESS_FINE_LOCATION +import android.view.KeyEvent; +import android.view.View; +import android.view.animation.LinearInterpolator; +import android.view.inputmethod.InputMethodManager; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +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.maps.AMap; +import com.amap.api.maps.CameraUpdate; +import com.amap.api.maps.CameraUpdateFactory; +import com.amap.api.maps.LocationSource; +import com.amap.api.maps.UiSettings; +import com.amap.api.maps.model.BitmapDescriptorFactory; +import com.amap.api.maps.model.CameraPosition; +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.maps.model.MyLocationStyle; +import com.amap.api.maps.model.animation.Animation; +import com.amap.api.maps.model.animation.RotateAnimation; +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.geocoder.GeocodeAddress; +import com.amap.api.services.geocoder.GeocodeQuery; +import com.amap.api.services.geocoder.GeocodeResult; +import com.amap.api.services.geocoder.GeocodeSearch; +import com.amap.api.services.geocoder.RegeocodeAddress; +import com.amap.api.services.geocoder.RegeocodeQuery; +import com.amap.api.services.geocoder.RegeocodeResult; +import com.amap.api.services.poisearch.PoiResult; +import com.amap.api.services.poisearch.PoiSearch; +import com.example.mangowalking.databinding.ActivityMainBinding; + +import java.util.ArrayList; +import java.util.List; + +public class MainActivity extends AppCompatActivity implements AMapLocationListener, LocationSource, PoiSearch.OnPoiSearchListener, AMap.OnMapClickListener, AMap.OnMapLongClickListener, GeocodeSearch.OnGeocodeSearchListener, AMap.OnMarkerClickListener, AMap.OnMarkerDragListener, AMap.InfoWindowAdapter, AMap.OnInfoWindowClickListener { + private static final String TAG = "MainActivity"; + private ActivityMainBinding binding; + private ActivityResultLauncher requestPermission; + + //声明AMapLocationClient类对象 + public AMapLocationClient mLocationClient = null; + //声明AMapLocationClientOption对象 + public AMapLocationClientOption mLocationOption = null; + // 声明地图控制器 + private AMap aMap = null; + // 声明地图定位监听 + private LocationSource.OnLocationChangedListener mListener = null; + + //POI查询对象 + private PoiSearch.Query query; + //POI搜索对象 + private PoiSearch poiSearch; + //城市码 + private String cityCode = null; + + //地理编码搜索 + private GeocodeSearch geocodeSearch; + //解析成功标识码 + private static final int PARSE_SUCCESS_CODE = 1000; + //城市 + private String city; + + //标点列表 + private final List markerList = new ArrayList<>(); + @Override + protected void onCreate(Bundle savedInstanceState) { + requestPermission = registerForActivityResult(new ActivityResultContracts.RequestPermission(), result -> { + // 权限申请结果 + Log.d(TAG, "权限申请结果: " + result); + showMsg(result ? "已获取权限" : "权限申请失败"); + }); + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + binding = ActivityMainBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); //这里要和博主一致 + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + initLocation(); + binding.mapView.onCreate(savedInstanceState); + initMap(); + initSearch(); + initView(); + + } + + /** + * 初始化搜索 + */ + private void initSearch() { + // 构造 GeocodeSearch 对象 + try { + geocodeSearch = new GeocodeSearch(this); + // 设置监听 + geocodeSearch.setOnGeocodeSearchListener(this); + } catch (com.amap.api.services.core.AMapException e) { + throw new RuntimeException(e); + } + } + + + private void initLocation() { + try { + //初始化定位 + mLocationClient = new AMapLocationClient(getApplicationContext()); + //设置定位回调监听 + mLocationClient.setLocationListener(this); + //初始化AMapLocationClientOption对象 + mLocationOption = new AMapLocationClientOption(); + //设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。 + mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); + //获取最近3s内精度最高的一次定位结果 + mLocationOption.setOnceLocationLatest(true); + //设置是否返回地址信息(默认返回地址信息) + mLocationOption.setNeedAddress(true); + //设置定位超时时间,单位是毫秒 + mLocationOption.setHttpTimeOut(6000); + //给定位客户端对象设置定位参数 + mLocationClient.setLocationOption(mLocationOption); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 开始定位 + */ + private void startLocation() { + if (mLocationClient != null) mLocationClient.startLocation(); + } + + /** + * 停止定位 + */ + private void stopLocation() { + if (mLocationClient != null) mLocationClient.stopLocation(); + } + + + /** + * 初始化地图 + */ + private void initMap() { + if (aMap == null) { + aMap = binding.mapView.getMap(); + // 创建定位蓝点的样式 + MyLocationStyle myLocationStyle = new MyLocationStyle(); + // 自定义定位蓝点图标 + myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.gps_point)); + // 自定义精度范围的圆形边框颜色 都为0则透明 + myLocationStyle.strokeColor(Color.argb(0, 0, 0, 0)); + // 自定义精度范围的圆形边框宽度 0 无宽度 + myLocationStyle.strokeWidth(0); + // 设置圆形的填充颜色 都为0则透明 + myLocationStyle.radiusFillColor(Color.argb(0, 0, 0, 0)); + // 设置定位蓝点的样式 + aMap.setMyLocationStyle(myLocationStyle); + // 设置定位监听 + aMap.setLocationSource(this); + // 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false。 + aMap.setMyLocationEnabled(true); + + aMap.setMinZoomLevel(15); //设置最小缩放等级为15 缩放等级为(3,20) + aMap.showIndoorMap(true); //开启室内地图 + // 设置地图点击事件 + aMap.setOnMapClickListener(this); + // 设置地图长按事件 + aMap.setOnMapLongClickListener(this); + // 设置地图标点点击事件 + aMap.setOnMarkerClickListener(this); + // 设置地图标点拖拽事件 + aMap.setOnMarkerDragListener(this); + // 设置InfoWindowAdapter监听 + aMap.setInfoWindowAdapter(this); + // 设置InfoWindow点击事件 + aMap.setOnInfoWindowClickListener(this); + + // 地图控件设置 + UiSettings uiSettings = aMap.getUiSettings(); + // 隐藏缩放按钮 + uiSettings.setZoomControlsEnabled(false); + // 显示比例尺,默认不显示 + uiSettings.setScaleControlsEnabled(true); + } + } + + + @Override + protected void onResume() { + super.onResume(); + // 检查是否已经获取到定位权限 + binding.mapView.onResume(); + if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + // 获取到权限 + requestPermission.launch(Manifest.permission.ACCESS_FINE_LOCATION); + } + } + + @Override + protected void onPause() { + super.onPause(); + // 绑定生命周期 onPause + binding.mapView.onPause(); + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + // 绑定生命周期 onSaveInstanceState + binding.mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // 绑定生命周期 onDestroy + binding.mapView.onDestroy(); + } + + + /** + * 初始化定位 + */ + + private void showMsg(CharSequence llw) { + Toast.makeText(this, llw, Toast.LENGTH_SHORT).show(); + } + + /** + * 初始化控件 + */ + private void initView() { + // Poi搜索按钮点击事件 + binding.fabPoi.setOnClickListener(v -> { + //构造query对象 + query = new PoiSearch.Query("购物", "", cityCode); + // 设置每页最多返回多少条poiItem + query.setPageSize(10); + //设置查询页码 + query.setPageNum(1); + //构造 PoiSearch 对象 + try { + poiSearch = new PoiSearch(this, query); + //设置搜索回调监听 + poiSearch.setOnPoiSearchListener(this); + //发起搜索附近POI异步请求 + poiSearch.searchPOIAsyn(); + } catch (AMapException e) { + throw new RuntimeException(e); + } + }); + // 清除标点按钮点击事件 + binding.fabClearMarker.setOnClickListener(v -> clearAllMarker()); + // 路线按钮点击事件 + binding.fabRoute.setOnClickListener(v -> startActivity(new Intent(this, RouteActivity.class))); + // 键盘按键监听 + binding.etAddress.setOnKeyListener((v, keyCode, event) -> { + if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { + //获取输入框的值 + String address = binding.etAddress.getText().toString().trim(); + if (address.isEmpty()) { + showMsg("请输入地址"); + } else { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + //隐藏软键盘 + imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0); + + // name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode + GeocodeQuery query = new GeocodeQuery(address, city); + geocodeSearch.getFromLocationNameAsyn(query); + } + return true; + } + return false; + }); + + } + + + + + /** + * 定位回调结果 + */ + @Override + public void onLocationChanged(AMapLocation aMapLocation) { + if (aMapLocation == null) { + showMsg("定位失败,aMapLocation 为空"); + return; + } + // 获取定位结果 + if (aMapLocation.getErrorCode() == 0) { + // 定位成功 + showMsg("定位成功"); +// aMapLocation.getLocationType();//获取当前定位结果来源,如网络定位结果,详见定位类型表 +// aMapLocation.getLatitude();//获取纬度 +// aMapLocation.getLongitude();//获取经度 +// aMapLocation.getAccuracy();//获取精度信息 +// aMapLocation.getAddress();//详细地址,如果option中设置isNeedAddress为false,则没有此结果,网络定位结果中会有地址信息,GPS定位不返回地址信息。 +// aMapLocation.getCountry();//国家信息 +// aMapLocation.getProvince();//省信息 +// aMapLocation.getCity();//城市信息 + String result = aMapLocation.getDistrict();//城区信息 +// aMapLocation.getStreet();//街道信息 +// aMapLocation.getStreetNum();//街道门牌号信息 +// aMapLocation.getCityCode();//城市编码 +// aMapLocation.getAdCode();//地区编码 +// aMapLocation.getAoiName();//获取当前定位点的AOI信息 +// aMapLocation.getBuildingId();//获取当前室内定位的建筑物Id +// aMapLocation.getFloor();//获取当前室内定位的楼层 +// aMapLocation.getGpsAccuracyStatus();//获取GPS的当前状态 + // 停止定位 + stopLocation(); + // 显示地图定位结果 + if (mListener != null) { + mListener.onLocationChanged(aMapLocation); + } + // 显示浮动按钮 + binding.fabPoi.show(); + // 城市编码赋值 + cityCode = aMapLocation.getCityCode(); + //城市 + city = aMapLocation.getCity(); + } else { + // 定位失败 + showMsg("定位失败,错误:" + aMapLocation.getErrorInfo()); + Log.e(TAG,"location Error, ErrCode:" + + aMapLocation.getErrorCode() + ", errInfo:" + + aMapLocation.getErrorInfo()); + } + } + + /** + * 激活定位 + */ + @Override + public void activate(OnLocationChangedListener onLocationChangedListener) { + if (mListener == null) { + mListener = onLocationChangedListener; + } + startLocation(); + } + + /** + * 禁用 + */ + @Override + public void deactivate() { + mListener = null; + if (mLocationClient != null) { + mLocationClient.stopLocation(); + mLocationClient.onDestroy(); + } + mLocationClient = null; + } + + /** + * POI搜索返回 + * + * @param poiResult POI所有数据 + * @param i + */ + @Override + public void onPoiSearched(PoiResult poiResult, int i) { + //解析result获取POI信息 + + //获取POI组数列表 + ArrayList poiItems = poiResult.getPois(); + for (PoiItem poiItem : poiItems) { +// Log.d("MainActivity", " Title:" + poiItem.getTitle() + " Snippet:" + poiItem.getSnippet()); + Log.d("MainActivity", "Title: " + poiItem.getTitle()); + Log.d("MainActivity", "Snippet: " + poiItem.getSnippet()); + } + } + + /** + * POI中的项目搜索返回 + * + * @param poiItem 获取POI item + * @param i + */ + @Override + public void onPoiItemSearched(PoiItem poiItem, int i) { + + } + + /** + * 通过经纬度获取地址 + * @param latLng + */ + private void latLonToAddress(LatLng latLng) { + //位置点 通过经纬度进行构建 + LatLonPoint latLonPoint = new LatLonPoint(latLng.latitude, latLng.longitude); + //逆编码查询 第一个参数表示一个Latlng,第二参数表示范围多少米,第三个参数表示是火系坐标系还是GPS原生坐标系 + RegeocodeQuery query = new RegeocodeQuery(latLonPoint, 20, GeocodeSearch.AMAP); + //异步获取地址信息 + geocodeSearch.getFromLocationAsyn(query); + } + + /** + * 添加地图标点 + * + * @param latLng + */ + private void addMarker(LatLng latLng) { + //显示浮动按钮 + binding.fabClearMarker.show(); + //添加标点 + Marker marker = aMap.addMarker(new MarkerOptions().draggable(true).position(latLng).title("标题").snippet("DefaultMarker")); + //显示InfoWindow + marker.showInfoWindow(); + //设置标点的绘制动画效果 + Animation animation = new RotateAnimation(marker.getRotateAngle(),marker.getRotateAngle()+360,0,0,0); + long duration = 1000L; + animation.setDuration(duration); + animation.setInterpolator(new LinearInterpolator()); + + marker.setAnimation(animation); + marker.startAnimation(); + + + + markerList.add(marker); + } + + /** + * 清空地图Marker + */ + public void clearAllMarker() { + if (markerList != null && !markerList.isEmpty()) { + for (Marker markerItem : markerList) { + markerItem.remove(); + } + } + binding.fabClearMarker.hide(); + } + + + /** + * 改变地图中心位置 + * @param latLng 位置 + */ + private void updateMapCenter(LatLng latLng) { + // CameraPosition 第一个参数: 目标位置的屏幕中心点经纬度坐标。 + // CameraPosition 第二个参数: 目标可视区域的缩放级别 + // CameraPosition 第三个参数: 目标可视区域的倾斜度,以角度为单位。 + // CameraPosition 第四个参数: 可视区域指向的方向,以角度为单位,从正北向顺时针方向计算,从0度到360度 + CameraPosition cameraPosition = new CameraPosition(latLng, 16, 30, 0); + //位置变更 + CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition(cameraPosition); + //改变位置(使用动画) + aMap.animateCamera(cameraUpdate); + } + + /** + * 地图点击事件 + * @param latLng + */ + @Override + public void onMapClick(LatLng latLng) { + latLonToAddress(latLng); + addMarker(latLng); + updateMapCenter(latLng); + } + + /** + * 地图长按事件 + * @param latLng + */ + @Override + public void onMapLongClick(LatLng latLng) { + latLonToAddress(latLng); + } + + @Override + public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int rCode) { + //解析result获取地址描述信息 + if(rCode == PARSE_SUCCESS_CODE){ + RegeocodeAddress regeocodeAddress = regeocodeResult.getRegeocodeAddress(); + //显示解析后的地址 + showMsg("地址:"+regeocodeAddress.getFormatAddress()); + }else { + showMsg("获取地址失败"); + } + } + + @Override + public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) { + if (rCode != PARSE_SUCCESS_CODE) { + showMsg("获取坐标失败"); + return; + } + List geocodeAddressList = geocodeResult.getGeocodeAddressList(); + if (geocodeAddressList != null && !geocodeAddressList.isEmpty()) { + LatLonPoint latLonPoint = geocodeAddressList.get(0).getLatLonPoint(); + //显示解析后的坐标 + showMsg("坐标:" + latLonPoint.getLongitude() + "," + latLonPoint.getLatitude()); + } + } + + @Override + public boolean onMarkerClick(Marker marker) { + if (!marker.isInfoWindowShown()) { // 显示 + marker.showInfoWindow(); + } else { // 隐藏 + marker.hideInfoWindow(); + } + return true; + } + + + @Override + public void onMarkerDragStart(Marker marker) { + Log.d(TAG, "开始拖拽"); + } + + @Override + public void onMarkerDrag(Marker marker) { + Log.d(TAG, "拖拽中..."); + } + + @Override + public void onMarkerDragEnd(Marker marker) { + showMsg("拖拽完成"); + } + + /** + * 修改内容 + * + * @param marker + * @return + */ + @Override + public View getInfoContents(Marker marker) { + View infoContent = getLayoutInflater().inflate( + R.layout.custom_info_contents, null); + render(marker, infoContent); + return infoContent; + } + + /** + * 修改背景 + * + * @param marker + */ + @Override + public View getInfoWindow(Marker marker) { + View infoWindow = getLayoutInflater().inflate( + R.layout.custom_info_window, null); + render(marker, infoWindow); + return infoWindow; + } + + /** + * 渲染 + * + * @param marker + * @param view + */ + private void render(Marker marker, View view) { + ((ImageView) view.findViewById(R.id.badge)) + .setImageResource(R.mipmap.ic_yuan); + + //修改InfoWindow标题内容样式 + String title = marker.getTitle(); + TextView titleUi = ((TextView) view.findViewById(R.id.title)); + if (title != null) { + SpannableString titleText = new SpannableString(title); + titleText.setSpan(new ForegroundColorSpan(Color.RED), 0, + titleText.length(), 0); + titleUi.setTextSize(15); + titleUi.setText(titleText); + + } else { + titleUi.setText(""); + } + //修改InfoWindow片段内容样式 + String snippet = marker.getSnippet(); + TextView snippetUi = ((TextView) view.findViewById(R.id.snippet)); + if (snippet != null) { + SpannableString snippetText = new SpannableString(snippet); + snippetText.setSpan(new ForegroundColorSpan(Color.GREEN), 0, + snippetText.length(), 0); + snippetUi.setTextSize(20); + snippetUi.setText(snippetText); + } else { + snippetUi.setText(""); + } + } + + /** + * InfoWindow点击事件 + * + * @param marker + */ + @Override + public void onInfoWindowClick(Marker marker) { + showMsg("弹窗内容:标题:" + marker.getTitle() + "\n内容:" + marker.getSnippet()); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/mangowalking/RouteActivity.java b/app/src/main/java/com/example/mangowalking/RouteActivity.java new file mode 100644 index 0000000..e9e2652 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/RouteActivity.java @@ -0,0 +1,841 @@ +package com.example.mangowalking; + +import static com.example.mangowalking.utils.MapUtil.convertToLatLng; +import static com.example.mangowalking.utils.MapUtil.convertToLatLonPoint; + +import android.annotation.SuppressLint; +import android.app.Activity; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattCallback; +import android.bluetooth.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattDescriptor; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothManager; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.ParcelUuid; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.RelativeLayout; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.activity.EdgeToEdge; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +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.maps.AMap; +import com.amap.api.maps.LocationSource; +import com.amap.api.maps.UiSettings; +import com.amap.api.maps.model.BitmapDescriptorFactory; +import com.amap.api.maps.model.LatLng; +import com.amap.api.maps.model.MarkerOptions; +import com.amap.api.maps.model.MyLocationStyle; +import com.amap.api.services.busline.BusLineItem; +import com.amap.api.services.core.AMapException; +import com.amap.api.services.core.LatLonPoint; +import com.amap.api.services.geocoder.GeocodeAddress; +import com.amap.api.services.geocoder.GeocodeQuery; +import com.amap.api.services.geocoder.GeocodeResult; +import com.amap.api.services.geocoder.GeocodeSearch; +import com.amap.api.services.geocoder.RegeocodeResult; +import com.amap.api.services.route.BusPath; +import com.amap.api.services.route.BusRouteResult; +import com.amap.api.services.route.BusStep; +import com.amap.api.services.route.DriveRouteResult; +import com.amap.api.services.route.RideRouteResult; +import com.amap.api.services.route.RouteBusLineItem; +import com.amap.api.services.route.RouteSearch; +import com.amap.api.services.route.WalkPath; +import com.amap.api.services.route.WalkRouteResult; +import com.amap.api.services.route.WalkStep; +import com.example.mangowalking.databinding.ActivityRouteBinding; +import com.example.mangowalking.overlay.BusRouteOverlay; +import com.example.mangowalking.overlay.WalkRouteOverlay; +import com.example.mangowalking.utils.MapUtil; + +import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class RouteActivity extends AppCompatActivity implements + AMapLocationListener, LocationSource, AMap.OnMapClickListener, RouteSearch.OnRouteSearchListener, GeocodeSearch.OnGeocodeSearchListener, View.OnKeyListener { + + + private static final String TAG = "RouteActivity"; + private ActivityRouteBinding binding; + //地图控制器 + private AMap aMap = null; + //声明AMapLocationClient类对象 + public AMapLocationClient mLocationClient = null; + //声明AMapLocationClientOption对象 + public AMapLocationClientOption mLocationOption = null; + //位置更改监听 + private LocationSource.OnLocationChangedListener mListener; + //定义一个UiSettings对象 + private UiSettings mUiSettings; + //定位样式 + private MyLocationStyle myLocationStyle = new MyLocationStyle(); + + //起点 + private LatLonPoint mStartPoint; + //终点 + private LatLonPoint mEndPoint; + //路线搜索对象 + private RouteSearch routeSearch; + + //出行方式数组 + private static final String[] travelModeArray = {"步行出行", "公交出行"}; + + //出行方式值 + private static int TRAVEL_MODE = 0; + + //数组适配器 + private ArrayAdapter arrayAdapter; + //地理编码搜索 + private GeocodeSearch geocodeSearch; + //解析成功标识码 + private static final int PARSE_SUCCESS_CODE = 1000; + //定位地址 + private String locationAddress; + //城市 + private String city; + //起点地址转坐标标识 1 + private int tag = -1; + + //路线规划详情 + private RelativeLayout bottomLayout; + //花费时间 + private TextView tvTime; + + //添加关于蓝牙的变量 + private BluetoothLeScanner scanner; + + private BluetoothDevice device; + + private BluetoothGatt bluetoothGatt; + + private boolean isBluetoothConnected = false; + + private String SERVICE_UUID = "0000FFE0-0000-1000-8000-00805F9B34FB"; + private String READ_UUID = "0000FFE0-0000-1000-8000-00805F9B34FB"; + + private String READ_DEDSCRIPTION_UUID = "00002902-0000-1000-8000-00805f9b34fb"; + + private BluetoothGattCharacteristic writeCharacteristic; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + binding = ActivityRouteBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + //添加蓝牙开启 蓝牙部分是成功开启了 + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Toast.makeText(RouteActivity.this, "没有蓝牙", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(RouteActivity.this, "该设备支持蓝牙", Toast.LENGTH_SHORT).show(); + } + + final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); + + ActivityResultLauncher launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { + if (result.getResultCode() == RESULT_OK) { + //处理返回结果 + Toast.makeText(RouteActivity.this, "已成功开启蓝牙", Toast.LENGTH_SHORT).show(); + } + }); + @SuppressLint("MissingPermission") + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); //创建一个蓝牙启动的意图 + launcher.launch(enableBtIntent);//使用launcer启动这个意图就可以了。 + //此处可行 + //初始化定位 + initLocation(); + //初始化地图 + initMap(savedInstanceState); + //启动定位 + mLocationClient.startLocation(); + initRoute(); + //初始化出行方式 + initTravelMode(); + + // 新加入的 + scanner = bluetoothAdapter.getBluetoothLeScanner(); + //不进行权限验证 + ScanCallback callback = new ScanCallback() { + @Override + public void onScanResult(int callbackType, ScanResult result) { + device = result.getDevice();//得到设备 + // Log.e(TAG, "发现设备" + device.getName()); + + } + + @Override + public void onScanFailed(int errorCode) { + super.onScanFailed(errorCode); + Log.e(TAG, "搜索错误" + errorCode); + } + }; + ScanFilter sn = new ScanFilter.Builder().setDeviceName("蓝牙设备的名称").setServiceUuid(ParcelUuid.fromString("0000FFE0-0000-1000-8000-00805F9B34FB")).build(); + List scanFilters = new ArrayList<>(); + scanFilters.add(sn); + if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) { + // TODO: Consider calling + // ActivityCompat#requestPermissions + return; + } + scanner.startScan(scanFilters, new ScanSettings.Builder().build(), callback); + bluetoothGatt = device.connectGatt(this, false, gattCallback); + } + + BluetoothGattCallback gattCallback = new BluetoothGattCallback() { + //GATT的链接状态回调 + @Override + public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { + super.onConnectionStateChange(gatt, status, newState); + if (newState == BluetoothProfile.STATE_CONNECTED) { + if (ActivityCompat.checkSelfPermission(RouteActivity.this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { + return; + } + gatt.discoverServices(); + Log.v(TAG, "连接成功"); + } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { + Log.e(TAG, "连接断开"); + } else if (newState == BluetoothProfile.STATE_CONNECTING) { + //TODO 在实际过程中,该方法并没有调用 + Log.e(TAG, "连接中...."); + } + } + + //获取GATT服务发现后的回调 + @Override + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + Log.e(TAG, "GATT_SUCCESS"); //服务发现 + for (BluetoothGattService bluetoothGattService : gatt.getServices()) { + Log.e(TAG, "Service_UUID" + bluetoothGattService.getUuid()); // 我们可以遍历到该蓝牙设备的全部Service对象。然后通过比较Service的UUID,我们可以区分该服务是属于什么业务的 + if (SERVICE_UUID.equals(bluetoothGattService.getUuid().toString())) { + + for (BluetoothGattCharacteristic characteristic : bluetoothGattService.getCharacteristics()) { + prepareBroadcastDataNotify(gatt, characteristic); //给满足条件的属性配置上消息通知 + } + return;//结束循环操作 + } + } + } else { + Log.e(TAG, "onServicesDiscovered received: " + status); + } + } + + //蓝牙设备发送消息后的自动监听 + @Override + public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { + // readUUID 是我要链接的蓝牙设备的消息读UUID值,跟通知的特性的UUID比较。这样可以避免其他消息的污染。 + if (READ_UUID.equals(characteristic.getUuid().toString())) { + try { + String chara = new String(characteristic.getValue(), "UTF-8"); + Log.e(TAG, "消息内容:" + chara); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + } + }; + + @SuppressLint("MissingPermission") + private void prepareBroadcastDataNotify(BluetoothGatt mBluetoothGatt, BluetoothGattCharacteristic characteristic) { + Log.e(TAG, "CharacteristicUUID:" + characteristic.getUuid().toString()); + int charaProp = characteristic.getProperties(); + //判断属性是否支持消息通知 + if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) { + BluetoothGattDescriptor descriptor = + characteristic.getDescriptor(UUID.fromString(READ_DEDSCRIPTION_UUID)); + if (descriptor != null) { + //注册消息通知 + mBluetoothGatt.setCharacteristicNotification(characteristic, true); + descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); + mBluetoothGatt.writeDescriptor(descriptor); + } + } + } + + public void onServicesDiscovered(BluetoothGatt gatt, int status) { + if (status == BluetoothGatt.GATT_SUCCESS) { + BluetoothGattService service = gatt.getService(UUID.fromString(SERVICE_UUID)); + if (service != null) { + // 假设写特征的 UUID 就是 READ_UUID(或者你需要再定义一个 WRITE_UUID) + writeCharacteristic = service.getCharacteristic(UUID.fromString(READ_UUID)); + // 如果该特征需要先 enableNotification,也可在这里设置: + if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { + return; + } + gatt.setCharacteristicNotification(writeCharacteristic, true); + } + } + } + + /** + * 通过 BLE 将 poiList 中的每个坐标点逐条发送到从机。 + * 由于单包 MTU 默认为 20 字节,必要时需要分包。 + */ + private void sendPoiList(List poiList) { + if (writeCharacteristic == null || bluetoothGatt == null) { + showMsg("蓝牙特征或连接未准备好"); + return; + } + + for (LatLng p : poiList) { + // 1) 将坐标格式化为字符串,比如:"31.2304,121.4737\n" + String str = p.latitude + "," + p.longitude + "\n"; + byte[] data = str.getBytes(StandardCharsets.UTF_8); + + // 2) 如果 data.length > MTU,需要分包;这里简单示例不分包 + writeCharacteristic.setValue(data); + + // 3) 发写命令,异步执行 + if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { + return; + } + boolean success = bluetoothGatt.writeCharacteristic(writeCharacteristic); + if (!success) { + Log.e(TAG, "写入特征失败:" + str); + } + + // 4) 为避免短时间内连续写多包导致丢包,可在这里短暂停一下(示例用 Handler) + try { + Thread.sleep(50); + } catch (InterruptedException ignored) {} + } + } + + /** + * 初始化出行方式 + */ + private void initTravelMode() { + Spinner spinner = findViewById(R.id.spinner); + + //将可选内容与ArrayAdapter连接起来 + arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, travelModeArray); + //设置下拉列表的风格 + arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + //将adapter 添加到spinner中 + spinner.setAdapter(arrayAdapter); + //添加事件Spinner事件监听 + spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + TRAVEL_MODE = position; + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + //键盘按键监听 + //起点 键盘按键监听 + binding.etStartAddress.setOnKeyListener(this); + //终点 键盘按键监听 + binding.etEndAddress.setOnKeyListener(this); + + + } + + /** + * 初始化路线 + */ + private void initRoute() { + try { + routeSearch = new RouteSearch(this); + } catch (AMapException e) { + e.printStackTrace(); + } + routeSearch.setRouteSearchListener(this); + } + + /** + * 初始化定位 + */ + private void initLocation() { + //初始化定位 + try { + mLocationClient = new AMapLocationClient(getApplicationContext()); + } catch (Exception e) { + e.printStackTrace(); + } + if (mLocationClient != null) { + mLocationClient.setLocationListener(this); + mLocationOption = new AMapLocationClientOption(); + mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); + mLocationOption.setOnceLocationLatest(true); + mLocationOption.setNeedAddress(true); + mLocationOption.setHttpTimeOut(20000); + mLocationOption.setLocationCacheEnable(false); + mLocationClient.setLocationOption(mLocationOption); + } + } + + /** + * 初始化地图 + * + * @param savedInstanceState + */ + private void initMap(Bundle savedInstanceState) { + binding.mapView.onCreate(savedInstanceState); + //初始化地图控制器对象 + aMap = binding.mapView.getMap(); + //设置最小缩放等级为12 ,缩放级别范围为[3, 20] + aMap.setMinZoomLevel(12); + //开启室内地图 + aMap.showIndoorMap(true); + //实例化UiSettings类对象 + mUiSettings = aMap.getUiSettings(); + //隐藏缩放按钮 默认显示 + mUiSettings.setZoomControlsEnabled(false); + //显示比例尺 默认不显示 + mUiSettings.setScaleControlsEnabled(true); + // 自定义定位蓝点图标 + myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.gps_point)); + //设置定位蓝点的Style + aMap.setMyLocationStyle(myLocationStyle); + // 设置定位监听 + aMap.setLocationSource(this); + // 设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false + aMap.setMyLocationEnabled(true); + //地图点击监听 + aMap.setOnMapClickListener(this); + //构造 GeocodeSearch 对象 + try { + geocodeSearch = new GeocodeSearch(this); + } catch (AMapException e) { + throw new RuntimeException(e); + } + //设置监听 + geocodeSearch.setOnGeocodeSearchListener(this); + + } + + @Override + public void onLocationChanged(AMapLocation aMapLocation) { + if (aMapLocation != null) { + if (aMapLocation.getErrorCode() == 0) { + //地址 + String address = aMapLocation.getAddress(); + //获取纬度 + double latitude = aMapLocation.getLatitude(); + //获取经度 + double longitude = aMapLocation.getLongitude(); + //设置当前所在地 + //地址 + locationAddress = aMapLocation.getAddress(); + //设置当前所在地 + binding.etStartAddress.setText(locationAddress); + //binding.etStartAddress.setEnabled(false);//禁用输入 + //城市赋值 + city = aMapLocation.getCity(); + Log.d(TAG, address); + //设置起点 + mStartPoint = convertToLatLonPoint(new LatLng(latitude, longitude)); + //停止定位后,本地定位服务并不会被销毁 + mLocationClient.stopLocation(); + + //显示地图定位结果 + if (mListener != null) { + // 显示系统图标 + mListener.onLocationChanged(aMapLocation); + } + + } else { + //定位失败时,可通过ErrCode(错误码)信息来确定失败的原因,errInfo是错误信息,详见错误码表。 + Log.e("AmapError", "location Error, ErrCode:" + + aMapLocation.getErrorCode() + ", errInfo:" + + aMapLocation.getErrorInfo()); + } + } + } + + /** + * 开始路线搜索 + */ + private void startRouteSearch() { + //在地图上添加起点Marker + aMap.addMarker(new MarkerOptions() + .position(convertToLatLng(mStartPoint)) + .icon(BitmapDescriptorFactory.fromResource(R.drawable.start))); + //在地图上添加终点Marker + aMap.addMarker(new MarkerOptions() + .position(convertToLatLng(mEndPoint)) + .icon(BitmapDescriptorFactory.fromResource(R.drawable.end))); + + //搜索路线 构建路径的起终点 + final RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo( + mStartPoint, mEndPoint); + //出行方式判断 + switch (TRAVEL_MODE) { + case 0://步行 + //构建步行路线搜索对象 + RouteSearch.WalkRouteQuery query = new RouteSearch.WalkRouteQuery(fromAndTo, RouteSearch.WalkDefault); + // 异步路径规划步行模式查询 + routeSearch.calculateWalkRouteAsyn(query); + break; + case 1://骑行 + //构建驾车路线搜索对象 第三个参数表示公交查询城市区号,第四个参数表示是否计算夜班车,0表示不计算,1表示计算 + RouteSearch.BusRouteQuery busQuery = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BusLeaseWalk, city, 1); + //公交规划路径计算 + routeSearch.calculateBusRouteAsyn(busQuery); + break; + default: + break; + } + + } + + + @Override + public void activate(LocationSource.OnLocationChangedListener onLocationChangedListener) { + mListener = onLocationChangedListener; + if (mLocationClient == null) { + mLocationClient.startLocation();//启动定位 + } + } + + @Override + public void deactivate() { + mListener = null; + if (mLocationClient != null) { + mLocationClient.stopLocation(); + mLocationClient.onDestroy(); + } + mLocationClient = null; + } + + + @Override + protected void onResume() { + super.onResume(); + binding.mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + binding.mapView.onPause(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + binding.mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + //销毁定位客户端,同时销毁本地定位服务。 + if (mLocationClient != null) { + mLocationClient.onDestroy(); + } + binding.mapView.onDestroy(); + + if (bluetoothGatt != null) { + if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { + return; + } + bluetoothGatt.close(); + bluetoothGatt.disconnect(); + bluetoothGatt = null; + } + + } + + private void showMsg(CharSequence llw) { + Toast.makeText(this, llw, Toast.LENGTH_SHORT).show(); + } + + /** + * 点击地图 + */ + @Override + public void onMapClick(LatLng latLng) { + //终点 + mEndPoint = convertToLatLonPoint(latLng); + //开始路线搜索 + startRouteSearch(); + } + + /** + * 公交规划路径结果 + * + * @param busRouteResult 结果 + * @param code 结果码 + */ + @Override + public void onBusRouteSearched(BusRouteResult busRouteResult, int code) { + aMap.clear();// 清理地图上的所有覆盖物 + if (code != AMapException.CODE_AMAP_SUCCESS) { + showMsg("错误码;" + code); + return; + } + if (busRouteResult == null || busRouteResult.getPaths() == null) { + showMsg("对不起,没有搜索到相关数据!"); + return; + } + if (busRouteResult.getPaths().isEmpty()) { + showMsg("对不起,没有搜索到相关数据!"); + return; + } + final BusPath busPath = busRouteResult.getPaths().get(0); + if (busPath == null) { + return; + } + // 绘制路线 + BusRouteOverlay busRouteOverlay = new BusRouteOverlay( + this, aMap, busPath, + busRouteResult.getStartPos(), + busRouteResult.getTargetPos()); + busRouteOverlay.removeFromMap(); + busRouteOverlay.addToMap(); + busRouteOverlay.zoomToSpan(); + + int dis = (int) busPath.getDistance(); + int dur = (int) busPath.getDuration(); + String des = MapUtil.getFriendlyTime(dur) + "(" + MapUtil.getFriendlyLength(dis) + ")"; + Log.d(TAG, des); + //显示公交花费时间 + //显示公交花费时间 + binding.tvTime.setText(des); + binding.layBottom.setVisibility(View.VISIBLE); + //跳转到路线详情页面 + binding.tvDetail.setOnClickListener(v -> { + Intent intent = new Intent(RouteActivity.this, + RouteDetailActivity.class); + intent.putExtra("type",1); + intent.putExtra("path", busPath); + startActivity(intent); + }); + List allPoiPoints = new ArrayList<>(); + + List steps = busPath.getSteps(); + for (BusStep step : steps) { + + // 🟢 步行部分 + if (step.getWalk() != null && step.getWalk().getSteps() != null) { + List walkSteps = step.getWalk().getSteps(); + for (WalkStep walkStep : walkSteps) { + List polyline = walkStep.getPolyline(); + for (LatLonPoint point : polyline) { + allPoiPoints.add(new LatLng(point.getLatitude(), point.getLongitude())); + Log.d("Walk:",point.getLatitude() + "," + point.getLongitude()); + } + } + } + + // 🟠 公交部分 + if (step.getBusLines() != null && !step.getBusLines().isEmpty()) { + BusLineItem busLine = step.getBusLines().get(0); // 取当前段第一条公交车 + List busPoints = ((RouteBusLineItem) busLine).getPolyline(); // 🚍 公交线路坐标 + for (LatLonPoint point : busPoints) { + allPoiPoints.add(new LatLng(point.getLatitude(), point.getLongitude())); + Log.d("Bus:",point.getLatitude() + "," + point.getLongitude()); + } + } + } + +// // 🧪 测试打印 +// for (LatLng p : poiList) { +// Log.d("POI_POINT", p.latitude + "," + p.longitude); +// } + + + } + + + @Override + public void onDriveRouteSearched(DriveRouteResult driveRouteResult, int i) { + + } + + /** + * 步行规划路径结果 + * + * @param walkRouteResult 结果 + * @param code 结果码 + */ + @Override + public void onWalkRouteSearched(WalkRouteResult walkRouteResult, int code) { + aMap.clear();// 清理地图上的所有覆盖物 + if (code != AMapException.CODE_AMAP_SUCCESS) { + showMsg("错误码;" + code); + return; + } + if (walkRouteResult == null || walkRouteResult.getPaths() == null) { + showMsg("对不起,没有搜索到相关数据!"); + return; + } + if (walkRouteResult.getPaths().isEmpty()) { + showMsg("对不起,没有搜索到相关数据!"); + return; + } + final WalkPath walkPath = walkRouteResult.getPaths().get(0); + if (walkPath == null) { + return; + } + //绘制路线 + WalkRouteOverlay walkRouteOverlay = new WalkRouteOverlay( + this, aMap, walkPath, + walkRouteResult.getStartPos(), + walkRouteResult.getTargetPos()); + walkRouteOverlay.removeFromMap(); + walkRouteOverlay.addToMap(); + walkRouteOverlay.zoomToSpan(); + + int dis = (int) walkPath.getDistance(); + int dur = (int) walkPath.getDuration(); + String des = MapUtil.getFriendlyTime(dur) + "(" + MapUtil.getFriendlyLength(dis) + ")"; + Log.d(TAG, des); + //显示步行花费时间 + binding.tvTime.setText(des); + binding.layBottom.setVisibility(View.VISIBLE); + //跳转到路线详情页面 + binding.tvDetail.setOnClickListener(v -> { + Intent intent = new Intent(RouteActivity.this, + RouteDetailActivity.class); + intent.putExtra("type",0); + intent.putExtra("path", walkPath); + startActivity(intent); + }); + List poiList = new ArrayList<>(); + + List steps = walkPath.getSteps(); + for (WalkStep step : steps) { + List polyline = step.getPolyline(); + for (LatLonPoint point : polyline) { + LatLng latLng = new LatLng(point.getLatitude(), point.getLongitude()); + poiList.add(latLng); + } + } + +// 🧪 测试打印 + for (LatLng p : poiList) { + Log.d("POI_POINT", p.latitude + "," + p.longitude); + } + sendPoiList(poiList); + } + + + @Override + public void onRideRouteSearched(RideRouteResult rideRouteResult, int i) { + + } + + @Override + public void onRegeocodeSearched(RegeocodeResult regeocodeResult, int i) { + + } + + /** + * 地址转坐标 + * + * @param geocodeResult + * @param rCode + */ + @Override + public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) { + if (rCode != PARSE_SUCCESS_CODE) { + showMsg("获取坐标失败,错误码:" + rCode); + return; + } + List geocodeAddressList = geocodeResult.getGeocodeAddressList(); + if (geocodeAddressList != null && !geocodeAddressList.isEmpty()) { + //判断是不是起点的搜索 + if (tag == 1) { + //起点 + mStartPoint = geocodeAddressList.get(0).getLatLonPoint(); + } else { + //终点 + mEndPoint = geocodeAddressList.get(0).getLatLonPoint(); + } + if (mStartPoint != null && mEndPoint != null) { + //开始路线搜索 + startRouteSearch(); + } + } + } + + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) { + //获取输入框的值 出发地(起点) + String startAddress = binding.etStartAddress.getText().toString().trim(); + //获取输入框的值 目的地(终点) + String endAddress = binding.etEndAddress.getText().toString().trim(); + + //判断出发地是否有值 不管这个值是定位还是手动输入 + if (startAddress.isEmpty()) { + showMsg("请输入当前的出发地"); + return false; + } + //判断目的地是否有值 + if (endAddress.isEmpty()) { + showMsg("请输入要前往的目的地"); + return false; + } + + //当出发地输入框有值的时候,判断这个值是否是定位的地址,是则说明你没有更改过,则不需要进行地址转坐标,不是则需要转换。 + if (!locationAddress.equals(startAddress)) { + tag = 1; + GeocodeQuery startQuery = new GeocodeQuery(startAddress, city); + geocodeSearch.getFromLocationNameAsyn(startQuery); + } else { + tag = -1; + } + + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + //隐藏软键盘 + imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0); + + //通过输入的目的地转为经纬度,然后进行地图上添加标点,最后计算出行路线规划 + + // name表示地址,第二个参数表示查询城市,中文或者中文全拼,citycode、adcode + GeocodeQuery endQuery = new GeocodeQuery(endAddress, city); + geocodeSearch.getFromLocationNameAsyn(endQuery); + return true; + } + return false; + } + +} diff --git a/app/src/main/java/com/example/mangowalking/RouteDetailActivity.java b/app/src/main/java/com/example/mangowalking/RouteDetailActivity.java new file mode 100644 index 0000000..32a4c03 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/RouteDetailActivity.java @@ -0,0 +1,147 @@ +package com.example.mangowalking; + +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +import androidx.activity.EdgeToEdge; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.amap.api.services.route.BusPath; +import com.amap.api.services.route.BusStep; +import com.amap.api.services.route.WalkPath; +import com.example.mangowalking.adapter.BusSegmentListAdapter; +import com.example.mangowalking.adapter.WalkSegmentListAdapter; +import com.example.mangowalking.databinding.ActivityRouteDetailBinding; +import com.example.mangowalking.utils.MapUtil; +import com.example.mangowalking.utils.SchemeBusStep; + +import java.text.BreakIterator; +import java.util.ArrayList; +import java.util.List; + +public class RouteDetailActivity extends AppCompatActivity { + + private ActivityRouteDetailBinding binding; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + binding = ActivityRouteDetailBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); + initView(); + } + + + /** + * 初始化视图 + */ + private void initView() { + binding.toolbar.setNavigationOnClickListener(v -> finish()); + Intent intent = getIntent(); + if (intent == null) { + return; + } + switch (intent.getIntExtra("type", 0)) { + case 0://步行 + walkDetail(intent); + break; + case 1:// + busDetail(intent); + break; + default: + break; + } + } + + /** + * 步行详情 + * @param intent + */ + private void walkDetail(Intent intent) { + binding.toolbar.setTitle("步行路线规划"); + WalkPath walkPath = intent.getParcelableExtra("path"); + String dur = MapUtil.getFriendlyTime((int) walkPath.getDuration()); + String dis = MapUtil.getFriendlyLength((int) walkPath.getDistance()); + binding.tvTime.setText(dur + "(" + dis + ")"); + binding.rvRouteDetail.setLayoutManager(new LinearLayoutManager(this)); + binding.rvRouteDetail.setAdapter(new WalkSegmentListAdapter(walkPath.getSteps())); + } + + private void busDetail(Intent intent) { + binding.toolbar.setTitle("公交路线规划"); + + BusPath busPath = intent.getParcelableExtra("path"); + if (busPath == null) { + Log.e("RouteDetail", "busPath is null!"); + return; + } + + String dur = MapUtil.getFriendlyTime((int) busPath.getDuration()); + String dis = MapUtil.getFriendlyLength((int) busPath.getDistance()); + binding.tvTime.setText(dur + "(" + dis + ")"); + + List stepList = getBusSteps(busPath.getSteps()); + Log.d("RouteDetail", "Steps count: " + stepList.size()); + + binding.rvRouteDetail.setLayoutManager(new LinearLayoutManager(this)); + binding.rvRouteDetail.setAdapter(new BusSegmentListAdapter(R.layout.item_segment, stepList)); + } + + + + + + /** + * 公交方案数据组装 + * @param list + * @return + */ + private List getBusSteps(List list) { + List busStepList = new ArrayList<>(); + SchemeBusStep start = new SchemeBusStep(null); + start.setStart(true); + busStepList.add(start); + for (BusStep busStep : list) { + if (busStep.getWalk() != null && busStep.getWalk().getDistance() > 0) { + SchemeBusStep walk = new SchemeBusStep(busStep); + walk.setWalk(true); + busStepList.add(walk); + } + if (busStep.getBusLine() != null) { + SchemeBusStep bus = new SchemeBusStep(busStep); + bus.setBus(true); + busStepList.add(bus); + } + if (busStep.getRailway() != null) { + SchemeBusStep railway = new SchemeBusStep(busStep); + railway.setRailway(true); + busStepList.add(railway); + } + + if (busStep.getTaxi() != null) { + SchemeBusStep taxi = new SchemeBusStep(busStep); + taxi.setTaxi(true); + busStepList.add(taxi); + } + } + SchemeBusStep end = new SchemeBusStep(null); + end.setEnd(true); + busStepList.add(end); + return busStepList; + } + + +} diff --git a/app/src/main/java/com/example/mangowalking/adapter/BusSegmentListAdapter.java b/app/src/main/java/com/example/mangowalking/adapter/BusSegmentListAdapter.java new file mode 100644 index 0000000..82ad250 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/adapter/BusSegmentListAdapter.java @@ -0,0 +1,175 @@ +package com.example.mangowalking.adapter; + +import static com.amap.api.maps.model.BitmapDescriptorFactory.getContext; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.amap.api.services.busline.BusStationItem; +import com.amap.api.services.route.RailwayStationItem; +import com.example.mangowalking.R; +import com.example.mangowalking.databinding.ItemSegmentBinding; +import com.example.mangowalking.utils.SchemeBusStep; + +import java.util.List; + +/** + * 公交段列表适配器 + */ +public class BusSegmentListAdapter extends RecyclerView.Adapter { + + private List mItemList; + + public BusSegmentListAdapter(int item_segment, List data) { + this.mItemList = data; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(ItemSegmentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + SchemeBusStep item = mItemList.get(position); + if (position == 0) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir_start); + holder.binding.busLineName.setText("出发"); + holder.binding.busDirIconUp.setVisibility(View.INVISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + holder.binding.busSegSplitLine.setVisibility(View.INVISIBLE); + } else if (position == mItemList.size() - 1) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir_end); + holder.binding.busLineName.setText("到达终点"); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.INVISIBLE); + } else { + if (item.isWalk() && item.getWalk() != null && item.getWalk().getDistance() > 0) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir13); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + holder.binding.busLineName.setText("步行" + + (int) item.getWalk().getDistance() + "米"); + holder.binding.busStationNum.setVisibility(View.GONE); + holder.binding.busExpandImage.setVisibility(View.GONE); + } else if (item.isBus() && item.getBusLines().size() > 0) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir14); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + holder.binding.busLineName.setText(item.getBusLines().get(0).getBusLineName()); + holder.binding.busStationNum.setVisibility(View.VISIBLE); + holder.binding.busStationNum + .setText((item.getBusLines().get(0).getPassStationNum() + 1) + "站"); + holder.binding.busExpandImage.setVisibility(View.VISIBLE); + } else if (item.isRailway() && item.getRailway() != null) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir16); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + holder.binding.busLineName.setText(item.getRailway().getName()); + holder.binding.busStationNum.setVisibility(View.VISIBLE); + holder.binding.busStationNum + .setText((item.getRailway().getViastops().size() + 1) + "站"); + holder.binding.busExpandImage.setVisibility(View.VISIBLE); + } else if (item.isTaxi() && item.getTaxi() != null) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir14); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + holder.binding.busLineName.setText("打车到终点"); + holder.binding.busStationNum.setVisibility(View.GONE); + holder.binding.busExpandImage.setVisibility(View.GONE); + } + } + + holder.binding.busItem.setOnClickListener(v -> { + if (item.isBus()) {//公交 + if (!item.isArrowExpend()) { + item.setArrowExpend(true); + holder.binding.busExpandImage.setImageResource(R.drawable.up); + addBusStation(item.getBusLine().getDepartureBusStation(), holder.binding.expandContent); + for (BusStationItem station : item.getBusLine() + .getPassStations()) { + addBusStation(station, holder.binding.expandContent); + } + addBusStation(item.getBusLine().getArrivalBusStation(), holder.binding.expandContent); + + } else { + item.setArrowExpend(false); + holder.binding.busExpandImage.setImageResource(R.drawable.down); + holder.binding.expandContent.removeAllViews(); + } + } else if (item.isRailway()) {//火车 + if (!item.isArrowExpend()) { + item.setArrowExpend(true); + holder.binding.busExpandImage.setImageResource(R.drawable.up); + addRailwayStation(item.getRailway().getDeparturestop(), holder.binding.expandContent); + for (RailwayStationItem station : item.getRailway().getViastops()) { + addRailwayStation(station, holder.binding.expandContent); + } + addRailwayStation(item.getRailway().getArrivalstop(), holder.binding.expandContent); + + } else { + item.setArrowExpend(false); + holder.binding.busExpandImage.setImageResource(R.drawable.down); + holder.binding.expandContent.removeAllViews(); + } + } + }); + } + + /** + * 添加公交车站 + * @param station + * @param expandContent + */ + private void addBusStation(BusStationItem station, LinearLayout expandContent) { + LinearLayout ll = (LinearLayout) View.inflate(getContext(), + R.layout.item_segment_ex, null); + TextView tv = ll.findViewById(R.id.bus_line_station_name); + tv.setText(station.getBusStationName()); + expandContent.addView(ll); + } + + /** + * 添加火车站 + * @param station + * @param expandContent + */ + private void addRailwayStation(RailwayStationItem station, LinearLayout expandContent) { + LinearLayout ll = (LinearLayout) View.inflate(getContext(), + R.layout.item_segment_ex, null); + TextView tv = ll + .findViewById(R.id.bus_line_station_name); + tv.setText(station.getName() + " " + getRailwayTime(station.getTime())); + expandContent.addView(ll); + } + + /** + * 获取铁路时间 + * @param time + * @return + */ + public static String getRailwayTime(String time) { + return time.substring(0, 2) + ":" + time.substring(2, time.length()); + } + + @Override + public int getItemCount() { + return mItemList == null || mItemList.isEmpty() ? 0 : mItemList.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + ItemSegmentBinding binding; + + public ViewHolder(ItemSegmentBinding itemView) { + super(itemView.getRoot()); + this.binding = itemView; + } + } +} diff --git a/app/src/main/java/com/example/mangowalking/adapter/WalkSegmentListAdapter.java b/app/src/main/java/com/example/mangowalking/adapter/WalkSegmentListAdapter.java new file mode 100644 index 0000000..20d6c1b --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/adapter/WalkSegmentListAdapter.java @@ -0,0 +1,72 @@ +package com.example.mangowalking.adapter; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.amap.api.services.route.WalkStep; +import com.example.mangowalking.R; +import com.example.mangowalking.databinding.ItemSegmentBinding; +import com.example.mangowalking.utils.MapUtil; + +import java.util.List; + +/** + * 步行段列表适配器 + */ +public class WalkSegmentListAdapter extends RecyclerView.Adapter { + + private List mItemList; + + public WalkSegmentListAdapter(List data) { + this.mItemList = data; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(ItemSegmentBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + WalkStep item = mItemList.get(position); + if (position == 0) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir_start); + holder.binding.busLineName.setText("出发"); + holder.binding.busDirIconUp.setVisibility(View.INVISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + holder.binding.busSegSplitLine.setVisibility(View.INVISIBLE); + } else if (position == mItemList.size() - 1) { + holder.binding.busDirIcon.setImageResource(R.drawable.dir_end); + holder.binding.busLineName.setText("到达终点"); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.INVISIBLE); + } else { + holder.binding.busSegSplitLine.setVisibility(View.VISIBLE); + holder.binding.busDirIconUp.setVisibility(View.VISIBLE); + holder.binding.busDirIconDown.setVisibility(View.VISIBLE); + String actionName = item.getAction(); + int resID = MapUtil.getWalkActionID(actionName); + holder.binding.busDirIcon.setImageResource(resID); + holder.binding.busLineName.setText(item.getInstruction()); + } + } + + @Override + public int getItemCount() { + return mItemList == null || mItemList.isEmpty() ? 0 : mItemList.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + ItemSegmentBinding binding; + + public ViewHolder(ItemSegmentBinding itemView) { + super(itemView.getRoot()); + this.binding = itemView; + } + } +} diff --git a/app/src/main/java/com/example/mangowalking/overlay/AMapServicesUtil.java b/app/src/main/java/com/example/mangowalking/overlay/AMapServicesUtil.java new file mode 100644 index 0000000..e961054 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/overlay/AMapServicesUtil.java @@ -0,0 +1,60 @@ +package com.example.mangowalking.overlay; + +/** + * 地图服务工具类 + */ + +import android.graphics.Bitmap; + +import com.amap.api.maps.model.LatLng; +import com.amap.api.services.core.LatLonPoint; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +class AMapServicesUtil { + public static int BUFFER_SIZE = 2048; + + public static byte[] inputStreamToByte(InputStream in) throws IOException { + + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + byte[] data = new byte[BUFFER_SIZE]; + int count = -1; + while ((count = in.read(data, 0, BUFFER_SIZE)) != -1){ + outStream.write(data, 0, count); + } + + data = null; + return outStream.toByteArray(); + } + public static LatLonPoint convertToLatLonPoint(LatLng latlon) { + return new LatLonPoint(latlon.latitude, latlon.longitude); + } + public static LatLng convertToLatLng(LatLonPoint latLonPoint) { + return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude()); + } + public static ArrayList convertArrList(List shapes) { + ArrayList lineShapes = new ArrayList(); + for (LatLonPoint point : shapes) { + LatLng latLngTemp = AMapServicesUtil.convertToLatLng(point); + lineShapes.add(latLngTemp); + } + return lineShapes; + } + public static Bitmap zoomBitmap(Bitmap bitmap, float res) { + if (bitmap == null) { + return null; + } + int width, height; + width = (int) (bitmap.getWidth() * res); + height = (int) (bitmap.getHeight() * res); + Bitmap newbmp = Bitmap.createScaledBitmap(bitmap, width, height, true); + return newbmp; + } + + + +} diff --git a/app/src/main/java/com/example/mangowalking/overlay/BusRouteOverlay.java b/app/src/main/java/com/example/mangowalking/overlay/BusRouteOverlay.java new file mode 100644 index 0000000..38a5636 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/overlay/BusRouteOverlay.java @@ -0,0 +1,496 @@ +package com.example.mangowalking.overlay; + +import android.content.Context; + +import com.amap.api.maps.AMap; +import com.amap.api.maps.model.LatLng; +import com.amap.api.maps.model.MarkerOptions; +import com.amap.api.maps.model.PolylineOptions; +import com.amap.api.services.busline.BusStationItem; +import com.amap.api.services.core.LatLonPoint; +import com.amap.api.services.route.BusPath; +import com.amap.api.services.route.BusStep; +import com.amap.api.services.route.Doorway; +import com.amap.api.services.route.RailwayStationItem; +import com.amap.api.services.route.RouteBusLineItem; +import com.amap.api.services.route.RouteBusWalkItem; +import com.amap.api.services.route.RouteRailwayItem; +import com.amap.api.services.route.TaxiItem; +import com.amap.api.services.route.WalkStep; +import com.example.mangowalking.utils.MapUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * 公交路线图层类。在高德地图API里,如果需要显示公交路线,可以用此类来创建公交路线图层。如不满足需求,也可以自己创建自定义的公交路线图层。 + * @since V2.1.0 + */ +public class BusRouteOverlay extends RouteOverlay { + + private BusPath busPath; + private LatLng latLng; + + /** + * 通过此构造函数创建公交路线图层。 + * @param context 当前activity。 + * @param amap 地图对象。 + * @param path 公交路径规划的一个路段。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类 BusPath。 + * @param start 起点坐标。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类 LatLonPoint。 + * @param end 终点坐标。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类 LatLonPoint。 + * @since V2.1.0 + */ + public BusRouteOverlay(Context context, AMap amap, BusPath path, + LatLonPoint start, LatLonPoint end) { + super(context); + this.busPath = path; + startPoint = MapUtil.convertToLatLng(start); + endPoint = MapUtil.convertToLatLng(end); + mAMap = amap; + } + + /** + * 添加公交路线到地图上。 + * @since V2.1.0 + */ + + public void addToMap() { + /** + * 绘制节点和线
+ * 细节情况较多
+ * 两个step之间,用step和step1区分
+ * 1.一个step内可能有步行和公交,然后有可能他们之间连接有断开
+ * 2.step的公交和step1的步行,有可能连接有断开
+ * 3.step和step1之间是公交换乘,且没有步行,需要把step的终点和step1的起点连起来
+ * 4.公交最后一站和终点间有步行,加入步行线路,还会有一些步行marker
+ * 5.公交最后一站和终点间无步行,之间连起来
+ */ + try { + List busSteps = busPath.getSteps(); + for (int i = 0; i < busSteps.size(); i++) { + BusStep busStep = busSteps.get(i); + if (i < busSteps.size() - 1) { + BusStep busStep1 = busSteps.get(i + 1);// 取得当前下一个BusStep对象 + // 假如步行和公交之间连接有断开,就把步行最后一个经纬度点和公交第一个经纬度点连接起来,避免断线问题 + if (busStep.getWalk() != null + && busStep.getBusLine() != null) { + checkWalkToBusline(busStep); + } + + // 假如公交和步行之间连接有断开,就把上一公交经纬度点和下一步行第一个经纬度点连接起来,避免断线问题 + if (busStep.getBusLine() != null + && busStep1.getWalk() != null + && busStep1.getWalk().getSteps().size() > 0) { + checkBusLineToNextWalk(busStep, busStep1); + } + // 假如两个公交换乘中间没有步行,就把上一公交经纬度点和下一步公交第一个经纬度点连接起来,避免断线问题 + if (busStep.getBusLine() != null + && busStep1.getWalk() == null + && busStep1.getBusLine() != null) { + checkBusEndToNextBusStart(busStep, busStep1); + } + // 和上面的很类似 + if (busStep.getBusLine() != null + && busStep1.getWalk() == null + && busStep1.getBusLine() != null) { + checkBusToNextBusNoWalk(busStep, busStep1); + } + if (busStep.getBusLine() != null + && busStep1.getRailway() != null ) { + checkBusLineToNextRailway(busStep, busStep1); + } + if (busStep1.getWalk() != null && + busStep1.getWalk().getSteps().size() > 0 && + busStep.getRailway() != null) { + checkRailwayToNextWalk(busStep, busStep1); + } + + if ( busStep1.getRailway() != null && + busStep.getRailway() != null) { + checkRailwayToNextRailway(busStep, busStep1); + } + + if (busStep.getRailway() != null && + busStep1.getTaxi() != null ){ + checkRailwayToNextTaxi(busStep, busStep1); + } + + + } + + if (busStep.getWalk() != null + && busStep.getWalk().getSteps().size() > 0) { + addWalkSteps(busStep); + } else { + if (busStep.getBusLine() == null && busStep.getRailway() == null && busStep.getTaxi() == null) { + addWalkPolyline(latLng, endPoint); + } + } + if (busStep.getBusLine() != null) { + RouteBusLineItem routeBusLineItem = busStep.getBusLine(); + addBusLineSteps(routeBusLineItem); + addBusStationMarkers(routeBusLineItem); + if (i == busSteps.size() - 1) { + addWalkPolyline(MapUtil.convertToLatLng(getLastBuslinePoint(busStep)), endPoint); + } + } + if (busStep.getRailway() != null) { + addRailwayStep(busStep.getRailway()); + addRailwayMarkers(busStep.getRailway()); + if (i == busSteps.size() - 1) { + addWalkPolyline(MapUtil.convertToLatLng(busStep.getRailway().getArrivalstop().getLocation()), endPoint); + } + } + if (busStep.getTaxi() != null) { + addTaxiStep(busStep.getTaxi()); + addTaxiMarkers(busStep.getTaxi()); + } + } + addStartAndEndMarker(); + + } catch (Throwable e) { + e.printStackTrace(); + } + } + + + + private void checkRailwayToNextTaxi(BusStep busStep, BusStep busStep1) { + LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation(); + LatLonPoint taxiFirstPoint = busStep1.getTaxi().getOrigin(); + if (!railwayLastPoint.equals(taxiFirstPoint)) { + addWalkPolyLineByLatLonPoints(railwayLastPoint, taxiFirstPoint); + } + } + + private void checkRailwayToNextRailway(BusStep busStep, BusStep busStep1) { + LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation(); + LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation(); + if (!railwayLastPoint.equals(railwayFirstPoint)) { + addWalkPolyLineByLatLonPoints(railwayLastPoint, railwayFirstPoint); + } + + } + + private void checkBusLineToNextRailway(BusStep busStep, BusStep busStep1) { + LatLonPoint busLastPoint = getLastBuslinePoint(busStep); + LatLonPoint railwayFirstPoint = busStep1.getRailway().getDeparturestop().getLocation(); + if (!busLastPoint.equals(railwayFirstPoint)) { + addWalkPolyLineByLatLonPoints(busLastPoint, railwayFirstPoint); + } + + } + + private void checkRailwayToNextWalk(BusStep busStep, BusStep busStep1) { + LatLonPoint railwayLastPoint = busStep.getRailway().getArrivalstop().getLocation(); + LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1); + if (!railwayLastPoint.equals(walkFirstPoint)) { + addWalkPolyLineByLatLonPoints(railwayLastPoint, walkFirstPoint); + } + + } + + private void addRailwayStep(RouteRailwayItem railway) { + List railwaylistpoint = new ArrayList(); + List railwayStationItems = new ArrayList(); + railwayStationItems.add(railway.getDeparturestop()); + railwayStationItems.addAll(railway.getViastops()); + railwayStationItems.add(railway.getArrivalstop()); + for (int i = 0; i < railwayStationItems.size(); i++) { + railwaylistpoint.add(MapUtil.convertToLatLng(railwayStationItems.get(i).getLocation())); + } + addRailwayPolyline(railwaylistpoint); + } + + private void addTaxiStep(TaxiItem taxi){ + addPolyLine(new PolylineOptions().width(getRouteWidth()) + .color(getBusColor()) + .add(MapUtil.convertToLatLng(taxi.getOrigin())) + .add(MapUtil.convertToLatLng(taxi.getDestination()))); + } + + /** + * @param busStep + */ + private void addWalkSteps(BusStep busStep) { + RouteBusWalkItem routeBusWalkItem = busStep.getWalk(); + List walkSteps = routeBusWalkItem.getSteps(); + for (int j = 0; j < walkSteps.size(); j++) { + WalkStep walkStep = walkSteps.get(j); + if (j == 0) { + LatLng latLng = MapUtil.convertToLatLng(walkStep + .getPolyline().get(0)); + String road = walkStep.getRoad();// 道路名字 + String instruction = getWalkSnippet(walkSteps);// 步行导航信息 + addWalkStationMarkers(latLng, road, instruction); + } + + List listWalkPolyline = MapUtil + .convertArrList(walkStep.getPolyline()); + this.latLng = listWalkPolyline.get(listWalkPolyline.size() - 1); + + addWalkPolyline(listWalkPolyline); + + // 假如步行前一段的终点和下的起点有断开,断画直线连接起来,避免断线问题 + if (j < walkSteps.size() - 1) { + LatLng lastLatLng = listWalkPolyline.get(listWalkPolyline + .size() - 1); + LatLng firstlatLatLng = MapUtil + .convertToLatLng(walkSteps.get(j + 1).getPolyline() + .get(0)); + if (!(lastLatLng.equals(firstlatLatLng))) { + addWalkPolyline(lastLatLng, firstlatLatLng); + } + } + + } + } + + /** + * 添加一系列的bus PolyLine + * + * @param routeBusLineItem + */ + private void addBusLineSteps(RouteBusLineItem routeBusLineItem) { + addBusLineSteps(routeBusLineItem.getPolyline()); + } + + private void addBusLineSteps(List listPoints) { + if (listPoints.size() < 1) { + return; + } + addPolyLine(new PolylineOptions().width(getRouteWidth()) + .color(getBusColor()) + .addAll(MapUtil.convertArrList(listPoints))); + } + + /** + * @param latLng + * marker + * @param title + * @param snippet + */ + private void addWalkStationMarkers(LatLng latLng, String title, + String snippet) { + addStationMarker(new MarkerOptions().position(latLng).title(title) + .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible) + .icon(getWalkBitmapDescriptor())); + } + + /** + * @param routeBusLineItem + */ + private void addBusStationMarkers(RouteBusLineItem routeBusLineItem) { + BusStationItem startBusStation = routeBusLineItem + .getDepartureBusStation(); + LatLng position = MapUtil.convertToLatLng(startBusStation + .getLatLonPoint()); + String title = routeBusLineItem.getBusLineName(); + String snippet = getBusSnippet(routeBusLineItem); + + addStationMarker(new MarkerOptions().position(position).title(title) + .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible) + .icon(getBusBitmapDescriptor())); + } + + private void addTaxiMarkers(TaxiItem taxiItem) { + + LatLng position = MapUtil.convertToLatLng(taxiItem + .getOrigin()); + String title = taxiItem.getmSname()+"打车"; + String snippet = "到终点"; + + addStationMarker(new MarkerOptions().position(position).title(title) + .snippet(snippet).anchor(0.5f, 0.5f).visible(nodeIconVisible) + .icon(getDriveBitmapDescriptor())); + } + + private void addRailwayMarkers(RouteRailwayItem railway) { + LatLng Departureposition = MapUtil.convertToLatLng(railway + .getDeparturestop().getLocation()); + String Departuretitle = railway.getDeparturestop().getName()+"上车"; + String Departuresnippet = railway.getName(); + + addStationMarker(new MarkerOptions().position(Departureposition).title(Departuretitle) + .snippet(Departuresnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible) + .icon(getBusBitmapDescriptor())); + + + LatLng Arrivalposition = MapUtil.convertToLatLng(railway + .getArrivalstop().getLocation()); + String Arrivaltitle = railway.getArrivalstop().getName()+"下车"; + String Arrivalsnippet = railway.getName(); + + addStationMarker(new MarkerOptions().position(Arrivalposition).title(Arrivaltitle) + .snippet(Arrivalsnippet).anchor(0.5f, 0.5f).visible(nodeIconVisible) + .icon(getBusBitmapDescriptor())); + } + /** + * 如果换乘没有步行 检查bus最后一点和下一个step的bus起点是否一致 + * + * @param busStep + * @param busStep1 + */ + private void checkBusToNextBusNoWalk(BusStep busStep, BusStep busStep1) { + LatLng endbusLatLng = MapUtil + .convertToLatLng(getLastBuslinePoint(busStep)); + LatLng startbusLatLng = MapUtil + .convertToLatLng(getFirstBuslinePoint(busStep1)); + if (startbusLatLng.latitude - endbusLatLng.latitude > 0.0001 + || startbusLatLng.longitude - endbusLatLng.longitude > 0.0001) { + drawLineArrow(endbusLatLng, startbusLatLng);// 断线用带箭头的直线连? + } + } + + /** + * + * checkBusToNextBusNoWalk 和这个类似 + * + * @param busStep + * @param busStep1 + */ + private void checkBusEndToNextBusStart(BusStep busStep, BusStep busStep1) { + LatLonPoint busLastPoint = getLastBuslinePoint(busStep); + LatLng endbusLatLng = MapUtil.convertToLatLng(busLastPoint); + LatLonPoint busFirstPoint = getFirstBuslinePoint(busStep1); + LatLng startbusLatLng = MapUtil.convertToLatLng(busFirstPoint); + if (!endbusLatLng.equals(startbusLatLng)) { + drawLineArrow(endbusLatLng, startbusLatLng);// + } + } + + /** + * 检查bus最后一步和下一各step的步行起点是否一致 + * + * @param busStep + * @param busStep1 + */ + private void checkBusLineToNextWalk(BusStep busStep, BusStep busStep1) { + LatLonPoint busLastPoint = getLastBuslinePoint(busStep); + LatLonPoint walkFirstPoint = getFirstWalkPoint(busStep1); + if (!busLastPoint.equals(walkFirstPoint)) { + addWalkPolyLineByLatLonPoints(busLastPoint, walkFirstPoint); + } + } + + /** + * 检查 步行最后一点 和 bus的起点 是否一致 + * + * @param busStep + */ + private void checkWalkToBusline(BusStep busStep) { + LatLonPoint walkLastPoint = getLastWalkPoint(busStep); + LatLonPoint buslineFirstPoint = getFirstBuslinePoint(busStep); + + if (!walkLastPoint.equals(buslineFirstPoint)) { + addWalkPolyLineByLatLonPoints(walkLastPoint, buslineFirstPoint); + } + } + + /** + * @param busStep1 + * @return + */ + private LatLonPoint getFirstWalkPoint(BusStep busStep1) { + return busStep1.getWalk().getSteps().get(0).getPolyline().get(0); + } + + /** + * + */ + private void addWalkPolyLineByLatLonPoints(LatLonPoint pointFrom, + LatLonPoint pointTo) { + LatLng latLngFrom = MapUtil.convertToLatLng(pointFrom); + LatLng latLngTo = MapUtil.convertToLatLng(pointTo); + + addWalkPolyline(latLngFrom, latLngTo); + } + + /** + * @param latLngFrom + * @param latLngTo + * @return + */ + private void addWalkPolyline(LatLng latLngFrom, LatLng latLngTo) { + addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo) + .width(getRouteWidth()).color(getWalkColor()).setDottedLine(true)); + } + + /** + * @param listWalkPolyline + */ + private void addWalkPolyline(List listWalkPolyline) { + + addPolyLine(new PolylineOptions().addAll(listWalkPolyline) + .color(getWalkColor()).width(getRouteWidth()).setDottedLine(true)); + } + + private void addRailwayPolyline(List listPolyline) { + + addPolyLine(new PolylineOptions().addAll(listPolyline) + .color(getDriveColor()).width(getRouteWidth())); + } + + + private String getWalkSnippet(List walkSteps) { + float disNum = 0; + for (WalkStep step : walkSteps) { + disNum += step.getDistance(); + } + return "\u6B65\u884C" + disNum + "\u7C73"; + } + + public void drawLineArrow(LatLng latLngFrom, LatLng latLngTo) { + + addPolyLine(new PolylineOptions().add(latLngFrom, latLngTo).width(3) + .color(getBusColor()).width(getRouteWidth()));// 绘制直线 + } + + private String getBusSnippet(RouteBusLineItem routeBusLineItem) { + return "(" + + routeBusLineItem.getDepartureBusStation().getBusStationName() + + "-->" + + routeBusLineItem.getArrivalBusStation().getBusStationName() + + ") \u7ECF\u8FC7" + (routeBusLineItem.getPassStationNum() + 1) + + "\u7AD9"; + } + + /** + * @param busStep + * @return + */ + private LatLonPoint getLastWalkPoint(BusStep busStep) { + + List walkSteps = busStep.getWalk().getSteps(); + WalkStep walkStep = walkSteps.get(walkSteps.size() - 1); + List lonPoints = walkStep.getPolyline(); + return lonPoints.get(lonPoints.size() - 1); + } + + private LatLonPoint getExitPoint(BusStep busStep) { + Doorway doorway = busStep.getExit(); + if (doorway == null) { + return null; + } + return doorway.getLatLonPoint(); + } + + private LatLonPoint getLastBuslinePoint(BusStep busStep) { + List lonPoints = busStep.getBusLine().getPolyline(); + + return lonPoints.get(lonPoints.size() - 1); + } + + private LatLonPoint getEntrancePoint(BusStep busStep) { + Doorway doorway = busStep.getEntrance(); + if (doorway == null) { + return null; + } + return doorway.getLatLonPoint(); + } + + private LatLonPoint getFirstBuslinePoint(BusStep busStep) { + return busStep.getBusLine().getPolyline().get(0); + } +} + diff --git a/app/src/main/java/com/example/mangowalking/overlay/RouteOverlay.java b/app/src/main/java/com/example/mangowalking/overlay/RouteOverlay.java new file mode 100644 index 0000000..30817ee --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/overlay/RouteOverlay.java @@ -0,0 +1,221 @@ +package com.example.mangowalking.overlay; + +import java.util.ArrayList; +import java.util.List; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; + +import com.amap.api.maps.AMap; +import com.amap.api.maps.CameraUpdateFactory; +import com.amap.api.maps.model.BitmapDescriptor; +import com.amap.api.maps.model.BitmapDescriptorFactory; +import com.amap.api.maps.model.LatLng; +import com.amap.api.maps.model.LatLngBounds; +import com.amap.api.maps.model.Marker; +import com.amap.api.maps.model.MarkerOptions; +import com.amap.api.maps.model.Polyline; +import com.amap.api.maps.model.PolylineOptions; +import com.example.mangowalking.R; + +/** + * 路线图层叠加 + */ +public class RouteOverlay { + protected List stationMarkers = new ArrayList(); + protected List allPolyLines = new ArrayList(); + protected Marker startMarker; + protected Marker endMarker; + protected LatLng startPoint; + protected LatLng endPoint; + protected AMap mAMap; + private Context mContext; + private Bitmap startBit, endBit, busBit, walkBit, driveBit; + protected boolean nodeIconVisible = true; + + public RouteOverlay(Context context) { + mContext = context; + } + + /** + * 去掉BusRouteOverlay上所有的Marker。 + * @since V2.1.0 + */ + public void removeFromMap() { + if (startMarker != null) { + startMarker.remove(); + + } + if (endMarker != null) { + endMarker.remove(); + } + for (Marker marker : stationMarkers) { + marker.remove(); + } + for (Polyline line : allPolyLines) { + line.remove(); + } + destroyBit(); + } + + private void destroyBit() { + if (startBit != null) { + startBit.recycle(); + startBit = null; + } + if (endBit != null) { + endBit.recycle(); + endBit = null; + } + if (busBit != null) { + busBit.recycle(); + busBit = null; + } + if (walkBit != null) { + walkBit.recycle(); + walkBit = null; + } + if (driveBit != null) { + driveBit.recycle(); + driveBit = null; + } + } + /** + * 给起点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 + * @return 更换的Marker图片。 + * @since V2.1.0 + */ + protected BitmapDescriptor getStartBitmapDescriptor() { + return BitmapDescriptorFactory.fromResource(R.drawable.amap_start); + } + /** + * 给终点Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 + * @return 更换的Marker图片。 + * @since V2.1.0 + */ + protected BitmapDescriptor getEndBitmapDescriptor() { + return BitmapDescriptorFactory.fromResource(R.drawable.amap_end); + } + /** + * 给公交Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 + * @return 更换的Marker图片。 + * @since V2.1.0 + */ + protected BitmapDescriptor getBusBitmapDescriptor() { + return BitmapDescriptorFactory.fromResource(R.drawable.amap_bus); + } + /** + * 给步行Marker设置图标,并返回更换图标的图片。如不用默认图片,需要重写此方法。 + * @return 更换的Marker图片。 + * @since V2.1.0 + */ + protected BitmapDescriptor getWalkBitmapDescriptor() { + return BitmapDescriptorFactory.fromResource(R.drawable.amap_man); + } + + protected BitmapDescriptor getDriveBitmapDescriptor() { + return BitmapDescriptorFactory.fromResource(R.drawable.amap_car); + } + + protected void addStartAndEndMarker() { + startMarker = mAMap.addMarker((new MarkerOptions()) + .position(startPoint).icon(getStartBitmapDescriptor()) + .title("\u8D77\u70B9")); + // startMarker.showInfoWindow(); + + endMarker = mAMap.addMarker((new MarkerOptions()).position(endPoint) + .icon(getEndBitmapDescriptor()).title("\u7EC8\u70B9")); + // mAMap.moveCamera(CameraUpdateFactory.newLatLngZoom(startPoint, + // getShowRouteZoom())); + } + /** + * 移动镜头到当前的视角。 + * @since V2.1.0 + */ + public void zoomToSpan() { + if (startPoint != null) { + if (mAMap == null) { + return; + } + try { + LatLngBounds bounds = getLatLngBounds(); + mAMap.animateCamera(CameraUpdateFactory + .newLatLngBounds(bounds, 100)); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + protected LatLngBounds getLatLngBounds() { + LatLngBounds.Builder b = LatLngBounds.builder(); + b.include(new LatLng(startPoint.latitude, startPoint.longitude)); + b.include(new LatLng(endPoint.latitude, endPoint.longitude)); + return b.build(); + } + /** + * 路段节点图标控制显示接口。 + * @param visible true为显示节点图标,false为不显示。 + * @since V2.3.1 + */ + public void setNodeIconVisibility(boolean visible) { + try { + nodeIconVisible = visible; + if (this.stationMarkers != null && this.stationMarkers.size() > 0) { + for (int i = 0; i < this.stationMarkers.size(); i++) { + this.stationMarkers.get(i).setVisible(visible); + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + + protected void addStationMarker(MarkerOptions options) { + if(options == null) { + return; + } + Marker marker = mAMap.addMarker(options); + if(marker != null) { + stationMarkers.add(marker); + } + + } + + protected void addPolyLine(PolylineOptions options) { + if(options == null) { + return; + } + Polyline polyline = mAMap.addPolyline(options); + if(polyline != null) { + allPolyLines.add(polyline); + } + } + + protected float getRouteWidth() { + return 18f; + } + + protected int getWalkColor() { + return Color.parseColor("#6db74d"); + } + + /** + * 自定义路线颜色。 + * return 自定义路线颜色。 + * @since V2.2.1 + */ + protected int getBusColor() { + return Color.parseColor("#537edc"); + } + + protected int getDriveColor() { + return Color.parseColor("#537edc"); + } + + // protected int getShowRouteZoom() { + // return 15; + // } +} + diff --git a/app/src/main/java/com/example/mangowalking/overlay/WalkRouteOverlay.java b/app/src/main/java/com/example/mangowalking/overlay/WalkRouteOverlay.java new file mode 100644 index 0000000..19ad138 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/overlay/WalkRouteOverlay.java @@ -0,0 +1,146 @@ +package com.example.mangowalking.overlay; + +import java.util.List; + +import com.amap.api.maps.AMap; +import com.amap.api.maps.model.BitmapDescriptor; +import com.amap.api.maps.model.LatLng; +import com.amap.api.maps.model.MarkerOptions; +import com.amap.api.maps.model.PolylineOptions; +import com.amap.api.services.core.LatLonPoint; +import com.amap.api.services.route.WalkPath; +import com.amap.api.services.route.WalkStep; + +import android.content.Context; + +/** + * 步行路线图层类。在高德地图API里,如果要显示步行路线规划,可以用此类来创建步行路线图层。如不满足需求,也可以自己创建自定义的步行路线图层。 + * @since V2.1.0 + */ +public class WalkRouteOverlay extends RouteOverlay { + + private PolylineOptions mPolylineOptions; + + private BitmapDescriptor walkStationDescriptor= null; + + private WalkPath walkPath; + /** + * 通过此构造函数创建步行路线图层。 + * @param context 当前activity。 + * @param amap 地图对象。 + * @param path 步行路线规划的一个方案。详见搜索服务模块的路径查询包(com.amap.api.services.route)中的类 WalkStep。 + * @param start 起点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类LatLonPoint。 + * @param end 终点。详见搜索服务模块的核心基础包(com.amap.api.services.core)中的类LatLonPoint。 + * @since V2.1.0 + */ + public WalkRouteOverlay(Context context, AMap amap, WalkPath path, + LatLonPoint start, LatLonPoint end) { + super(context); + this.mAMap = amap; + this.walkPath = path; + startPoint = AMapServicesUtil.convertToLatLng(start); + endPoint = AMapServicesUtil.convertToLatLng(end); + } + /** + * 添加步行路线到地图中。 + * @since V2.1.0 + */ + public void addToMap() { + + initPolylineOptions(); + try { + List walkPaths = walkPath.getSteps(); + for (int i = 0; i < walkPaths.size(); i++) { + WalkStep walkStep = walkPaths.get(i); + LatLng latLng = AMapServicesUtil.convertToLatLng(walkStep + .getPolyline().get(0)); + + addWalkStationMarkers(walkStep, latLng); + addWalkPolyLines(walkStep); + + } + addStartAndEndMarker(); + + showPolyline(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + /** + * 检查这一步的最后一点和下一步的起始点之间是否存在空隙 + */ + private void checkDistanceToNextStep(WalkStep walkStep, + WalkStep walkStep1) { + LatLonPoint lastPoint = getLastWalkPoint(walkStep); + LatLonPoint nextFirstPoint = getFirstWalkPoint(walkStep1); + if (!(lastPoint.equals(nextFirstPoint))) { + addWalkPolyLine(lastPoint, nextFirstPoint); + } + } + + /** + * @param walkStep + * @return + */ + private LatLonPoint getLastWalkPoint(WalkStep walkStep) { + return walkStep.getPolyline().get(walkStep.getPolyline().size() - 1); + } + + /** + * @param walkStep + * @return + */ + private LatLonPoint getFirstWalkPoint(WalkStep walkStep) { + return walkStep.getPolyline().get(0); + } + + + private void addWalkPolyLine(LatLonPoint pointFrom, LatLonPoint pointTo) { + addWalkPolyLine(AMapServicesUtil.convertToLatLng(pointFrom), AMapServicesUtil.convertToLatLng(pointTo)); + } + + private void addWalkPolyLine(LatLng latLngFrom, LatLng latLngTo) { + mPolylineOptions.add(latLngFrom, latLngTo); + } + + /** + * @param walkStep + */ + private void addWalkPolyLines(WalkStep walkStep) { + mPolylineOptions.addAll(AMapServicesUtil.convertArrList(walkStep.getPolyline())); + } + + /** + * @param walkStep + * @param position + */ + private void addWalkStationMarkers(WalkStep walkStep, LatLng position) { + addStationMarker(new MarkerOptions() + .position(position) + .title("\u65B9\u5411:" + walkStep.getAction() + + "\n\u9053\u8DEF:" + walkStep.getRoad()) + .snippet(walkStep.getInstruction()).visible(nodeIconVisible) + .anchor(0.5f, 0.5f).icon(walkStationDescriptor)); + } + + /** + * 初始化线段属性 + */ + private void initPolylineOptions() { + + if(walkStationDescriptor == null) { + walkStationDescriptor = getWalkBitmapDescriptor(); + } + + mPolylineOptions = null; + + mPolylineOptions = new PolylineOptions(); + mPolylineOptions.color(getWalkColor()).width(getRouteWidth()); + } + + + private void showPolyline() { + addPolyLine(mPolylineOptions); + } +} diff --git a/app/src/main/java/com/example/mangowalking/utils/ChString.java b/app/src/main/java/com/example/mangowalking/utils/ChString.java new file mode 100644 index 0000000..430d3f7 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/utils/ChString.java @@ -0,0 +1,26 @@ +package com.example.mangowalking.utils; + +public class ChString { + public static final String Kilometer = "\u516c\u91cc";// "公里"; + public static final String Meter = "\u7c73";// "米"; + public static final String ByFoot = "\u6b65\u884c";// "步行"; + public static final String To = "\u53bb\u5f80";// "去往"; + public static final String Station = "\u8f66\u7ad9";// "车站"; + public static final String TargetPlace = "\u76ee\u7684\u5730";// "目的地"; + public static final String StartPlace = "\u51fa\u53d1\u5730";// "出发地"; + public static final String About = "\u5927\u7ea6";// "大约"; + public static final String Direction = "\u65b9\u5411";// "方向"; + + public static final String GetOn = "\u4e0a\u8f66";// "上车"; + public static final String GetOff = "\u4e0b\u8f66";// "下车"; + public static final String Zhan = "\u7ad9";// "站"; + + public static final String cross = "\u4ea4\u53c9\u8def\u53e3"; // 交叉路口 + public static final String type = "\u7c7b\u522b"; // 类别 + public static final String address = "\u5730\u5740"; // 地址 + public static final String PrevStep = "\u4e0a\u4e00\u6b65"; + public static final String NextStep = "\u4e0b\u4e00\u6b65"; + public static final String Gong = "\u516c\u4ea4"; + public static final String ByBus = "\u4e58\u8f66"; + public static final String Arrive = "\u5230\u8FBE";// 到达 +} diff --git a/app/src/main/java/com/example/mangowalking/utils/MapUtil.java b/app/src/main/java/com/example/mangowalking/utils/MapUtil.java new file mode 100644 index 0000000..7cdaa7c --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/utils/MapUtil.java @@ -0,0 +1,150 @@ +package com.example.mangowalking.utils; + +import com.amap.api.maps.model.LatLng; +import com.amap.api.services.core.LatLonPoint; +import com.example.mangowalking.R; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * 地图帮助类 + * @author llw + */ +public class MapUtil { + + /** + * 把LatLng对象转化为LatLonPoint对象 + */ + public static LatLonPoint convertToLatLonPoint(LatLng latLng) { + return new LatLonPoint(latLng.latitude, latLng.longitude); + } + + /** + * 把LatLonPoint对象转化为LatLon对象 + */ + public static LatLng convertToLatLng(LatLonPoint latLonPoint) { + return new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude()); + } + + public static String getFriendlyTime(int second) { + if (second > 3600) { + int hour = second / 3600; + int miniate = (second % 3600) / 60; + return hour + "小时" + miniate + "分钟"; + } + if (second >= 60) { + int miniate = second / 60; + return miniate + "分钟"; + } + return second + "秒"; + } + + public static String getFriendlyLength(int lenMeter) { + if (lenMeter > 10000) // 10 km + { + int dis = lenMeter / 1000; + return dis + ChString.Kilometer; + } + + if (lenMeter > 1000) { + float dis = (float) lenMeter / 1000; + DecimalFormat fnum = new DecimalFormat("##0.0"); + String dstr = fnum.format(dis); + return dstr + ChString.Kilometer; + } + + if (lenMeter > 100) { + int dis = lenMeter / 50 * 50; + return dis + ChString.Meter; + } + + int dis = lenMeter / 10 * 10; + if (dis == 0) { + dis = 10; + } + + return dis + ChString.Meter; + } + /** + * 把集合体的LatLonPoint转化为集合体的LatLng + */ + public static ArrayList convertArrList(List shapes) { + ArrayList lineShapes = new ArrayList(); + for (LatLonPoint point : shapes) { + LatLng latLngTemp = convertToLatLng(point); + lineShapes.add(latLngTemp); + } + return lineShapes; + } + + public static int getWalkActionID(String actionName) { + if (actionName == null || actionName.equals("")) { + return R.drawable.dir13; + } + if ("左转".equals(actionName)) { + return R.drawable.dir2; + } + if ("右转".equals(actionName)) { + return R.drawable.dir1; + } + if ("向左前方".equals(actionName) || "靠左".equals(actionName) || actionName.contains("向左前方")) { + return R.drawable.dir6; + } + if ("向右前方".equals(actionName) || "靠右".equals(actionName) || actionName.contains("向右前方")) { + return R.drawable.dir5; + } + if ("向左后方".equals(actionName)|| actionName.contains("向左后方")) { + return R.drawable.dir7; + } + if ("向右后方".equals(actionName)|| actionName.contains("向右后方")) { + return R.drawable.dir8; + } + if ("直行".equals(actionName)) { + return R.drawable.dir3; + } + if ("通过人行横道".equals(actionName)) { + return R.drawable.dir9; + } + if ("通过过街天桥".equals(actionName)) { + return R.drawable.dir11; + } + if ("通过地下通道".equals(actionName)) { + return R.drawable.dir10; + } + return R.drawable.dir13; + } + + public static int getDriveActionID(String actionName) { + if (actionName == null || actionName.isEmpty()) { + return R.drawable.dir3; + } + if ("左转".equals(actionName)) { + return R.drawable.dir2; + } + if ("右转".equals(actionName)) { + return R.drawable.dir1; + } + if ("向左前方行驶".equals(actionName) || "靠左".equals(actionName)) { + return R.drawable.dir6; + } + if ("向右前方行驶".equals(actionName) || "靠右".equals(actionName)) { + return R.drawable.dir5; + } + if ("向左后方行驶".equals(actionName) || "左转调头".equals(actionName)) { + return R.drawable.dir7; + } + if ("向右后方行驶".equals(actionName)) { + return R.drawable.dir8; + } + if ("直行".equals(actionName)) { + return R.drawable.dir3; + } + if ("减速行驶".equals(actionName)) { + return R.drawable.dir4; + } + return R.drawable.dir3; + } + +} diff --git a/app/src/main/java/com/example/mangowalking/utils/SchemeBusStep.java b/app/src/main/java/com/example/mangowalking/utils/SchemeBusStep.java new file mode 100644 index 0000000..4273ac2 --- /dev/null +++ b/app/src/main/java/com/example/mangowalking/utils/SchemeBusStep.java @@ -0,0 +1,80 @@ +package com.example.mangowalking.utils; + +import com.amap.api.services.route.BusStep; + +public class SchemeBusStep extends BusStep { + + private boolean isWalk = false; + private boolean isBus = false; + private boolean israilway = false; + private boolean istaxi = false; + private boolean isStart = false; + private boolean isEnd = false; + private boolean arrowExpend = false; + + public SchemeBusStep(BusStep step) { + if (step != null) { + this.setBusLine(step.getBusLine()); + this.setWalk(step.getWalk()); + this.setRailway(step.getRailway()); + this.setTaxi(step.getTaxi()); + } + } + + public boolean isArrowExpend() { + return arrowExpend; + } + + public void setArrowExpend(boolean arrowExpend) { + this.arrowExpend = arrowExpend; + } + + public boolean isWalk() { + return isWalk; + } + + public void setWalk(boolean isWalk) { + this.isWalk = isWalk; + } + + public boolean isBus() { + return isBus; + } + + public void setBus(boolean isBus) { + this.isBus = isBus; + } + + public boolean isStart() { + return isStart; + } + + public void setStart(boolean isStart) { + this.isStart = isStart; + } + + public boolean isEnd() { + return isEnd; + } + + public void setEnd(boolean isEnd) { + this.isEnd = isEnd; + } + + public boolean isRailway() { + return israilway; + } + + public boolean isTaxi() { + return istaxi; + } + + public void setRailway(boolean israilway) { + this.israilway = israilway; + } + + public void setTaxi(boolean istaxi) { + this.istaxi = istaxi; + } + +} diff --git a/app/src/main/res/drawable/amap_bus.png b/app/src/main/res/drawable/amap_bus.png new file mode 100644 index 0000000..519d81e Binary files /dev/null and b/app/src/main/res/drawable/amap_bus.png differ diff --git a/app/src/main/res/drawable/amap_car.png b/app/src/main/res/drawable/amap_car.png new file mode 100644 index 0000000..0d81d7d Binary files /dev/null and b/app/src/main/res/drawable/amap_car.png differ diff --git a/app/src/main/res/drawable/amap_end.png b/app/src/main/res/drawable/amap_end.png new file mode 100644 index 0000000..5503dc5 Binary files /dev/null and b/app/src/main/res/drawable/amap_end.png differ diff --git a/app/src/main/res/drawable/amap_man.png b/app/src/main/res/drawable/amap_man.png new file mode 100644 index 0000000..62a598b Binary files /dev/null and b/app/src/main/res/drawable/amap_man.png differ diff --git a/app/src/main/res/drawable/amap_ride.png b/app/src/main/res/drawable/amap_ride.png new file mode 100644 index 0000000..98d6ee1 Binary files /dev/null and b/app/src/main/res/drawable/amap_ride.png differ diff --git a/app/src/main/res/drawable/amap_start.png b/app/src/main/res/drawable/amap_start.png new file mode 100644 index 0000000..cd716c8 Binary files /dev/null and b/app/src/main/res/drawable/amap_start.png differ diff --git a/app/src/main/res/drawable/amap_through.png b/app/src/main/res/drawable/amap_through.png new file mode 100644 index 0000000..e636b44 Binary files /dev/null and b/app/src/main/res/drawable/amap_through.png differ diff --git a/app/src/main/res/drawable/amap_train.png b/app/src/main/res/drawable/amap_train.png new file mode 100644 index 0000000..0466c78 Binary files /dev/null and b/app/src/main/res/drawable/amap_train.png differ diff --git a/app/src/main/res/drawable/dir1.png b/app/src/main/res/drawable/dir1.png new file mode 100644 index 0000000..fec3f4e Binary files /dev/null and b/app/src/main/res/drawable/dir1.png differ diff --git a/app/src/main/res/drawable/dir10.png b/app/src/main/res/drawable/dir10.png new file mode 100644 index 0000000..34c2228 Binary files /dev/null and b/app/src/main/res/drawable/dir10.png differ diff --git a/app/src/main/res/drawable/dir11.png b/app/src/main/res/drawable/dir11.png new file mode 100644 index 0000000..df9e8dc Binary files /dev/null and b/app/src/main/res/drawable/dir11.png differ diff --git a/app/src/main/res/drawable/dir12.png b/app/src/main/res/drawable/dir12.png new file mode 100644 index 0000000..a4ba8c2 Binary files /dev/null and b/app/src/main/res/drawable/dir12.png differ diff --git a/app/src/main/res/drawable/dir13.png b/app/src/main/res/drawable/dir13.png new file mode 100644 index 0000000..39ad870 Binary files /dev/null and b/app/src/main/res/drawable/dir13.png differ diff --git a/app/src/main/res/drawable/dir14.png b/app/src/main/res/drawable/dir14.png new file mode 100644 index 0000000..a3c1632 Binary files /dev/null and b/app/src/main/res/drawable/dir14.png differ diff --git a/app/src/main/res/drawable/dir15.png b/app/src/main/res/drawable/dir15.png new file mode 100644 index 0000000..c99fb65 Binary files /dev/null and b/app/src/main/res/drawable/dir15.png differ diff --git a/app/src/main/res/drawable/dir16.png b/app/src/main/res/drawable/dir16.png new file mode 100644 index 0000000..86898c2 Binary files /dev/null and b/app/src/main/res/drawable/dir16.png differ diff --git a/app/src/main/res/drawable/dir2.png b/app/src/main/res/drawable/dir2.png new file mode 100644 index 0000000..750b6fb Binary files /dev/null and b/app/src/main/res/drawable/dir2.png differ diff --git a/app/src/main/res/drawable/dir3.png b/app/src/main/res/drawable/dir3.png new file mode 100644 index 0000000..8cbe886 Binary files /dev/null and b/app/src/main/res/drawable/dir3.png differ diff --git a/app/src/main/res/drawable/dir4.png b/app/src/main/res/drawable/dir4.png new file mode 100644 index 0000000..cfe7d6c Binary files /dev/null and b/app/src/main/res/drawable/dir4.png differ diff --git a/app/src/main/res/drawable/dir5.png b/app/src/main/res/drawable/dir5.png new file mode 100644 index 0000000..e465d5e Binary files /dev/null and b/app/src/main/res/drawable/dir5.png differ diff --git a/app/src/main/res/drawable/dir6.png b/app/src/main/res/drawable/dir6.png new file mode 100644 index 0000000..0d2f3c0 Binary files /dev/null and b/app/src/main/res/drawable/dir6.png differ diff --git a/app/src/main/res/drawable/dir7.png b/app/src/main/res/drawable/dir7.png new file mode 100644 index 0000000..e53c9ff Binary files /dev/null and b/app/src/main/res/drawable/dir7.png differ diff --git a/app/src/main/res/drawable/dir8.png b/app/src/main/res/drawable/dir8.png new file mode 100644 index 0000000..edfdbe1 Binary files /dev/null and b/app/src/main/res/drawable/dir8.png differ diff --git a/app/src/main/res/drawable/dir9.png b/app/src/main/res/drawable/dir9.png new file mode 100644 index 0000000..a762d0b Binary files /dev/null and b/app/src/main/res/drawable/dir9.png differ diff --git a/app/src/main/res/drawable/dir_end.png b/app/src/main/res/drawable/dir_end.png new file mode 100644 index 0000000..f3cb78a Binary files /dev/null and b/app/src/main/res/drawable/dir_end.png differ diff --git a/app/src/main/res/drawable/dir_start.png b/app/src/main/res/drawable/dir_start.png new file mode 100644 index 0000000..4d2a797 Binary files /dev/null and b/app/src/main/res/drawable/dir_start.png differ diff --git a/app/src/main/res/drawable/dir_station.png b/app/src/main/res/drawable/dir_station.png new file mode 100644 index 0000000..2ed6222 Binary files /dev/null and b/app/src/main/res/drawable/dir_station.png differ diff --git a/app/src/main/res/drawable/down.png b/app/src/main/res/drawable/down.png new file mode 100644 index 0000000..6768a17 Binary files /dev/null and b/app/src/main/res/drawable/down.png differ diff --git a/app/src/main/res/drawable/end.png b/app/src/main/res/drawable/end.png new file mode 100644 index 0000000..5503dc5 Binary files /dev/null and b/app/src/main/res/drawable/end.png differ diff --git a/app/src/main/res/drawable/et_bg.xml b/app/src/main/res/drawable/et_bg.xml new file mode 100644 index 0000000..b11a1fe --- /dev/null +++ b/app/src/main/res/drawable/et_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/gps_point.png b/app/src/main/res/drawable/gps_point.png new file mode 100644 index 0000000..ec4332d Binary files /dev/null and b/app/src/main/res/drawable/gps_point.png differ diff --git a/app/src/main/res/drawable/ic_black_back.xml b/app/src/main/res/drawable/ic_black_back.xml new file mode 100644 index 0000000..5168fa7 --- /dev/null +++ b/app/src/main/res/drawable/ic_black_back.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_blue_open.xml b/app/src/main/res/drawable/ic_blue_open.xml new file mode 100644 index 0000000..bfd3e96 --- /dev/null +++ b/app/src/main/res/drawable/ic_blue_open.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml new file mode 100644 index 0000000..a6af65f --- /dev/null +++ b/app/src/main/res/drawable/ic_clear.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_favorite_red.xml b/app/src/main/res/drawable/ic_favorite_red.xml new file mode 100644 index 0000000..6ec39d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite_red.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_route.xml b/app/src/main/res/drawable/ic_route.xml new file mode 100644 index 0000000..ff3035e --- /dev/null +++ b/app/src/main/res/drawable/ic_route.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/start.png b/app/src/main/res/drawable/start.png new file mode 100644 index 0000000..cd716c8 Binary files /dev/null and b/app/src/main/res/drawable/start.png differ diff --git a/app/src/main/res/drawable/up.png b/app/src/main/res/drawable/up.png new file mode 100644 index 0000000..f17662e Binary files /dev/null and b/app/src/main/res/drawable/up.png differ diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..6809c32 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_route.xml b/app/src/main/res/layout/activity_route.xml new file mode 100644 index 0000000..65a8d22 --- /dev/null +++ b/app/src/main/res/layout/activity_route.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_route_detail.xml b/app/src/main/res/layout/activity_route_detail.xml new file mode 100644 index 0000000..5e2a967 --- /dev/null +++ b/app/src/main/res/layout/activity_route_detail.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/custom_info_contents.xml b/app/src/main/res/layout/custom_info_contents.xml new file mode 100644 index 0000000..b0f6f79 --- /dev/null +++ b/app/src/main/res/layout/custom_info_contents.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/custom_info_window.xml b/app/src/main/res/layout/custom_info_window.xml new file mode 100644 index 0000000..6762b51 --- /dev/null +++ b/app/src/main/res/layout/custom_info_window.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_segment.xml b/app/src/main/res/layout/item_segment.xml new file mode 100644 index 0000000..0eb0ccb --- /dev/null +++ b/app/src/main/res/layout/item_segment.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_segment_ex.xml b/app/src/main/res/layout/item_segment_ex.xml new file mode 100644 index 0000000..9030560 --- /dev/null +++ b/app/src/main/res/layout/item_segment_ex.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/custom_info_bubble.9.png b/app/src/main/res/mipmap-xhdpi/custom_info_bubble.9.png new file mode 100644 index 0000000..b1cfec3 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/custom_info_bubble.9.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_my.png b/app/src/main/res/mipmap-xhdpi/ic_my.png new file mode 100644 index 0000000..f8c5b0e Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_my.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_yuan.png b/app/src/main/res/mipmap-xhdpi/ic_yuan.png new file mode 100644 index 0000000..41636ef Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_yuan.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 0000000..7429e36 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..c8524cd --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,5 @@ + + + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..36cf53f --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + ManGoWalking + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..9dd6e6d --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,9 @@ + + + + +