AlertDialog的菜单以及MapView使用

本文介绍了一个基于Google Maps API的应用实例,展示了如何通过Android平台实现地图显示、卫星视图、交通信息及街景等功能,并提供了完整的代码示例。
这个例子主要可以学习到:
1.使用AlertDialog显示菜单item以方便选择
2.利用mapView显示goole地图

下面直接把代码贴上:


package com.sun.GoogleMap1;

import android.os.Bundle;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;

public class GoogleMap1 extends MapActivity {
private final String TAG = "MapPrac";
private MapView mapView = null;
private MapController mc;
private int chooseItem = 0;
// Menu items
final private int menuMode = Menu.FIRST;
final private int menuExit = Menu.FIRST + 1;
final private int menuCommandList = Menu.FIRST + 2;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

mapView = (MapView) findViewById(R.id.map);
mc = mapView.getController();

mapView.setTraffic(true);
mapView.setSatellite(true);

// GeoPoint gp = new GeoPoint((int)(39.269259 * 1000000),
// (int)(115.255762 * 1000000));
// yixian

GeoPoint gp = new GeoPoint((int) (39.95 * 1000000),
(int) (116.37 * 1000000));// beijing

// mc.animateTo(gp);
// mc.setZoom(12);
mc.setCenter(gp);
// to display zoom control in MapView
mapView.setBuiltInZoomControls(true);

}

@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub

Log.i(TAG, "enter onKeyDown");
return super.onKeyDown(keyCode, event);

}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, menuMode, 0, "Map Mode");
menu.add(0, menuCommandList, 1, "Command List");
menu.add(0, menuExit, 2, "Exit");

return super.onCreateOptionsMenu(menu);

}

@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case menuMode:
Dialog dMode = new AlertDialog.Builder(this)
.setIcon(R.drawable.alert_dialog_icon)
.setTitle(R.string.alert_dialog_single_choice)
.setSingleChoiceItems(R.array.select_dialog_items,
chooseItem, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {

chooseItem = whichButton;
}
})
.setPositiveButton(R.string.alert_dialog_ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
switch (chooseItem) {
case 0:
mapView.setSatellite(true);
break;
case 1:
mapView.setSatellite(true);
break;
case 2:
mapView.setTraffic(true);
break;
case 3:
mapView.setStreetView(true);
break;
default:
break; }
}
})
.setNegativeButton(R.string.alert_dialog_cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int whichButton) {
}
}).create();
dMode.show();
break;
case menuCommandList:
// create the dialog
Dialog d = new AlertDialog.Builder(this)
.setTitle(R.string.select_dialog)
.setItems(R.array.select_dialog_items,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
/* User clicked so do some stuff */
String[] items = getResources()
.getStringArray(
R.array.select_dialog_items);
}
}).create();
// show the dialog
d.show();

break;

case menuExit:
finish();
break;
default:
break;
}
return super.onMenuItemSelected(featureId, item);
}

}



这个String.xml也要贴下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, GoogleMap1!</string>
<string name="app_name">GoogleMap1</string>
<string name="select_dialog">select_dialog</string>
<string name="alert_dialog_ok">ok</string>
<string name="alert_dialog_cancel">Cancel</string>
<string name="alert_dialog_single_choice">Map Mode List</string>

<string-array name="select_dialog_items">
<item>Map</item>
<item>Satellite</item>
<item>Traffic</item>
<item>Street View</item>
</string-array>
</resources>


因为里面用到了string-array这个属性,我们可以将这个数组列表显示在比如:
AlertDialog当中

最后贴下main.xml,这个就是一个简单的MapView,我们只要申请个API-key就可以了:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.google.android.maps.MapView
android:id="@+id/map"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="0y_0zdWEE7CcFIM2QPL756MOF-FT981aiCzSrhA"
/>
</LinearLayout>



最后还是加下AndroidMenifest.xml吧

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sun.GoogleMap1"
android:versionCode="1"
android:versionName="1.0">


