我的意思并不是说在home界面就请求定位权限,而是点击搜索按钮之后弹出使用该功能需请求定位权限。要不然的话,这个软件一点开如果拒绝了定位权限连home界面都进不去,而我进入map界面的请求定位权限流程已经完善的很完美了,所以就是说我如果是在map中搜索就不需要再请求一次了,只是home中搜索才需要单独请求定位权限。只不过请求定位权限的流程我希望与进入map的流程一模一样,而这个流程是在MainActivity中写的。刚刚各部分代码已经提供给你了,我还没有做出任何的修改,包括你说要添加的那几项。现在我将这些代码再次给你提供一遍,以免你遗忘,还是一样,我不希望改变已有的内容,仅做添加即可。AndroidManifest.xml代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.bus">
<!-- 获取精确位置(GPS + 网络) -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 获取粗略位置 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 请求后台定位权限 -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- 访问网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 读取设备状态(用于生成设备标识) -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 使用 GPS -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Bus">
<!-- 高德地图 API Key -->
<meta-data
android:name="com.amap.api.v2.apikey"
android:value="a487b8aa9be4ecdcb101a04a9eecff8a" />
<activity
android:name=".AboutActivity"
android:exported="false"
android:parentActivityName=".MainActivity" />
<activity
android:name=".SurveyActivity"
android:label="用户调研"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.java代码如下:package com.example.bus;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.example.bus.databinding.ActivityMainBinding;
import com.google.android.material.bottomnavigation.BottomNavigationView;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
private static final String SAVED_NAV_ID = "saved_nav_id";
private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ✅ 添加日志:查看冷启动时权限是否已被系统撤销
Log.d("PERMISSION_DEBUG", "onCreate: FINE_LOCATION_GRANTED = " +
(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED));
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
BottomNavigationView navView = findViewById(R.id.nav_view);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home,
R.id.navigation_map,
R.id.navigation_settings)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
// ✅ 更新底部导航逻辑
navView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.navigation_map) {
ensureFineLocationPermission(() -> {
if (navController.getCurrentDestination().getId() != R.id.navigation_map) {
navController.navigate(R.id.navigation_map);
}
});
return true;
}
navController.navigate(itemId);
return true;
});
// 恢复上次选中的底部菜单项
if (savedInstanceState != null) {
int savedId = savedInstanceState.getInt(SAVED_NAV_ID, R.id.navigation_home);
navView.setSelectedItemId(savedId);
}
}
// ✅ 新增:通用权限保障方法(所有进地图前都必须走这里)
public void ensureFineLocationPermission(Runnable onGranted) {
boolean hasFine = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
if (hasFine) {
onGranted.run(); // 权限存在,直接执行
} else {
requestFineLocationPermission(onGranted); // 否则发起请求
}
}
/**
* 请求精确定位权限(带解释说明)
*/
private void requestFineLocationPermission(Runnable onGranted) {
boolean hasFine = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
if (hasFine) {
onGranted.run();
return;
}
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
new AlertDialog.Builder(this)
.setTitle("需要精确定位权限")
.setMessage("为了准确查找您附近的公交站点和车辆位置,本应用需要获取您的精确位置。\n否则将无法使用地图相关功能。\n\n请务必选择【允许】或【仅限这一次】。")
.setPositiveButton("去允许", (d, w) -> requestFineLocation())
.setNegativeButton("取消", null)
.show();
} else {
requestFineLocation();
}
}
private void requestFineLocation() {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
LOCATION_PERMISSION_REQUEST_CODE);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
boolean hasFine = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
if (hasFine) {
Toast.makeText(this, "已获得精确定位权限", Toast.LENGTH_SHORT).show();
navigateToMapIfNeeded();
} else {
boolean hasCoarse = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED;
if (hasCoarse) {
new AlertDialog.Builder(this)
.setTitle("需要精确位置")
.setMessage("检测到您使用的是【大致位置】,这会导致地图功能无法正常使用。\n\n" +
"请在设置中将定位权限修改为【精确位置】。")
.setPositiveButton("去设置", (d, w) -> openAppSettings())
.setNegativeButton("取消", null)
.show();
} else {
Toast.makeText(this, "定位权限未授予,无法使用地图功能", Toast.LENGTH_LONG).show();
}
}
}
}
private void navigateToMapIfNeeded() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_main);
if (navController.getCurrentDestination().getId() != R.id.navigation_map) {
navController.navigate(R.id.navigation_map);
}
}
private void openAppSettings() {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
BottomNavigationView navView = findViewById(R.id.nav_view);
outState.putInt(SAVED_NAV_ID, navView.getSelectedItemId());
}
}
activity_main.xml代码如下:<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="@color/black"> <!-- 替代原来的 paddingTop -->
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<fragment
android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="@id/nav_view"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_map.xml代码如下:<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.map.MapFragment"
android:fitsSystemWindows="true"
android:background="@color/surface_background">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_top_offset"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.015" />
<!-- 🔹 起点输入框 -->
<EditText
android:id="@+id/map_input1"
android:layout_width="0dp"
android:layout_height="48dp"
android:hint="请输入起点"
android:textColorHint="#777777"
android:textColor="@color/black"
android:background="@drawable/rounded_edittext"
android:padding="12dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="32dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/guideline_search"
app:layout_constraintTop_toTopOf="@id/guideline_top_offset" />
<!-- 🔹 终点输入框 -->
<EditText
android:id="@+id/map_input2"
android:layout_width="0dp"
android:layout_height="48dp"
android:hint="请输入终点"
android:textColorHint="#777777"
android:textColor="@color/black"
android:background="@drawable/rounded_edittext"
android:padding="12dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/guideline_search"
app:layout_constraintTop_toBottomOf="@id/map_input1" />
<!-- ✅ 分割线:75% 处(原样保留) -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.75" />
<!-- 🔍 搜索按钮:纵向拉高,覆盖两个输入框 -->
<Button
android:id="@+id/map_search"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="搜索"
android:textSize="16sp"
android:gravity="center"
app:layout_constraintStart_toStartOf="@id/guideline_search"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/map_input1"
app:layout_constraintBottom_toBottomOf="@id/map_input2"
android:layout_marginEnd="16dp" />
<!-- 🗺️ 地图视图:从终点输入框下方开始,延伸到底部 -->
<com.amap.api.maps.MapView
android:id="@+id/map_view"
android:text="Bus-1.0"
android:textColor="#777777"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/map_input2"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="4dp"
android:layout_marginBottom="0dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
fragment_home.xml代码如下:<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.home.HomeFragment"
android:fitsSystemWindows="true"
android:background="@color/surface_background">
<!-- 🔍 输入框 -->
<EditText
android:id="@+id/home_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="请输入需要查询的公交线路或站点"
android:textColorHint="#777777"
android:textColor="@color/black"
android:background="@drawable/rounded_edittext"
android:minHeight="48dp"
android:textSize="16sp"
android:padding="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/home_search"
app:layout_constraintHorizontal_chainStyle="packed"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="88dp" />
<!-- 🔎 搜索按钮 -->
<Button
android:id="@+id/home_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="搜索"
app:layout_constraintTop_toTopOf="@id/home_input"
app:layout_constraintBottom_toBottomOf="@id/home_input"
app:layout_constraintStart_toEndOf="@id/home_input"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="16dp" />
<!-- 🚌 图片:居中偏上 -->
<ImageView
android:id="@+id/image_bus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/bus"
app:layout_constraintTop_toBottomOf="@id/home_input"
app:layout_constraintBottom_toTopOf="@+id/text_home"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="144dp"
android:layout_marginBottom="144dp"/>
<!-- ℹ️ 底部说明文字 -->
<TextView
android:id="@+id/text_home"
android:textColor="#777777"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="11sp"
app:layout_constraintTop_toBottomOf="@id/image_bus"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MapFragment.java代码如下:package com.example.bus.ui.map;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.amap.api.maps.MapView;
import com.amap.api.maps.AMap;
import com.amap.api.maps.model.LatLng;
//import com.example.bus.R;
//import com.example.bus.databinding.FragmentMapBinding;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.UiSettings;
import com.example.bus.R;
import com.example.bus.databinding.FragmentMapBinding;
// MapView 已在 XML 中声明,无需额外 import(会自动识别)
public class MapFragment extends Fragment {
private FragmentMapBinding binding;
private MapView mapView; // 高德地图视图
private AMap aMap; // 地图控制器
private boolean isFirstLocationSet = false; // 防止反复跳转
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// 初始化 ViewModel 和 ViewBinding
binding = FragmentMapBinding.inflate(inflater, container, false);
View root = binding.getRoot();
//绑定 MapView
mapView = root.findViewById(R.id.map_view);
mapView.onCreate(savedInstanceState); // 必须调用生命周期方法
// 初始化地图
initMap();
return root;
}
/**
* 初始化地图
*/
private void initMap() {
if (aMap == null) {
aMap = mapView.getMap();
UiSettings uiSettings = aMap.getUiSettings();
uiSettings.setZoomControlsEnabled(true); // 显示缩放按钮
uiSettings.setCompassEnabled(true); // 显示指南针
uiSettings.setMyLocationButtonEnabled(false); // 我们自己控制定位行为
}
}
@Override
public void onResume() {
super.onResume(); //每次恢复可见时都检查权限状态
mapView.onResume();
// ✅ 直接开启定位图层(信任 MainActivity 的判断)
if (aMap != null) {
aMap.setMyLocationEnabled(true);
// 只第一次进入时移动相机
if (!isFirstLocationSet) {
LatLng defaultLoc = new LatLng(39.909186, 116.397411);
aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(defaultLoc, 12f));
isFirstLocationSet = true;
}
}
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (mapView != null) {
mapView.onDestroy();
}
binding = null;
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}
HomeFragment.java代码如下:
package com.example.bus.ui.home;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import com.example.bus.databinding.FragmentHomeBinding;
public class HomeFragment extends Fragment {
private FragmentHomeBinding binding;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
com.example.bus.ui.home.HomeViewModel homeViewModel =
new ViewModelProvider(this).get(com.example.bus.ui.home.HomeViewModel.class);
binding = FragmentHomeBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.textHome;
homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
return root;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
build.gradle.kts代码如下:
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.example.bus"
compileSdk = 36
defaultConfig {
applicationId = "com.example.bus"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation(files("libs/AMap3DMap_10.1.500_AMapNavi_10.1.500_AMapSearch_9.7.4_AMapLocation_6.5.0_20250814.aar"))
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
implementation(libs.lifecycle.livedata.ktx)
implementation(libs.lifecycle.viewmodel.ktx)
implementation(libs.navigation.fragment)
implementation(libs.navigation.ui)
implementation(libs.play.services.maps)
implementation(libs.recyclerview)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}
现在就是说我的各部分应该都准备就绪了吧,接下来我的想法是home_fragment点击搜索后先跳转到界面1,显示搜索的信息,包含公交线路、站点等,然后做一个按钮显示“到这去”,点击按钮后就跳转到界面2显示从“我的位置”到达“搜索位置”的公交方案。map_fragment点击搜索后直接跳转到界面2显示从起点到终点的公交方案。只不过就是在home界面点击搜索之后弹出那个请求定位权限的框
最新发布