BottomNavigationView长按弹出Toast

此篇博客介绍了如何在真机上移除底部导航栏的长按点击弹出的Toast提示,但在夜神模拟器中失效。作者分享了针对这个问题的代码片段及其解决方案。
/**
 * 移除长按点击弹出Toast
 */
fun BottomNavigationView.removeLongTouchToast() {
    val bottomNavigationMenuView = this.getChildAt(0) as ViewGroup
    val size = bottomNavigationMenuView.childCount
    for (index in 0 until size) {
        bottomNavigationMenuView[index].setOnLongClickListener {
            true
        }
    }
}

真机测试有效,夜神模拟器无效

不对,先说第一个问题,第一次点击搜索框的时候他不会弹出提示需要精准定位权限。我还想问一下MapFragment这边是怎么实现的,好像这边的话比较稳妥,代码如下:package com.example.bus.ui.map; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Editable; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.constraintlayout.widget.ConstraintSet; import com.amap.api.maps.AMap; import com.amap.api.maps.AMapUtils; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.MapView; 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.Marker; import com.amap.api.maps.model.MarkerOptions; import com.amap.api.maps.model.MyLocationStyle; 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 com.amap.api.services.geocoder.GeocodeSearch; import com.amap.api.services.geocoder.RegeocodeQuery; import com.amap.api.services.geocoder.RegeocodeResult; import com.example.bus.CityManager; import com.example.bus.R; import com.example.bus.RealTimePoiSuggestHelper; import com.example.bus.RoutePlanActivity; import com.example.bus.ResultAdapter; import com.example.bus.databinding.FragmentMapBinding; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; public class MapFragment extends Fragment implements PoiSearch.OnPoiSearchListener, GeocodeSearch.OnGeocodeSearchListener { private FragmentMapBinding binding; private MapView mapView; private AMap aMap; private RealTimePoiSuggestHelper activeSuggestHelper; // ← 我们会在这里初始化 // 数据 private List<PoiItem> poiList = new ArrayList<>(); private ResultAdapter adapter; // 当前阶段:1=选择起点, 2=选择终点 private int selectionStage = 0; // 缓存已选 POI private PoiItem selectedStartPoi = null; private PoiItem selectedEndPoi = null; private Marker startMarker = null; private Marker endMarker = null; // 缓存关键词 private String lastStartKeyword = ""; private String lastEndKeyword = ""; // ✅ 当前城市 private String currentCity = ""; private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001; // ✅ 标记是否已居中我的位置 private boolean userHasInteracted = false; // ✅ 反地理编码(仅用于“我的位置”) private GeocodeSearch geocodeSearch; // 【关键新增】保存定位得到的“我的位置” private double myCurrentLat = 0; private double myCurrentLng = 0; private boolean isLocationReady = false; // 定位是否完成 // 🔽 新增缓存字段 private List<PoiItem> nationalResults = new ArrayList<>(); // 全国结果 private List<PoiItem> localResults = new ArrayList<>(); // 本市结果 private List<PoiItem> nearbyResults = new ArrayList<>(); // 附近结果 private boolean isNearbyLoaded = false; // 是否已加载 nearby 数据 private boolean isInNearbyMode = false; // 是否处于附近模式 @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentMapBinding.inflate(inflater, container, false); View root = binding.getRoot(); mapView = binding.mapView; mapView.onCreate(savedInstanceState); initViews(); setupMap(savedInstanceState); setupSearchSuggestion(); // ← 这个方法被重写了 return root; } private void initViews() { adapter = new ResultAdapter(poiList, this::onPoiItemSelected); binding.resultList.setLayoutManager(new LinearLayoutManager(requireContext())); binding.resultList.setAdapter(adapter); binding.mapSearch.setOnClickListener(v -> performSearch()); binding.btnSwitchTarget.setOnClickListener(v -> { if (selectionStage == 1) { showEndpointSelection(binding.mapInput2.getText().toString().trim()); } else if (selectionStage == 2) { showStartpointSelection(binding.mapInput1.getText().toString().trim()); } }); binding.btnGoTo.setOnClickListener(v -> { if (selectedStartPoi != null && selectedEndPoi != null) { Intent intent = new Intent(requireContext(), RoutePlanActivity.class); intent.putExtra(RoutePlanActivity.EXTRA_SOURCE, RoutePlanActivity.SOURCE_FROM_MAP_DIRECT); intent.putExtra("start_lat", selectedStartPoi.getLatLonPoint().getLatitude()); intent.putExtra("start_lng", selectedStartPoi.getLatLonPoint().getLongitude()); intent.putExtra("target_lat", selectedEndPoi.getLatLonPoint().getLatitude()); intent.putExtra("target_lng", selectedEndPoi.getLatLonPoint().getLongitude()); intent.putExtra("target_title", selectedEndPoi.getTitle()); startActivity(intent); } else { Toast.makeText(requireContext(), "请完成起点和终点的选择", Toast.LENGTH_SHORT).show(); } }); binding.btnToggleMode.setOnClickListener(v -> { if (isInNearbyMode) { exitNearbyMode(); } else { enterNearbyMode(); } }); } private void performSearch() { String startKeyword = binding.mapInput1.getText().toString().trim(); String endKeyword = binding.mapInput2.getText().toString().trim(); if (startKeyword.isEmpty()) { Toast.makeText(requireContext(), "请输入起点", Toast.LENGTH_SHORT).show(); return; } if (endKeyword.isEmpty()) { Toast.makeText(requireContext(), "请输入终点", Toast.LENGTH_SHORT).show(); return; } if (startKeyword.equals(lastStartKeyword) && endKeyword.equals(lastEndKeyword) && selectedStartPoi != null && selectedEndPoi != null) { binding.btnGoTo.performClick(); return; } binding.containerResultList.setVisibility(View.VISIBLE); binding.buttonGroup.setVisibility(View.VISIBLE); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(binding.getRoot()); constraintSet.connect( R.id.map_view, ConstraintSet.BOTTOM, R.id.container_result_list, ConstraintSet.TOP, 0 ); constraintSet.applyTo(binding.getRoot()); userHasInteracted = true; View currentFocus = requireActivity().getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0); } if (!startKeyword.equals(lastStartKeyword)) { lastStartKeyword = startKeyword; lastEndKeyword = endKeyword; showStartpointSelection(startKeyword); } else if (!endKeyword.equals(lastEndKeyword)) { lastEndKeyword = endKeyword; showEndpointSelection(endKeyword); } else if (selectedStartPoi == null) { showStartpointSelection(startKeyword); } else { showEndpointSelection(endKeyword); } } private void showStartpointSelection(String keyword) { selectionStage = 1; binding.btnSwitchTarget.setText("选择终点"); binding.btnGoTo.setEnabled(false); binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索起点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void showEndpointSelection(String keyword) { selectionStage = 2; binding.btnSwitchTarget.setText("选择起点"); binding.btnGoTo.setEnabled(false); binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索终点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void doSearch(String keyword) { if (keyword.isEmpty()) return; nationalResults.clear(); localResults.clear(); nearbyResults.clear(); isNearbyLoaded = false; isInNearbyMode = false; binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); // ✅ 线路判断 boolean isLineQuery = keyword.matches(".*?(地铁|公交|轻轨|云巴|磁浮|有轨电车|brt|apm).*?\\d+.*") || Pattern.compile("(s|r|x)\\d+", Pattern.CASE_INSENSITIVE).matcher(keyword).find() || keyword.contains("云巴") || keyword.contains("轻轨"); if (isLineQuery) { String numberPart = keyword.replaceAll("[^\\d]", ""); String baseKeyword = numberPart.isEmpty() ? keyword : numberPart + "号线"; String poiType = keyword.contains("地铁") || keyword.contains("轨道") ? "subway" : keyword.contains("公交") ? "bus_station" : keyword.contains("云巴") ? "bus_station" : keyword.contains("轻轨") ? "light_rail" : ""; PoiSearch.Query query = new PoiSearch.Query(baseKeyword, poiType, currentCity.isEmpty() ? "" : currentCity); query.setPageSize(50); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<PoiItem> filtered = new ArrayList<>(); String lowerKey = keyword.toLowerCase(); for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase(); if (title.contains(lowerKey) || title.contains(baseKeyword.toLowerCase())) { filtered.add(item); } } if (!filtered.isEmpty()) { updateResultList(filtered); return; } } // 失败 → 降级 fallbackToOriginalSearch(keyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); return; } catch (Exception e) { // 忽略异常,继续走原逻辑 } } // ❌ 原有逻辑(完全保留) CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(this); search.searchPOIAsyn(); } catch (Exception e) { Toast.makeText(requireContext(), "搜索失败", Toast.LENGTH_SHORT).show(); } } else { binding.emptyView.setText("🔍 搜索中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch nationalSearch = new PoiSearch(requireContext(), nationalQuery); nationalSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nationalSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); requestLocalSearch(searchKeyword); } } } private void fallbackToOriginalSearch(String keyword) { CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(MapFragment.this); search.searchPOIAsyn(); } catch (Exception e) { binding.emptyView.setText("⚠️ 未找到相关地点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } else { nationalResults.clear(); localResults.clear(); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch ns = new PoiSearch(requireContext(), nationalQuery); ns.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); ns.searchPOIAsyn(); } catch (Exception e) { requestLocalSearch(searchKeyword); } } } private void requestLocalSearch(String keyword) { if (currentCity.isEmpty()) { showCombinedResults(); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(requireContext(), localQuery); localSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { localResults.clear(); localResults.addAll(result.getPois()); } showCombinedResults(); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); showCombinedResults(); } } private void showCombinedResults() { List<PoiItem> combined = new ArrayList<>(); Set<String> seen = new HashSet<>(); adapter.clearExtraText(); for (PoiItem item : localResults) { if (seen.add(item.getPoiId())) { combined.add(item); String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } } for (PoiItem item : nationalResults) { if (seen.add(item.getPoiId())) { combined.add(item); String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } } updateResultList(combined); } private String getDisplayCity(PoiItem item) { if (item == null) return "未知城市"; String city = item.getCityName(); if (city != null && !city.isEmpty() && !city.equals("[]")) { return city; } String adName = item.getAdName(); if (adName != null && !adName.isEmpty() && !adName.equals("[]")) { return adName; } String province = item.getProvinceName(); if (province != null && !province.isEmpty()) { return province; } return "未知城市"; } private void enterNearbyMode() { if (!isLocationReady) { Toast.makeText(requireContext(), "正在获取位置...", Toast.LENGTH_SHORT).show(); return; } String keyword = getCurrentKeyword(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); String explicitCity = parsed.targetCity; String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; if (!explicitCity.isEmpty() && !isSameCity(explicitCity, currentCity)) { nearbyResults.clear(); localResults.clear(); nationalResults.clear(); poiList.clear(); adapter.notifyDataSetChanged(); binding.emptyView.setText("📍 所选城市非当前所在城市\n无法搜索附近"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); isInNearbyMode = false; return; } if (!isNearbyLoaded) { startNearbySearch(searchKeyword); } else { showNearbyResults(); } isInNearbyMode = true; binding.btnToggleMode.setText("🌐 全范围"); } private boolean isSameCity(String city1, String city2) { if (city1 == null || city2 == null) return false; String c1 = city1.endsWith("市") ? city1.substring(0, city1.length() - 1) : city1; String c2 = city2.endsWith("市") ? city2.substring(0, city2.length() - 1) : city2; return c1.equals(c2); } private void exitNearbyMode() { showCombinedResults(); isInNearbyMode = false; binding.btnToggleMode.setText("📍 附近"); } private void startNearbySearch(String keyword) { String rawKeyword = getCurrentKeyword(); CityManager.ParsedQuery parsed = CityManager.parse(rawKeyword); String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty() && !isSameCity(explicitCity, currentCity)) { Log.w("MapFragment", "拒绝发起跨城 nearby 搜索: " + explicitCity); binding.emptyView.setText("📍 所选城市非当前所在城市\n无法搜索附近"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); return; } LatLonPoint center = new LatLonPoint(myCurrentLat, myCurrentLng); PoiSearch.Query query = new PoiSearch.Query(keyword, "", ""); query.setPageSize(20); try { PoiSearch nearbySearch = new PoiSearch(requireContext(), query); nearbySearch.setBound(new PoiSearch.SearchBound(center, 3000)); nearbySearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult res, int code) { if (code == 1000 && res != null && res.getPois() != null && !res.getPois().isEmpty()) { nearbyResults.clear(); nearbyResults.addAll(sortByDistance(res.getPois(), myCurrentLat, myCurrentLng)); isNearbyLoaded = true; showNearbyResults(); } else { binding.emptyView.setText("⚠️ 附近未找到地点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nearbySearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); binding.emptyView.setText("⚠️ 附近搜索失败"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } private void showNearbyResults() { List<PoiItem> list = new ArrayList<>(nearbyResults); adapter.clearExtraText(); LatLng me = new LatLng(myCurrentLat, myCurrentLng); for (PoiItem item : nearbyResults) { double dist = AMapUtils.calculateLineDistance(toLatLng(item.getLatLonPoint()), me); String distText = dist < 1000 ? ((int) dist) + "m" : String.format("%.1fkm", dist / 1000); adapter.setExtraText(item, " | " + distText); } updateResultList(list); } private void onPoiItemSelected(PoiItem item) { LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); if (selectionStage == 1) { if (startMarker != null) startMarker.remove(); startMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("起点:" + item.getTitle()) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))); selectedStartPoi = item; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); updateGoToButtonState(); } else if (selectionStage == 2) { if (endMarker != null) endMarker.remove(); endMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("终点:" + item.getTitle()) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))); selectedEndPoi = item; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); updateGoToButtonState(); } userHasInteracted = true; } private void updateGoToButtonState() { binding.btnGoTo.setEnabled(selectedStartPoi != null && selectedEndPoi != null); } @Override public void onPoiSearched(PoiResult result, int rCode) { String keyword = getCurrentKeyword(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); if (parsed.targetCity.isEmpty()) return; if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { updateResultList(result.getPois()); } else { handleSearchError(rCode); binding.emptyView.setText("⚠️ 未找到相关地点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } private String getCurrentKeyword() { return selectionStage == 1 ? binding.mapInput1.getText().toString().trim() : binding.mapInput2.getText().toString().trim(); } private void updateResultList(List<PoiItem> list) { poiList.clear(); poiList.addAll(list); adapter.notifyDataSetChanged(); binding.resultList.scrollToPosition(0); binding.emptyView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); binding.resultList.setVisibility(list.isEmpty() ? View.GONE : View.VISIBLE); if (!list.isEmpty()) { adapter.setSelected(0); onPoiItemSelected(list.get(0)); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} private LatLng toLatLng(LatLonPoint point) { if (point == null) return null; return new LatLng(point.getLatitude(), point.getLongitude()); } private List<PoiItem> sortByDistance(List<PoiItem> list, double lat, double lng) { LatLng me = new LatLng(lat, lng); return list.stream() .sorted((a, b) -> { double da = AMapUtils.calculateLineDistance(toLatLng(a.getLatLonPoint()), me); double db = AMapUtils.calculateLineDistance(toLatLng(b.getLatLonPoint()), me); return Double.compare(da, db); }) .collect(java.util.stream.Collectors.toList()); } private void setupMap(Bundle savedInstanceState) { mapView.onCreate(savedInstanceState); aMap = mapView.getMap(); if (aMap != null) { initMapSettings(); } else { new Handler(Looper.getMainLooper()).post(() -> { aMap = mapView.getMap(); if (aMap != null) { initMapSettings(); } else { waitAMapReady(); } }); } try { geocodeSearch = new GeocodeSearch(requireContext()); geocodeSearch.setOnGeocodeSearchListener(this); } catch (Exception e) { e.printStackTrace(); } } private void waitAMapReady() { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { int retry = 0; @Override public void run() { if (mapView == null) return; aMap = mapView.getMap(); if (aMap != null) { initMapSettings(); } else if (retry++ < 30) { new Handler(Looper.getMainLooper()).postDelayed(this, 100); } } }, 100); } private void initMapSettings() { UiSettings uiSettings = aMap.getUiSettings(); uiSettings.setZoomControlsEnabled(true); uiSettings.setCompassEnabled(true); uiSettings.setScrollGesturesEnabled(true); uiSettings.setMyLocationButtonEnabled(true); new Handler(Looper.getMainLooper()).post(() -> aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.909186, 116.397411), 10f)) ); enableMyLocationLayer(); } private void enableMyLocationLayer() { if (aMap == null) return; if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { MyLocationStyle myLocationStyle = new MyLocationStyle(); myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER); aMap.setMyLocationStyle(myLocationStyle); aMap.setMyLocationEnabled(true); AMap.OnMyLocationChangeListener listener = location -> { if (location != null && !userHasInteracted) { LatLng curLatlng = new LatLng(location.getLatitude(), location.getLongitude()); if (activeSuggestHelper != null) { activeSuggestHelper.setLocationBias(location.getLatitude(), location.getLongitude()); } myCurrentLat = location.getLatitude(); myCurrentLng = location.getLongitude(); isLocationReady = true; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 16f)); userHasInteracted = true; LatLonPoint point = new LatLonPoint(myCurrentLat, myCurrentLng); RegeocodeQuery query = new RegeocodeQuery(point, 200, GeocodeSearch.AMAP); try { geocodeSearch.getFromLocationAsyn(query); } catch (Exception e) { e.printStackTrace(); } aMap.setOnMyLocationChangeListener(null); } }; aMap.setOnMyLocationChangeListener(listener); } else { ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (aMap != null) { MyLocationStyle myLocationStyle = new MyLocationStyle(); myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER); aMap.setMyLocationStyle(myLocationStyle); aMap.setMyLocationEnabled(true); AMap.OnMyLocationChangeListener listener = location -> { if (location != null && !userHasInteracted) { LatLng curLatlng = new LatLng(location.getLatitude(), location.getLongitude()); if (activeSuggestHelper != null) { activeSuggestHelper.setLocationBias(location.getLatitude(), location.getLongitude()); } myCurrentLat = location.getLatitude(); myCurrentLng = location.getLongitude(); isLocationReady = true; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 16f)); userHasInteracted = true; LatLonPoint point = new LatLonPoint(myCurrentLat, myCurrentLng); RegeocodeQuery query = new RegeocodeQuery(point, 200, GeocodeSearch.AMAP); try { geocodeSearch.getFromLocationAsyn(query); } catch (Exception e) { e.printStackTrace(); } aMap.setOnMyLocationChangeListener(null); } }; aMap.setOnMyLocationChangeListener(listener); } } } } @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (result == null || result.getRegeocodeQuery() == null) return; LatLonPoint point = result.getRegeocodeQuery().getPoint(); if (rCode == 1000 && result.getRegeocodeAddress() != null) { String city = result.getRegeocodeAddress().getCity(); String updatedCity = (city != null && !city.isEmpty()) ? city : result.getRegeocodeAddress().getProvince(); if (Math.abs(point.getLatitude() - myCurrentLat) < 0.0001 && Math.abs(point.getLongitude() - myCurrentLng) < 0.0001) { currentCity = updatedCity; if (activeSuggestHelper != null) { activeSuggestHelper.setCurrentCity(currentCity); } Log.d("MapFragment", "🎯 currentCity 已更新为: " + currentCity); } } else { Log.e("MapFragment", "❌ 反编译失败: rCode=" + rCode); } } @Override public void onGeocodeSearched(com.amap.api.services.geocoder.GeocodeResult geocodeResult, int i) {} @Override public void onResume() { super.onResume(); mapView.onResume(); if (!userHasInteracted) { enableMyLocationLayer(); } setupSearchSuggestion(); } @Override public void onPause() { super.onPause(); mapView.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); mapView.onDestroy(); geocodeSearch = null; binding = null; } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } private void setupSearchSuggestion() { Handler handler = new Handler(Looper.getMainLooper()); Runnable[] pendingRunnable1 = {null}; Runnable[] pendingRunnable2 = {null}; // 输入框1:起点输入建议 binding.mapInput1.addTextChangedListener(new android.text.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 (pendingRunnable1[0] != null) { handler.removeCallbacks(pendingRunnable1[0]); } if (s.length() == 0) { binding.mapInput1.setAdapter(null); return; } String keyword = s.toString().trim(); pendingRunnable1[0] = () -> fetchSuggestionTitles(keyword, binding.mapInput1); handler.postDelayed(pendingRunnable1[0], 300); } @Override public void afterTextChanged(Editable s) {} }); // 输入框2:终点输入建议 binding.mapInput2.addTextChangedListener(new android.text.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 (pendingRunnable2[0] != null) { handler.removeCallbacks(pendingRunnable2[0]); } if (s.length() == 0) { binding.mapInput2.setAdapter(null); return; } String keyword = s.toString().trim(); pendingRunnable2[0] = () -> fetchSuggestionTitles(keyword, binding.mapInput2); handler.postDelayed(pendingRunnable2[0], 300); } @Override public void afterTextChanged(Editable s) {} }); // 软键盘搜索事件 binding.mapInput1.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { performSearch(); return true; } return false; }); binding.mapInput2.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { performSearch(); return true; } return false; }); // 如果已定位成功,不需要再设 bias(因为我们不再使用 suggestHelper) } private void fetchSuggestionTitles(String keyword, android.widget.AutoCompleteTextView inputView) { if (keyword.isEmpty()) return; CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; // 如果指定了城市,只查该城市 if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<String> titles = result.getPois().stream() .map(PoiItem::getTitle) .collect(java.util.stream.Collectors.toList()); updateSuggestionAdapter(titles.toArray(new String[0]), inputView); } else { updateSuggestionAdapter(new String[0], inputView); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); } catch (Exception e) { updateSuggestionAdapter(new String[0], inputView); } return; } // 否则走两级查询:national + local List<PoiItem> tempNational = new ArrayList<>(); List<PoiItem> tempLocal = new ArrayList<>(); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch nationalSearch = new PoiSearch(requireContext(), nationalQuery); nationalSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { tempNational.addAll(result.getPois()); } requestLocalForSuggestion(searchKeyword, tempNational, tempLocal, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nationalSearch.searchPOIAsyn(); } catch (Exception e) { requestLocalForSuggestion(searchKeyword, tempNational, tempLocal, inputView); } } private void requestLocalForSuggestion(String keyword, List<PoiItem> national, List<PoiItem> local, android.widget.AutoCompleteTextView inputView) { if (currentCity.isEmpty()) { showSuggestionResult(national, local, inputView); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(requireContext(), localQuery); localSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { local.addAll(result.getPois()); } showSuggestionResult(national, local, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { showSuggestionResult(national, local, inputView); } } private void showSuggestionResult(List<PoiItem> national, List<PoiItem> local, android.widget.AutoCompleteTextView inputView) { Set<String> seen = new HashSet<>(); List<String> combined = new ArrayList<>(); for (PoiItem item : local) { if (seen.add(item.getPoiId())) { combined.add(item.getTitle()); } } for (PoiItem item : national) { if (seen.add(item.getPoiId())) { combined.add(item.getTitle()); } } updateSuggestionAdapter(combined.toArray(new String[0]), inputView); } private void updateSuggestionAdapter(String[] suggestions, android.widget.AutoCompleteTextView inputView) { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( requireContext(), android.R.layout.simple_dropdown_item_1line, suggestions ) { @Override public View getView(int position, View convertView, android.view.ViewGroup parent) { View view = super.getView(position, convertView, parent); // 为每个 item 添加点击事件 view.setOnClickListener(v -> { String selectedText = getItem(position); if (selectedText != null) { inputView.setText(selectedText); inputView.clearFocus(); // 隐藏软键盘 InputMethodManager imm = (InputMethodManager) requireContext() .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(inputView.getWindowToken(), 0); // ✅ 模拟点击搜索按钮(触发 performSearch) performSearch(); } }); return view; } }; new Handler(Looper.getMainLooper()).post(() -> { inputView.setAdapter(adapter); if (requireActivity().getCurrentFocus() == inputView) { inputView.showDropDown(); } }); } else { new Handler(Looper.getMainLooper()).post(() -> inputView.setAdapter(null)); } } private void handleSearchError(int rCode) { String msg; switch (rCode) { case 12: msg = "API Key 错误"; 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(); } } 而HomeFragmenet代码如下:package com.example.bus.ui.home; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import com.example.bus.MainActivity; import com.example.bus.RealTimePoiSuggestHelper; import com.example.bus.SearchResultActivity; import com.example.bus.databinding.FragmentHomeBinding; public class HomeFragment extends Fragment { private FragmentHomeBinding binding; private HomeViewModel homeViewModel; private static final int TIPS_DELAY = 300; // 防抖延迟 private Handler handler = new Handler(Looper.getMainLooper()); private Runnable pendingRequest; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentHomeBinding.inflate(inflater, container, false); View root = binding.getRoot(); // 初始化 ViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class); homeViewModel.getText().observe(getViewLifecycleOwner(), text -> { binding.textHome.setText(text); }); setupSearchSuggestion(); setupSearchAction(); setupSearchBoxWithPermissionControl(); return root; } private void setupSearchBoxWithPermissionControl() { binding.homeInput.setOnClickListener(v -> { MainActivity activity = (MainActivity) requireActivity(); activity.ensureFineLocationPermission(() -> { // 权限已获得,现在才允许 EditText 获取焦点并弹出软键盘 binding.homeInput.post(() -> { binding.homeInput.setFocusableInTouchMode(true); binding.homeInput.requestFocus(); InputMethodManager imm = (InputMethodManager) requireContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.showSoftInput(binding.homeInput, InputMethodManager.SHOW_IMPLICIT); } }); }); }); } /** * 设置输入建议功能(suggestion) */ private void setupSearchSuggestion() { // 创建建议助手 RealTimePoiSuggestHelper suggestHelper = new RealTimePoiSuggestHelper(requireContext()); suggestHelper.setCurrentCity("全国"); // 可改为动态城市 suggestHelper.setCallback(suggestions -> { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( requireContext(), android.R.layout.simple_dropdown_item_1line, suggestions ) { @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); // 👉 添加点击事件 view.setOnClickListener(v -> { String selectedText = getItem(position); if (selectedText != null && !selectedText.isEmpty()) { binding.homeInput.setText(selectedText); binding.homeInput.clearFocus(); // 隐藏软键盘 InputMethodManager imm = (InputMethodManager) requireContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(binding.homeInput.getWindowToken(), 0); } // 直接触发搜索(跳转到 SearchResultActivity) performSearch(); } }); return view; } }; // 主线程更新 UI new Handler(Looper.getMainLooper()).post(() -> { binding.homeInput.setAdapter(adapter); if (requireActivity().getCurrentFocus() == binding.homeInput) { binding.homeInput.showDropDown(); } }); } else { new Handler(Looper.getMainLooper()).post(() -> binding.homeInput.setAdapter(null) ); } }); // 防抖控制 handler = new Handler(Looper.getMainLooper()); pendingRequest = null; 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 (pendingRequest != null) { handler.removeCallbacks(pendingRequest); } if (s.length() == 0) { binding.homeInput.setAdapter(null); return; } boolean hasPermission = requireContext().checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED; if (!hasPermission) { // 没有权限 → 不请求建议,也不显示任何提示 binding.homeInput.setAdapter(null); return; } pendingRequest = () -> suggestHelper.requestSuggestions(s.toString()); handler.postDelayed(pendingRequest, TIPS_DELAY); } @Override public void afterTextChanged(Editable s) {} }); } /** * 设置搜索动作:回车 & 按钮点击 */ private void setupSearchAction() { // 回车搜索 binding.homeInput.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { performSearch(); return true; } return false; }); // 搜索按钮点击 binding.homeSearch.setOnClickListener(v -> performSearch()); } private void performSearch() { String keyword = binding.homeInput.getText().toString().trim(); if (keyword.isEmpty()) { Toast.makeText(requireContext(), "请输入关键词", Toast.LENGTH_SHORT).show(); return; } MainActivity activity = (MainActivity) requireActivity(); activity.ensureFineLocationPermission(() -> { Intent intent = new Intent(requireContext(), SearchResultActivity.class); intent.putExtra("keyword", keyword); startActivity(intent); }); } @Override public void onDestroyView() { super.onDestroyView(); binding = null; // 清理防抖任务 if (handler != null) { handler.removeCallbacksAndMessages(null); } } } 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.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; private static final String PREF_FIRST_LAUNCH = "pref_first_launch"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); boolean isFirstLaunch = getSharedPreferences("app_config", MODE_PRIVATE) .getBoolean(PREF_FIRST_LAUNCH, true); if (isFirstLaunch) { getSharedPreferences("app_config", MODE_PRIVATE) .edit() .putBoolean(PREF_FIRST_LAUNCH, false) .apply(); } 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); // 设置 BottomNavigationView 的监听器 navView.setOnItemSelectedListener(item -> { int itemId = item.getItemId(); if (itemId == R.id.navigation_map) { // 只有点击地图 tab 才走权限 + 跳转逻辑 ensureFineLocationPermission(() -> { if (navController.getCurrentDestination().getId() != R.id.navigation_map) { navController.navigate(R.id.navigation_map); } }); } else { navController.navigate(itemId); } return true; }); if (savedInstanceState != null) { int savedId = savedInstanceState.getInt(SAVED_NAV_ID, R.id.navigation_home); navView.setSelectedItemId(savedId); } } /** * 统一权限请求方法,权限通过后执行 onGranted */ public void ensureFineLocationPermission(Runnable onGranted) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { onGranted.run(); } else { requestLocationPermission(onGranted); } } private void requestLocationPermission(Runnable onGranted) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { new AlertDialog.Builder(this) .setTitle("需要精确定位权限") .setMessage("为了提供更好的服务,我们需要获取您的精确位置。\n否则将无法使用地图及搜索等相关功能。\n\n请务必选择【仅使用期间允许】或【本次使用允许】。") .setPositiveButton("去允许", (d, w) -> startRequestPermission(onGranted)) .setNegativeButton("取消", null) .show(); } else { startRequestPermission(onGranted); } } private void startRequestPermission(Runnable onGranted) { // 使用成员变量保存回调 this.pendingRunnable = onGranted; ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); } private Runnable pendingRunnable; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; if (granted) { Toast.makeText(this, "已获得精确定位权限", Toast.LENGTH_SHORT).show(); // 执行传进来的任务(可能是跳转 SearchResultActivity 或进入 MapFragment) if (pendingRunnable != null) { pendingRunnable.run(); pendingRunnable = null; } } 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(); } pendingRunnable = null; // 清理 } } } 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(@NonNull Bundle outState) { super.onSaveInstanceState(outState); BottomNavigationView navView = findViewById(R.id.nav_view); outState.putInt(SAVED_NAV_ID, navView.getSelectedItemId()); } } 难道这几个逻辑不一样吗?
最新发布
12-11
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值