<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".GoogleMap1"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<uses-library android:name="com.google.android.maps" />
</application>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-sdk android:minSdkVersion="3" />
</manifest>
说的有点快,我有点乱。当然了,我需要告诉你我一开始的代码是这样的:HomeFragment代码如下:package com.example.bus.ui.home; import android.content.Intent; 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.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import com.amap.api.services.help.Inputtips; import com.amap.api.services.help.Tip; import android.widget.ArrayAdapter; import android.text.TextWatcher; import android.text.Editable; import com.example.bus.MainActivity; import com.example.bus.SearchResultActivity; import com.example.bus.databinding.FragmentHomeBinding; import com.amap.api.services.core.AMapException; import java.util.List; public class HomeFragment extends Fragment { private FragmentHomeBinding binding; private Inputtips inputTips; public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class); binding = FragmentHomeBinding.inflate(inflater, container, false); View root = binding.getRoot(); final TextView textView = binding.textHome; homeViewModel.getText().observe(getViewLifecycleOwner(), textView::setText); setupSearchSuggestion(); binding.homeSearch.setOnClickListener(v -> { String keyword = binding.homeInput.getText().toString().trim(); if (keyword.isEmpty()) { Toast.makeText(requireContext(), "请输入关键词", Toast.LENGTH_SHORT).show(); return; } // ✅ 使用 MainActivity 的统一权限流程 MainActivity activity = (MainActivity) requireActivity(); activity.ensureFineLocationPermission(() -> { // ✅ 权限通过后再跳转 Intent intent = new Intent(requireContext(), SearchResultActivity.class); intent.putExtra("keyword", keyword); startActivity(intent); }); }); return root; } private void setupSearchSuggestion() { try { inputTips = new Inputtips(requireContext(), new Inputtips.InputtipsListener() { @Override public void onGetInputtips(List<Tip> tipList, int rCode) { if (rCode == 1000 && !tipList.isEmpty()) { String[] arr = new String[tipList.size()]; for (int i = 0; i < tipList.size(); i++) { arr[i] = tipList.get(i).getName(); } ArrayAdapter<String> adapter = new ArrayAdapter<>( requireContext(), android.R.layout.simple_dropdown_item_1line, arr ); binding.homeInput.setAdapter(adapter); } else { // 可选:清空建议列表 binding.homeInput.setAdapter(null); handleSearchError(rCode); } } }); } catch (AMapException e) { throw new RuntimeException(e); } binding.homeInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.length() > 0) { try { inputTips.requestInputtips(s.toString(), "全国"); // 这个方法在你的版本里确实 throws AMapException } catch (AMapException e) { e.printStackTrace(); Toast.makeText(requireContext(), "输入提示请求失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } else { binding.homeInput.setAdapter(null); } } @Override public void afterTextChanged(Editable s) {} }); } private void handleSearchError(int rCode) { String msg; switch (rCode) { case 12: msg = "API Key 错误,请检查 AndroidManifest.xml"; break; case 27: msg = "网络连接失败"; break; case 30: msg = "SHA1 或包名未正确配置"; break; case 33: msg = "请求过于频繁,请稍后再试"; break; default: msg = "搜索失败,错误码: " + rCode; break; } Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void onDestroyView() { super.onDestroyView(); inputTips = null; binding = null; } } MapFragment代码如下:package com.example.bus.ui.map; import android.Manifest; import android.content.Intent; 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.services.core.AMapException; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.UiSettings; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import com.example.bus.R; import com.example.bus.RoutePlanActivity; import com.example.bus.databinding.FragmentMapBinding; import com.amap.api.services.core.PoiItem; import com.amap.api.services.core.LatLonPoint; // 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(); if (binding.mapSearch != null) { binding.mapSearch.setOnClickListener(v -> { String keyword = binding.mapInput2.getText().toString().trim(); if (keyword.isEmpty()) { Toast.makeText(requireContext(), "请输入终点", Toast.LENGTH_SHORT).show(); return; } PoiSearch.Query query = new PoiSearch.Query(keyword, "", "全国"); query.setPageSize(10); query.setPageNum(0); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { requireActivity().runOnUiThread(() -> { if (rCode == 1000 && result != null && !result.getPois().isEmpty()) { PoiItem item = result.getPois().get(0); LatLonPoint point = item.getLatLonPoint(); // ✅ 使用地图当前中心作为起点 LatLng center = aMap.getCameraPosition().target; Intent intent = new Intent(requireContext(), RoutePlanActivity.class); intent.putExtra(RoutePlanActivity.EXTRA_SOURCE, RoutePlanActivity.SOURCE_FROM_MAP_DIRECT); intent.putExtra("start_lat", center.latitude); intent.putExtra("start_lng", center.longitude); intent.putExtra("target_lat", point.getLatitude()); intent.putExtra("target_lng", point.getLongitude()); intent.putExtra("target_title", item.getTitle()); startActivity(intent); } else { handlePoiSearchError(rCode); } }); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(requireContext(), "搜索失败:" + e.getMessage(), Toast.LENGTH_LONG).show(); } }); } return root; } private void handlePoiSearchError(int rCode) { String errorMsg; switch (rCode) { case 1101: errorMsg = "网络连接异常"; break; case 1102: errorMsg = "网络响应超时"; break; case 1103: errorMsg = "解析异常"; break; case 1104: errorMsg = "请求参数非法"; break; case 1200: errorMsg = "API Key 不存在"; break; case 1202: errorMsg = "签名验证失败(SHA1/包名不匹配)"; break; case 1206: errorMsg = "API Key 权限不足(未开通 POI 搜索服务)"; break; default: errorMsg = "未知错误: " + rCode; break; } Toast.makeText(requireContext(), "搜索失败: " + errorMsg, Toast.LENGTH_LONG).show(); } /** * 初始化地图 */ 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); } } MainActivity代码如下: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()); } } SearchResultActivity代码如下:package com.example.bus; import android.content.Intent; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.amap.api.maps.AMap; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.MapView; import com.amap.api.maps.UiSettings; 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.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import java.util.ArrayList; import java.util.List; public class SearchResultActivity extends AppCompatActivity implements PoiSearch.OnPoiSearchListener { private EditText searchInput; private Button searchBtn, goToBtn; private RecyclerView resultListView; private List<PoiItem> poiList = new ArrayList<>(); private ResultAdapter adapter; private PoiSearch poiSearch; // 地图相关 private MapView mapView; private AMap aMap; private Marker selectedMarker; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_result); // 设置 ActionBar 返回按钮 if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("搜索地点"); } initViews(); setupMap(savedInstanceState); // 初始化地图 setupSearch(); // 初始化搜索 } private void initViews() { searchInput = findViewById(R.id.search_input); searchBtn = findViewById(R.id.search_btn); resultListView = findViewById(R.id.result_list); goToBtn = findViewById(R.id.btn_go_to); goToBtn.setEnabled(false); } private void setupMap(Bundle savedInstanceState) { mapView = findViewById(R.id.map_view); mapView.onCreate(savedInstanceState); if (aMap == null) { aMap = mapView.getMap(); UiSettings uiSettings = aMap.getUiSettings(); uiSettings.setZoomControlsEnabled(true); uiSettings.setCompassEnabled(true); uiSettings.setScrollGesturesEnabled(true); aMap.setOnMapClickListener(latLng -> { if (selectedMarker != null) selectedMarker.remove(); selectedMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("选中位置")); goToBtn.setEnabled(true); }); aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.909186, 116.397411), 10f)); } } private void setupSearch() { String keyword = getIntent().getStringExtra("keyword"); if (keyword != null && !keyword.isEmpty()) { searchInput.setText(keyword); performSearch(keyword); } searchBtn.setOnClickListener(v -> { String text = searchInput.getText().toString().trim(); if (!text.isEmpty()) { performSearch(text); } else { Toast.makeText(this, "请输入关键词", Toast.LENGTH_SHORT).show(); } }); goToBtn.setOnClickListener(v -> { if (selectedMarker != null) { LatLng pos = selectedMarker.getPosition(); navigateToRoute(pos.latitude, pos.longitude, "地图选点"); } else if (!poiList.isEmpty()) { PoiItem item = poiList.get(0); LatLonPoint p = item.getLatLonPoint(); navigateToRoute(p.getLatitude(), p.getLongitude(), item.getTitle()); } else { Toast.makeText(this, "暂无目标位置", Toast.LENGTH_SHORT).show(); } }); } private void navigateToRoute(double lat, double lng, String title) { Intent intent = new Intent(this, RoutePlanActivity.class); intent.putExtra(RoutePlanActivity.EXTRA_SOURCE, RoutePlanActivity.SOURCE_FROM_HOME_SEARCH); intent.putExtra("target_lat", lat); intent.putExtra("target_lng", lng); intent.putExtra("target_title", title); startActivity(intent); } private void performSearch(String keyword) { PoiSearch.Query query; if (isLikelyBusStop(keyword)) { // 如果像公交站名,加过滤 query = new PoiSearch.Query(keyword, "15", "全国"); // 公共交通类 } else { // 否则不限类别,确保能搜到地标 query = new PoiSearch.Query(keyword, "", "全国"); } query.setPageSize(20); query.setPageNum(0); try { poiSearch = new PoiSearch(this, query); poiSearch.setOnPoiSearchListener(this); poiSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "搜索启动失败", Toast.LENGTH_SHORT).show(); } } private boolean isLikelyBusStop(String keyword) { return keyword.contains("公交") || keyword.contains("地铁") || keyword.contains("BRT") || keyword.endsWith("站") || keyword.endsWith("场"); } @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { poiList.clear(); poiList.addAll(result.getPois()); if (adapter == null) { adapter = new ResultAdapter(poiList, item -> { if (selectedMarker != null) selectedMarker.remove(); LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); selectedMarker = aMap.addMarker(new MarkerOptions().position(latLng).title(item.getTitle())); aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); goToBtn.setEnabled(true); }); resultListView.setLayoutManager(new LinearLayoutManager(this)); resultListView.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); } goToBtn.setEnabled(!poiList.isEmpty()); } else { String msg = rCode == 17 ? "高德Key校验失败" : "搜索失败: 错误码=" + rCode; Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} @Override protected void onResume() { super.onResume(); mapView.onResume(); } @Override protected void onPause() { super.onPause(); mapView.onPause(); } @Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); if (poiSearch != null) poiSearch.setOnPoiSearchListener(null); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } } 以上这些是我原本的代码,是不报错的。刚刚的修改我希望是最小化修改,不变动代码框架也不乱添加杂七杂八的东西,请求位置的话MapFragment都能够成功请求,我也只是希望在HomeFragment中点击搜索按钮之后按照同样的方法请求位置,只不过请求后不是跳转到MapFragment去,而是跳转到SearchResultActivity去,让蓝点在SearchResultActivity也能够显示出来。也许你刚刚修改的东西会不会有点多呢
11-15
我只需要你做最小量的修改实现目标,刚刚的RoutePlanActivity代码如下:package com.example.bus; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.amap.api.maps.AMap; import com.amap.api.maps.MapView; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.PolylineOptions; import com.amap.api.services.core.AMapException; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.route.*; import com.amap.api.services.route.BusRouteResult; import com.amap.api.services.route.RouteSearch; import java.util.ArrayList; import java.util.List; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.model.LatLngBounds; import com.amap.api.services.route.BusPath; public class RoutePlanActivity extends AppCompatActivity implements RouteSearch.OnRouteSearchListener { // ✅ 新增:定义来源类型常量 public static final String EXTRA_SOURCE = "extra_source"; public static final int SOURCE_FROM_HOME_SEARCH = 1001; public static final int SOURCE_FROM_MAP_DIRECT = 1002; private MapView mapView; private AMap aMap; private RouteSearch routeSearch; private RecyclerView recyclerSteps; private boolean isSearching = false; // ✅ 新增:记录来源类型 private int sourceType = -1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_route_plan); // ✅ 新增:获取来源类型 sourceType = getIntent().getIntExtra(EXTRA_SOURCE, -1); mapView = findViewById(R.id.map_view); recyclerSteps = findViewById(R.id.recycler_steps); mapView.onCreate(savedInstanceState); if (aMap == null) { aMap = mapView.getMap(); } try { routeSearch = new RouteSearch(this); } catch (AMapException e) { throw new RuntimeException(e); } routeSearch.setRouteSearchListener(this); parseIntentAndStartSearch(); } /** * 解析 Intent 并发起公交路线搜索 */ private void parseIntentAndStartSearch() { if (isSearching) return; double targetLat = getIntent().getDoubleExtra("target_lat", 0); double targetLng = getIntent().getDoubleExtra("target_lng", 0); if (targetLat == 0 || targetLng == 0) { Toast.makeText(this, "目标位置无效", Toast.LENGTH_SHORT).show(); finish(); return; } // 起点:模拟当前位置 LatLonPoint start = new LatLonPoint(39.909186, 116.397411); // 北京 LatLonPoint target = new LatLonPoint(targetLat, targetLng); RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(start, target); RouteSearch.BusRouteQuery query = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BUS_DEFAULT, "全国", 0); query.setCityd("全国"); // 支持跨城 isSearching = true; routeSearch.calculateBusRouteAsyn(query); // ✅ 异步调用,无需 try-catch } @Override public void onBusRouteSearched(BusRouteResult result, int rCode) { isSearching = false; if (rCode != 1000 || result == null || result.getPaths().isEmpty()) { Toast.makeText(this, "未找到公交路线", Toast.LENGTH_SHORT).show(); finish(); return; } BusPath bestPath = result.getPaths().get(0); // ✅ 手动绘制路线(替代 BusRouteOverlay) drawRouteOnMap(bestPath); // 显示换乘步骤 StepAdapter adapter = new StepAdapter(bestPath.getSteps()); recyclerSteps.setLayoutManager(new LinearLayoutManager(this)); recyclerSteps.setAdapter(adapter); // 显示总时间和票价 String info = String.format("耗时:%d分钟 | 票价:¥%.2f", bestPath.getDuration() / 60, bestPath.getCost()); Toast.makeText(this, info, Toast.LENGTH_LONG).show(); } /** * 手动绘制公交路线 Polyline */ private void drawRouteOnMap(BusPath path) { aMap.clear(); List<LatLng> allPoints = new ArrayList<>(); for (BusStep step : path.getSteps()) { boolean added = false; // 1. 【公交】尝试通过反射获取 getPolyline() if (step.getBusLine() != null) { try { java.lang.reflect.Method method = step.getBusLine().getClass().getMethod("getPolyline"); Object result = method.invoke(step.getBusLine()); if (result instanceof java.util.List) { for (Object obj : (java.util.List<?>) result) { if (obj instanceof LatLonPoint) { LatLonPoint point = (LatLonPoint) obj; allPoints.add(new LatLng(point.getLatitude(), point.getLongitude())); added = true; } } } } catch (Exception e) { Log.d("RoutePlan", "BusLine.getPolyline() 不可用: " + e.getMessage()); } } // 2. 【步行】反射获取 walk polyline if (!added && step.getWalk() != null) { try { java.lang.reflect.Method method = step.getWalk().getClass().getMethod("getPolyline"); Object result = method.invoke(step.getWalk()); if (result instanceof java.util.List) { for (Object obj : (java.util.List<?>) result) { if (obj instanceof LatLonPoint) { LatLonPoint point = (LatLonPoint) obj; allPoints.add(new LatLng(point.getLatitude(), point.getLongitude())); added = true; } } } } catch (Exception e) { Log.d("RoutePlan", "WalkPath.getPolyline() 不可用: " + e.getMessage()); } } // 3. 【地铁/换乘】使用 getEntrance() 和 getExit() -> Doorway -> LatLonPoint if (!added) { Doorway entrance = step.getEntrance(); // 返回 Doorway! Doorway exit = step.getExit(); // 返回 Doorway! if (entrance != null && entrance.getLatLonPoint() != null) { LatLonPoint p = entrance.getLatLonPoint(); allPoints.add(new LatLng(p.getLatitude(), p.getLongitude())); added = true; } if (exit != null && exit.getLatLonPoint() != null) { LatLonPoint p = exit.getLatLonPoint(); allPoints.add(new LatLng(p.getLatitude(), p.getLongitude())); added = true; } if (added) { Log.d("RoutePlan", "使用 Doorway 坐标近似绘制路段"); } } } // === 绘制折线 === if (!allPoints.isEmpty()) { PolylineOptions options = new PolylineOptions() .addAll(allPoints) .color(0xFF4A90E2) .width(12f) .geodesic(false); aMap.addPolyline(options); // 聚焦地图 LatLngBounds.Builder builder = new LatLngBounds.Builder(); for (LatLng latLng : allPoints) { builder.include(latLng); } try { LatLngBounds bounds = builder.build(); aMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100)); } catch (Exception e) { Log.e("RoutePlan", "边界计算失败", e); } } else { Toast.makeText(this, "无法获取路线坐标", Toast.LENGTH_SHORT).show(); } } // ✅ 新增:重写返回键,根据来源决定跳转行为 @Override public void onBackPressed() { if (sourceType == SOURCE_FROM_MAP_DIRECT) { // 来自地图直接跳转:回到主页面并打开地图 Tab Intent mainIntent = new Intent(this, MainActivity.class); mainIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); mainIntent.putExtra("open_tab", "map"); // 可选:通知 MainActivity 切换到地图 startActivity(mainIntent); finish(); // 关闭当前页面 } else { // 默认情况:正常返回上一页(如 SearchResultActivity) super.onBackPressed(); } } // 其他不需要处理的回调 @Override public void onDriveRouteSearched(DriveRouteResult r, int c) {} @Override public void onWalkRouteSearched(WalkRouteResult r, int c) {} @Override public void onRideRouteSearched(RideRouteResult r, int c) {} // 生命周期方法保持不变 @Override protected void onResume() { super.onResume(); mapView.onResume(); } @Override protected void onPause() { super.onPause(); mapView.onPause(); } @Override protected void onDestroy() { if (mapView != null) mapView.onDestroy(); if (routeSearch != null) routeSearch.setRouteSearchListener(null); super.onDestroy(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } } 第一,我希望按照MainActivity的方式获取用户位置,MainActivity代码如下: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()); } }第二,我希望这个代码能够区分是从哪个界面进入的。如果从SearchFragment进入,我需要他真实获取用户位置信息为起点,在SearchResultActivity中输入的信息为终点规划公交路线。如果是从MapFragment进入的,我需要把MapFragment中输入的起点作为线路的起点,MapFragment中输入的终点作为线路的终点。然后,activity_route_plan.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:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:layout_marginTop="?attr/actionBarSize"> <!-- 地图区域(占上面 60%) --> <com.amap.api.maps.MapView android:id="@+id/map_view" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/recycler_steps" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.6" /> <!-- 步骤列表(下方 40%) --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_steps" android:layout_width="0dp" android:layout_height="0dp" android:background="#f5f5f5" android:elevation="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_percent="0.4" /> </androidx.constraintlayout.widget.ConstraintLayout> 我需要你这顶部添加一个返回键,如果是从SearchResultActivity中进来的就返回到SearchResultActivity去。如果是从MapFragment就返回到MapFragment去。相关代码SearchResultActivity如下:package com.example.bus; import android.content.Intent; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.amap.api.maps.AMap; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.MapView; import com.amap.api.maps.UiSettings; 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.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import java.util.ArrayList; import java.util.List; public class SearchResultActivity extends AppCompatActivity implements PoiSearch.OnPoiSearchListener { private EditText searchInput; private Button searchBtn, goToBtn; private RecyclerView resultListView; private List<PoiItem> poiList = new ArrayList<>(); private ResultAdapter adapter; private PoiSearch poiSearch; // 地图相关 private MapView mapView; private AMap aMap; private Marker selectedMarker; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_result); // 设置 ActionBar 返回按钮 if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("搜索地点"); } initViews(); setupMap(savedInstanceState); // 初始化地图 setupSearch(); // 初始化搜索 } private void initViews() { searchInput = findViewById(R.id.search_input); searchBtn = findViewById(R.id.search_btn); resultListView = findViewById(R.id.result_list); goToBtn = findViewById(R.id.btn_go_to); goToBtn.setEnabled(false); } private void setupMap(Bundle savedInstanceState) { mapView = findViewById(R.id.map_view); mapView.onCreate(savedInstanceState); if (aMap == null) { aMap = mapView.getMap(); UiSettings uiSettings = aMap.getUiSettings(); uiSettings.setZoomControlsEnabled(true); uiSettings.setCompassEnabled(true); uiSettings.setScrollGesturesEnabled(true); aMap.setOnMapClickListener(latLng -> { if (selectedMarker != null) selectedMarker.remove(); selectedMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("选中位置")); goToBtn.setEnabled(true); }); aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.909186, 116.397411), 10f)); } } private void setupSearch() { String keyword = getIntent().getStringExtra("keyword"); if (keyword != null && !keyword.isEmpty()) { searchInput.setText(keyword); performSearch(keyword); } searchBtn.setOnClickListener(v -> { String text = searchInput.getText().toString().trim(); if (!text.isEmpty()) { performSearch(text); } else { Toast.makeText(this, "请输入关键词", Toast.LENGTH_SHORT).show(); } }); goToBtn.setOnClickListener(v -> { if (selectedMarker != null) { LatLng pos = selectedMarker.getPosition(); navigateToRoute(pos.latitude, pos.longitude, "地图选点"); } else if (!poiList.isEmpty()) { PoiItem item = poiList.get(0); LatLonPoint p = item.getLatLonPoint(); navigateToRoute(p.getLatitude(), p.getLongitude(), item.getTitle()); } else { Toast.makeText(this, "暂无目标位置", Toast.LENGTH_SHORT).show(); } }); } private void navigateToRoute(double lat, double lng, String title) { Intent intent = new Intent(this, RoutePlanActivity.class); intent.putExtra("start_mode", "my_location"); intent.putExtra("target_lat", lat); intent.putExtra("target_lng", lng); intent.putExtra("target_title", title); startActivity(intent); } private void performSearch(String keyword) { PoiSearch.Query query; if (isLikelyBusStop(keyword)) { // 如果像公交站名,加过滤 query = new PoiSearch.Query(keyword, "15", "全国"); // 公共交通类 } else { // 否则不限类别,确保能搜到地标 query = new PoiSearch.Query(keyword, "", "全国"); } query.setPageSize(20); query.setPageNum(0); try { poiSearch = new PoiSearch(this, query); poiSearch.setOnPoiSearchListener(this); poiSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "搜索启动失败", Toast.LENGTH_SHORT).show(); } } private boolean isLikelyBusStop(String keyword) { return keyword.contains("公交") || keyword.contains("地铁") || keyword.contains("BRT") || keyword.endsWith("站") || keyword.endsWith("场"); } @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { poiList.clear(); poiList.addAll(result.getPois()); if (adapter == null) { adapter = new ResultAdapter(poiList, item -> { if (selectedMarker != null) selectedMarker.remove(); LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); selectedMarker = aMap.addMarker(new MarkerOptions().position(latLng).title(item.getTitle())); aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); goToBtn.setEnabled(true); }); resultListView.setLayoutManager(new LinearLayoutManager(this)); resultListView.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); } goToBtn.setEnabled(!poiList.isEmpty()); } else { String msg = rCode == 17 ? "高德Key校验失败" : "搜索失败: 错误码=" + rCode; Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} @Override protected void onResume() { super.onResume(); mapView.onResume(); } @Override protected void onPause() { super.onPause(); mapView.onPause(); } @Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); if (poiSearch != null) poiSearch.setOnPoiSearchListener(null); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } } MapFragment如下:package com.example.bus.ui.map; import android.Manifest; import android.content.Intent; 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.services.core.AMapException; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.UiSettings; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import com.example.bus.R; import com.example.bus.RoutePlanActivity; import com.example.bus.databinding.FragmentMapBinding; import com.amap.api.services.core.PoiItem; import com.amap.api.services.core.LatLonPoint; // 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(); if (binding.mapSearch != null) { binding.mapSearch.setOnClickListener(v -> { String keyword = binding.mapInput2.getText().toString().trim(); if (keyword.isEmpty()) { Toast.makeText(requireContext(), "请输入终点", Toast.LENGTH_SHORT).show(); return; } PoiSearch.Query query = new PoiSearch.Query(keyword, "", "全国"); query.setPageSize(10); // 可选:设置每页数量 query.setPageNum(0); // 可选:第一页 try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { requireActivity().runOnUiThread(() -> { if (rCode == 1000 && result != null && !result.getPois().isEmpty()) { PoiItem item = result.getPois().get(0); LatLonPoint point = item.getLatLonPoint(); Intent intent = new Intent(requireContext(), RoutePlanActivity.class); intent.putExtra("start_mode", "my_location"); intent.putExtra("target_lat", point.getLatitude()); intent.putExtra("target_lng", point.getLongitude()); intent.putExtra("target_title", item.getTitle()); startActivity(intent); } else { handlePoiSearchError(rCode); } }); } @Override public void onPoiItemSearched(PoiItem item, int rCode) { // 不使用单个 POI 搜索 } }); // 发起异步请求(这个方法不会再抛异常了) search.searchPOIAsyn(); } catch (AMapException e) { e.printStackTrace(); Toast.makeText(requireContext(), "POI搜索初始化失败:" + e.getMessage(), Toast.LENGTH_LONG).show(); } }); } return root; } private void handlePoiSearchError(int rCode) { String errorMsg; switch (rCode) { case 1101: errorMsg = "网络连接异常"; break; case 1102: errorMsg = "网络响应超时"; break; case 1103: errorMsg = "解析异常"; break; case 1104: errorMsg = "请求参数非法"; break; case 1200: errorMsg = "API Key 不存在"; break; case 1202: errorMsg = "签名验证失败(SHA1/包名不匹配)"; break; case 1206: errorMsg = "API Key 权限不足(未开通 POI 搜索服务)"; break; default: errorMsg = "未知错误: " + rCode; break; } Toast.makeText(requireContext(), "搜索失败: " + errorMsg, Toast.LENGTH_LONG).show(); } /** * 初始化地图 */ 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); } }
11-15
有一个问题,为什么我MainActivity中获取定位就那么顺利呢?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()); } } 但是一到这边就报错package com.example.bus; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; 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.MapView; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.PolylineOptions; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.route.*; import com.amap.api.services.core.AMapException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.model.LatLngBounds; import com.amap.api.services.route.BusPath; public class RoutePlanActivity extends AppCompatActivity implements RouteSearch.OnRouteSearchListener, AMapLocationListener { // ✅ 来源类型常量 public static final String EXTRA_SOURCE = "extra_source"; public static final int SOURCE_FROM_HOME_SEARCH = 1001; public static final int SOURCE_FROM_MAP_DIRECT = 1002; private MapView mapView; private AMap aMap; private RouteSearch routeSearch; private RecyclerView recyclerSteps; private boolean isSearching = false; private int sourceType = -1; // ✅ 新增:定位客户端 private AMapLocationClient locationClient; private LatLonPoint currentLocation; // 存储真实位置 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_route_plan); // ✅ 获取来源 sourceType = getIntent().getIntExtra(EXTRA_SOURCE, -1); mapView = findViewById(R.id.map_view); recyclerSteps = findViewById(R.id.recycler_steps); mapView.onCreate(savedInstanceState); if (aMap == null) { aMap = mapView.getMap(); } routeSearch = new RouteSearch(this); routeSearch.setRouteSearchListener(this); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("公交路线规划"); } // ✅ 初始化定位并开始获取一次位置 initLocation(); parseIntentAndStartSearch(); } /** * 初始化定位客户端(只请求一次) */ private void initLocation() { try { locationClient = new AMapLocationClient(this); AMapLocationClientOption option = new AMapLocationClientOption(); option.setOnceLocation(true); // 一次性定位 option.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy); option.setMockEnable(false); locationClient.setLocationOption(option); locationClient.setLocationListener(this); locationClient.startLocation(); // 开始定位 } catch (AMapException e) { Log.e("RoutePlan", "定位客户端初始化失败", e); Toast.makeText(this, "定位功能初始化失败,请检查权限", Toast.LENGTH_LONG).show(); // 可以选择 finish() 或降级处理 } } /** * 解析 Intent 并发起公交路线搜索 */ private void parseIntentAndStartSearch() { if (isSearching) return; double targetLat = getIntent().getDoubleExtra("target_lat", 0); double targetLng = getIntent().getDoubleExtra("target_lng", 0); if (targetLat == 0 || targetLng == 0) { Toast.makeText(this, "目标位置无效", Toast.LENGTH_SHORT).show(); finish(); return; } // ✅ 动态确定起点 LatLonPoint start = null; // 情况1:来自 SearchResultActivity -> 使用真实定位 if (sourceType == SOURCE_FROM_HOME_SEARCH && currentLocation != null) { start = currentLocation; } // 情况2:来自 MapFragment -> 使用自定义起点 else if (sourceType == SOURCE_FROM_MAP_DIRECT) { double startLat = getIntent().getDoubleExtra("start_lat", 0); double startLng = getIntent().getDoubleExtra("start_lng", 0); if (startLat != 0 && startLng != 0) { start = new LatLonPoint(startLat, startLng); } } // 起点为空?提示错误 if (start == null) { Toast.makeText(this, "无法获取起点位置,请稍后再试", Toast.LENGTH_LONG).show(); finish(); return; } LatLonPoint target = new LatLonPoint(targetLat, targetLng); RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(start, target); RouteSearch.BusRouteQuery query = new RouteSearch.BusRouteQuery(fromAndTo, RouteSearch.BUS_DEFAULT, "全国", 0); query.setCityd("全国"); isSearching = true; routeSearch.calculateBusRouteAsyn(query); } // ✅ 定位回调 @Override public void onLocationChanged(AMapLocation loc) { if (loc != null && loc.getErrorCode() == 0) { currentLocation = new LatLonPoint(loc.getLatitude(), loc.getLongitude()); Log.d("RoutePlan", "✅ 获取到真实位置: " + loc.getLatitude() + "," + loc.getLongitude()); } else { Log.e("RoutePlan", "❌ 定位失败: " + (loc != null ? loc.getErrorInfo() : "null")); } } @Override public void onBusRouteSearched(BusRouteResult result, int rCode) { isSearching = false; if (rCode != 1000 || result == null || result.getPaths().isEmpty()) { Toast.makeText(this, "未找到公交路线", Toast.LENGTH_SHORT).show(); finish(); return; } BusPath bestPath = result.getPaths().get(0); drawRouteOnMap(bestPath); StepAdapter adapter = new StepAdapter(bestPath.getSteps()); recyclerSteps.setLayoutManager(new LinearLayoutManager(this)); recyclerSteps.setAdapter(adapter); String info = String.format("耗时:%d分钟 | 票价:¥%.2f", bestPath.getDuration() / 60, bestPath.getCost()); Toast.makeText(this, info, Toast.LENGTH_LONG).show(); } private void drawRouteOnMap(BusPath path) { aMap.clear(); List<LatLng> allPoints = new ArrayList<>(); for (BusStep step : path.getSteps()) { boolean added = false; if (step.getBusLine() != null) { try { Method method = step.getBusLine().getClass().getMethod("getPolyline"); Object result = method.invoke(step.getBusLine()); if (result instanceof List) { for (Object obj : (List<?>) result) { if (obj instanceof LatLonPoint) { LatLonPoint p = (LatLonPoint) obj; allPoints.add(new LatLng(p.getLatitude(), p.getLongitude())); added = true; } } } } catch (Exception e) { Log.d("RoutePlan", "BusLine.getPolyline() 失败: " + e.getMessage()); } } if (!added && step.getWalk() != null) { try { Method method = step.getWalk().getClass().getMethod("getPolyline"); Object result = method.invoke(step.getWalk()); if (result instanceof List) { for (Object obj : (List<?>) result) { if (obj instanceof LatLonPoint) { LatLonPoint p = (LatLonPoint) obj; allPoints.add(new LatLng(p.getLatitude(), p.getLongitude())); added = true; } } } } catch (Exception e) { Log.d("RoutePlan", "WalkPath.getPolyline() 失败: " + e.getMessage()); } } if (!added) { // 使用反射处理 Entrance/Exit Object entrance = step.getEntrance(); Object exit = step.getExit(); try { if (entrance != null) { Method method = entrance.getClass().getMethod("getLatLonPoint"); LatLonPoint point = (LatLonPoint) method.invoke(entrance); if (point != null) { allPoints.add(new LatLng(point.getLatitude(), point.getLongitude())); added = true; } } if (exit != null) { Method method = exit.getClass().getMethod("getLatLonPoint"); LatLonPoint point = (LatLonPoint) method.invoke(exit); if (point != null) { allPoints.add(new LatLng(point.getLatitude(), point.getLongitude())); } } } catch (Exception e) { Log.d("RoutePlan", "通过 Entrance/Exit 获取坐标失败", e); } } } if (!allPoints.isEmpty()) { PolylineOptions options = new PolylineOptions() .addAll(allPoints) .color(0xFF4A90E2) .width(12f); aMap.addPolyline(options); LatLngBounds.Builder builder = new LatLngBounds.Builder(); for (LatLng point : allPoints) { builder.include(point); } LatLngBounds bounds = builder.build(); aMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100)); } else { Toast.makeText(this, "无法绘制路线", Toast.LENGTH_SHORT).show(); } } // ✅ 返回键行为控制 @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } @Override public void onBackPressed() { if (sourceType == SOURCE_FROM_MAP_DIRECT) { // 回到 MainActivity 并打开地图 Tab Intent intent = new Intent(this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.putExtra("open_tab", "map"); startActivity(intent); finish(); } else { // 默认返回上一页 super.onBackPressed(); } } // 其他回调(空实现) @Override public void onDriveRouteSearched(DriveRouteResult r, int c) {} @Override public void onWalkRouteSearched(WalkRouteResult r, int c) {} @Override public void onRideRouteSearched(RideRouteResult r, int c) {} // 生命周期方法 @Override protected void onResume() { super.onResume(); mapView.onResume(); } @Override protected void onPause() { super.onPause(); mapView.onPause(); } @Override protected void onDestroy() { if (mapView != null) mapView.onDestroy(); if (routeSearch != null) routeSearch.setRouteSearchListener(null); if (locationClient != null) locationClient.onDestroy(); super.onDestroy(); } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } } 错误:Unhandled exception: com.amap.api.services.core.AMapException、Unhandled exception: java.lang.Exception、`onBackPressed` is no longer called for back gestures; migrate to AndroidX's backward compatible `OnBackPressedDispatcher`
11-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值