Android Tips-- Activity.runOnUiThread

本文探讨了在Android开发中,如何高效地更新UI。通常开发者会直接使用Handler来完成这一任务,但实际上,对于简单的UI更新需求,Activity提供的runOnUiThread方法更为简便有效。该方法能确保在UI线程执行代码,避免了不必要的复杂操作。

     在开发过程中,发现Handler无处不在,更多的是直接new出一个Handler仅仅是为了在UI线程执行一句代码。

Handler mHandler = new Handler();
.....
   mHandler.post(new Runnable() {
			   public void run() {
				   textView.setText(R.string.success);
			   }
		});

是,Handler很强大,但有那么一些地方根本不必自己去使用Handler,或许有更好的方法呢?如仅仅是子线程更新界面,google的工程师们已经替我们想好了,其实Activity中提供了这么一个方法:

public final void runOnUiThread (Runnable action)
Added in  API level 1

Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.

Parameters
action the action to run on the UI thread

其中的action将会被保证在UI线程去执行.


其实看源码的话,你会发现,这个方法的实现跟你想做的没什么,但是它替你做了,我们又何必重复呢

/** 
* Runs the specified action on the UI thread. If the current thread is the UI 
* thread, then the action is executed immediately. If the current thread is 
* not the UI thread, the action is posted to the event queue of the UI thread. 
* 
* @param action the action to run on the UI thread 
*/ 
public final void runOnUiThread(Runnable action) { 
    if (Thread.currentThread() != mUiThread) { 
        mHandler.post(action); 
    } else { 
        action.run(); 
    } 
}



那现在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.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; 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 androidx.constraintlayout.widget.ConstraintLayout; // 🔺 新增导入:用于设置 Marker 颜色 import com.amap.api.maps.model.BitmapDescriptorFactory; 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.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.help.Inputtips; import com.amap.api.services.help.Tip; 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.R; import com.example.bus.RoutePlanActivity; import com.example.bus.ResultAdapter; import com.example.bus.databinding.FragmentMapBinding; import java.util.ArrayList; import java.util.List; // ✅ 新增导入 import android.view.inputmethod.EditorInfo; public class MapFragment extends Fragment implements PoiSearch.OnPoiSearchListener, GeocodeSearch.OnGeocodeSearchListener { private FragmentMapBinding binding; private MapView mapView; private AMap aMap; private Inputtips inputTips; // 数据 private List<PoiItem> poiList = new ArrayList<>(); private ResultAdapter adapter; private PoiSearch poiSearch; // 当前阶段: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; // 定位是否完成 @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(); } }); } 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; } // 展示 UI binding.containerResultList.setVisibility(View.VISIBLE); binding.buttonGroup.setVisibility(View.VISIBLE); // 动态压缩地图区域 ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone((ConstraintLayout) binding.getRoot()); constraintSet.connect( R.id.map_view, ConstraintSet.BOTTOM, R.id.container_result_list, ConstraintSet.TOP, 0 ); constraintSet.applyTo((ConstraintLayout) 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.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.emptyView.setText("🔍 搜索终点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void doSearch(String keyword) { if (keyword.isEmpty()) return; PoiSearch.Query query = new PoiSearch.Query(keyword, "", currentCity); query.setPageSize(20); query.setPageNum(0); // ✅ 使用缓存的位置(仅当真实定位完成后) if (isLocationReady) { LatLonPoint lp = new LatLonPoint(myCurrentLat, myCurrentLng); query.setLocation(lp); // 让高德根据“我在哪”智能排序 } try { poiSearch = new PoiSearch(requireContext(), query); poiSearch.setOnPoiSearchListener(this); poiSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(requireContext(), "搜索失败", Toast.LENGTH_SHORT).show(); } } private void onPoiItemSelected(PoiItem item) { LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); if (selectionStage == 1) { if (startMarker != null) { startMarker.remove(); startMarker = null; } 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 = null; } 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) { if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { List<PoiItem> newPoiList = result.getPois(); poiList.clear(); poiList.addAll(newPoiList); if (adapter == null) { adapter = new ResultAdapter(poiList, this::onPoiItemSelected); binding.resultList.setLayoutManager(new LinearLayoutManager(requireContext())); binding.resultList.setAdapter(adapter); } else { adapter.notifyDataSetChanged(); binding.resultList.scrollToPosition(0); } binding.emptyView.setVisibility(View.GONE); binding.resultList.setVisibility(View.VISIBLE); // 先设置 Adapter 的选中状态(触发 UI 高亮) adapter.setSelected(0); // 自动选中第一个 onPoiItemSelected(poiList.get(0)); } else { if (selectionStage == 1) { binding.emptyView.setText("⚠️ 未找到相关起点"); } else if (selectionStage == 2) { binding.emptyView.setText("⚠️ 未找到相关终点"); } binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) { // 必须实现 } 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()); // ✅ 记录真实位置 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()); // ✅ 同步记录位置 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 { Toast.makeText(requireContext(), "定位权限被拒绝,部分功能受限", Toast.LENGTH_LONG).show(); currentCity = "全国"; } } } @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (rCode == 1000 && result != null && result.getRegeocodeAddress() != null) { String city = result.getRegeocodeAddress().getCity(); currentCity = (city != null && !city.isEmpty()) ? city : result.getRegeocodeAddress().getProvince(); } else { currentCity = "全国"; } } @Override public void onGeocodeSearched(com.amap.api.services.geocoder.GeocodeResult geocodeResult, int i) { // 忽略 } @Override public void onResume() { super.onResume(); mapView.onResume(); if (!userHasInteracted) { enableMyLocationLayer(); } } @Override public void onPause() { super.onPause(); mapView.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); mapView.onDestroy(); inputTips = null; geocodeSearch = null; binding = null; } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } // ✅ 完整保留原始的 setupSearchSuggestion 实现 private void setupSearchSuggestion() { try { inputTips = new Inputtips(requireContext(), new Inputtips.InputtipsListener() { @Override public void onGetInputtips(List<Tip> tipList, int rCode) { if (rCode == 1000 && tipList != null && !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 ); // ✅ 主线程执行 + 强制弹出 new Handler(Looper.getMainLooper()).post(() -> { // 不管哪个是焦点,都尝试设置两个输入框的 adapter binding.mapInput1.setAdapter(adapter); binding.mapInput2.setAdapter(adapter); // ✅ 关键修复:强制弹出下拉框 if (requireActivity().getCurrentFocus() == binding.mapInput1) { binding.mapInput1.showDropDown(); } else if (requireActivity().getCurrentFocus() == binding.mapInput2) { binding.mapInput2.showDropDown(); } }); } // ✅ 不再清空 adapter 或报错 —— 让用户保留上次有效建议 } }); } catch (Exception e) { e.printStackTrace(); Toast.makeText(requireContext(), "智能提示初始化失败", Toast.LENGTH_SHORT).show(); } Handler handler = new Handler(Looper.getMainLooper()); Runnable[] pending1 = {null}, pending2 = {null}; // 修改 mapInput1 监听器:移除延迟,改为直接请求 binding.mapInput1.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 (pending1[0] != null) { handler.removeCallbacks(pending1[0]); } // ✅ 输入为空则清空建议 if (s.length() == 0) { binding.mapInput1.setAdapter(null); return; } // ✅ 【核心】不再等 600ms,直接发起请求 try { inputTips.requestInputtips(s.toString(), currentCity); } catch (Exception e) { e.printStackTrace(); } } @Override public void afterTextChanged(Editable s) {} }); // 同样处理 mapInput2 binding.mapInput2.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 (pending2[0] != null) { handler.removeCallbacks(pending2[0]); } if (s.length() == 0) { binding.mapInput2.setAdapter(null); return; } try { inputTips.requestInputtips(s.toString(), currentCity); } catch (Exception e) { e.printStackTrace(); } } @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; }); } 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(); } // ✅ 自定义 TextWatcher 类,保留原始写法 private static class SimpleTextWatcher implements android.text.TextWatcher { private final java.util.function.Consumer<CharSequence> onTextChanged; public SimpleTextWatcher(java.util.function.Consumer<CharSequence> onTextChanged) { this.onTextChanged = onTextChanged; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { onTextChanged.accept(s); } } } HomeFragment代码如下:package com.example.bus.ui.home; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Editable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; 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 com.example.bus.MainActivity; import com.example.bus.SearchResultActivity; import com.example.bus.databinding.FragmentHomeBinding; import java.util.List; public class HomeFragment extends Fragment { private FragmentHomeBinding binding; private HomeViewModel homeViewModel; private Inputtips inputTips; 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(); return root; } /** * 设置输入建议功能(suggestion) */ private void setupSearchSuggestion() { try { inputTips = new Inputtips(requireContext(), new Inputtips.InputtipsListener() { @Override public void onGetInputtips(List<Tip> tipList, int rCode) { if (rCode == 1000 && tipList != null && !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 ); // 主线程更新 UI 并弹出下拉 new Handler(Looper.getMainLooper()).post(() -> { binding.homeInput.setAdapter(adapter); binding.homeInput.showDropDown(); // ✅ 关键:强制弹出 }); } // 如果失败或无结果,不清空 adapter,保留上次有效建议 } }); } catch (Exception e) { e.printStackTrace(); Toast.makeText(requireContext(), "提示服务初始化失败", Toast.LENGTH_SHORT).show(); } // 添加文本监听 binding.homeInput.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 (pendingRequest != null) { handler.removeCallbacks(pendingRequest); } if (s.length() == 0) { binding.homeInput.setAdapter(null); return; } pendingRequest = () -> { try { // 使用全国范围搜索 inputTips.requestInputtips(s.toString(), "全国"); } catch (Exception e) { e.printStackTrace(); } }; 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; // 置空引用即可 inputTips = null; // 清理防抖任务 if (handler != null) { handler.removeCallbacksAndMessages(null); } } } SearchResultActivity代码如下:package com.example.bus; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import 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.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.amap.api.services.help.Inputtips; import com.amap.api.services.help.Tip; import android.text.Editable; import android.text.TextWatcher; import android.widget.ArrayAdapter; import java.util.ArrayList; import java.util.List; import android.view.inputmethod.EditorInfo; public class SearchResultActivity extends AppCompatActivity implements PoiSearch.OnPoiSearchListener, GeocodeSearch.OnGeocodeSearchListener { 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; // 终点标记 // 输入提示 private Inputtips inputTips; private GeocodeSearch geocodeSearch; // 当前城市 private String currentCity = "全国"; // 是否已与地图交互(防止重复居中) private boolean userHasInteracted = false; private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001; // 空状态提示视图 private TextView emptyView; // 【关键新增】保存定位得到的“我的位置” private double myCurrentLat = 0; private double myCurrentLng = 0; private boolean isLocationReady = false; // 定位是否完成 // 缓存从 HomeFragment 传来的关键词,等待定位完成后使用 private String pendingKeyword = null; // ✅ 新增:将搜索输入框提升为成员变量 private android.widget.AutoCompleteTextView searchInput; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_result); if (getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle("搜索地点"); } initViews(); setupMap(savedInstanceState); setupSearchSuggestion(); try { geocodeSearch = new GeocodeSearch(this); geocodeSearch.setOnGeocodeSearchListener(this); } catch (Exception e) { e.printStackTrace(); } // ✅ 只缓存 keyword,不再尝试提前搜索 pendingKeyword = getIntent().getStringExtra("keyword"); // 删除以下三行:它们永远不会生效 // if (isLocationReady && pendingKeyword != null) { // runOnUiThread(() -> performSearchWithKeyword(pendingKeyword)); // } } private void initViews() { searchBtn = findViewById(R.id.search_btn); resultListView = findViewById(R.id.result_list); goToBtn = findViewById(R.id.btn_go_to); emptyView = findViewById(R.id.empty_view); searchInput = findViewById(R.id.search_input); // ✅ 初始化成员变量 goToBtn.setEnabled(false); adapter = new ResultAdapter(poiList, this::onPoiItemSelected); resultListView.setLayoutManager(new LinearLayoutManager(this)); resultListView.setAdapter(adapter); resultListView.setVisibility(View.GONE); emptyView.setVisibility(View.GONE); // 修改:传递真实定位点 goToBtn.setOnClickListener(v -> { if (selectedMarker == null) { Toast.makeText(this, "请先选择一个位置", Toast.LENGTH_SHORT).show(); return; } LatLng targetPos = selectedMarker.getPosition(); // 确保定位已完成 if (!isLocationReady) { Toast.makeText(this, "正在获取您的位置,请稍后再试", Toast.LENGTH_SHORT).show(); return; } Intent intent = new Intent(SearchResultActivity.this, RoutePlanActivity.class); intent.putExtra("start_lat", myCurrentLat); intent.putExtra("start_lng", myCurrentLng); intent.putExtra("target_lat", targetPos.latitude); intent.putExtra("target_lng", targetPos.longitude); intent.putExtra(RoutePlanActivity.EXTRA_SOURCE, RoutePlanActivity.SOURCE_FROM_SEARCH_RESULT); startActivity(intent); finish(); }); } private void setupMap(Bundle savedInstanceState) { mapView = findViewById(R.id.map_view); 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(); } }); } } 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); aMap.setOnMapClickListener(latLng -> { onCustomLocationSelected(latLng); }); // 初始视野:中国中心 new Handler(Looper.getMainLooper()).post(() -> aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(35.8617, 104.1954), 4f)) ); enableMyLocationLayer(); } private void enableMyLocationLayer() { if (aMap == null) return; if (ContextCompat.checkSelfPermission(this, 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()); // 记录真实“我的位置” myCurrentLat = location.getLatitude(); myCurrentLng = location.getLongitude(); isLocationReady = true; // ✅ 先居中地图 aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 16f), 500, null); 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(); } // ✅ 关键修改:延迟 800ms 再触发搜索,给地图留出渲染时间 new Handler(Looper.getMainLooper()).postDelayed(() -> { if (pendingKeyword != null && !pendingKeyword.isEmpty()) { performSearchWithKeyword(pendingKeyword); } }, 800); // 防止重复触发 aMap.setOnMyLocationChangeListener(null); } }; aMap.setOnMyLocationChangeListener(listener); } else { 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) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { enableMyLocationLayer(); } else { Toast.makeText(this, "定位权限被拒绝,部分功能受限", Toast.LENGTH_LONG).show(); currentCity = "全国"; } } } private void setupSearchSuggestion() { try { inputTips = new Inputtips(this, new Inputtips.InputtipsListener() { @Override public void onGetInputtips(List<Tip> tipList, int rCode) { if (rCode == 1000 && tipList != null && !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<>( SearchResultActivity.this, android.R.layout.simple_dropdown_item_1line, arr ); searchInput.setAdapter(adapter); searchInput.showDropDown(); } } }); } catch (Exception e) { e.printStackTrace(); } Handler handler = new Handler(Looper.getMainLooper()); Runnable[] pendingRunnable = {null}; searchInput.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 (pendingRunnable[0] != null) { handler.removeCallbacks(pendingRunnable[0]); } // ✅ 输入为空 → 清空下拉框 if (s.length() == 0) { searchInput.setAdapter(null); return; } // ✅ 【核心修改】不再等待,直接发起请求 try { inputTips.requestInputtips(s.toString(), currentCity); } catch (Exception e) { e.printStackTrace(); } } @Override public void afterTextChanged(Editable s) {} }); searchInput.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { searchBtn.performClick(); return true; } return false; }); searchBtn.setOnClickListener(v -> { String keyword = searchInput.getText().toString().trim(); if (!keyword.isEmpty()) { performSearch(keyword); } else { Toast.makeText(this, "请输入关键词", Toast.LENGTH_SHORT).show(); } }); } // ✅ 修改:先设置文本,再模拟点击按钮(等价于用户操作) private void performSearchWithKeyword(String keyword) { searchInput.setText(keyword); searchInput.clearFocus(); searchBtn.performClick(); // ✅ 触发完整 UI 搜索流程 } private void performSearch(String keyword) { emptyView.setText("🔍 搜索中..."); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); PoiSearch.Query query = new PoiSearch.Query(keyword, "", currentCity); query.setPageSize(20); query.setPageNum(0); if (isLocationReady) { LatLonPoint lp = new LatLonPoint(myCurrentLat, myCurrentLng); query.setLocation(lp); // 告诉高德“我在哪”,让它自行加权 } try { poiSearch = new PoiSearch(this, query); poiSearch.setOnPoiSearchListener(this); poiSearch.searchPOIAsyn(); } catch (Exception e) { Toast.makeText(this, "搜索失败", Toast.LENGTH_SHORT).show(); emptyView.setText("⚠️ 未找到相关地点"); } } private void onPoiItemSelected(PoiItem item) { LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); if (selectedMarker != null) { selectedMarker.remove(); selectedMarker = null; } selectedMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("终点:" + item.getTitle()) .icon(com.amap.api.maps.model.BitmapDescriptorFactory.defaultMarker( com.amap.api.maps.model.BitmapDescriptorFactory.HUE_RED))); aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); goToBtn.setEnabled(true); } private void onCustomLocationSelected(LatLng latLng) { if (selectedMarker != null) { selectedMarker.remove(); } selectedMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("终点:自定义位置") .icon(com.amap.api.maps.model.BitmapDescriptorFactory.defaultMarker( com.amap.api.maps.model.BitmapDescriptorFactory.HUE_RED))); goToBtn.setEnabled(true); } @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { List<PoiItem> newPoiList = result.getPois(); poiList.clear(); poiList.addAll(newPoiList); adapter.notifyDataSetChanged(); resultListView.scrollToPosition(0); resultListView.setVisibility(View.VISIBLE); emptyView.setVisibility(View.GONE); // 先设置 Adapter 的选中状态 adapter.setSelected(0); // 再执行业务逻辑 onPoiItemSelected(poiList.get(0)); } else { emptyView.setText("⚠️ 未找到相关地点"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) { // 必须实现 } @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (rCode == 1000 && result != null && result.getRegeocodeAddress() != null) { String city = result.getRegeocodeAddress().getCity(); currentCity = (city != null && !city.isEmpty()) ? city : result.getRegeocodeAddress().getProvince(); } else { currentCity = "全国"; } } @Override public void onGeocodeSearched(com.amap.api.services.geocoder.GeocodeResult geocodeResult, int i) { // 忽略 } @Override protected void onResume() { super.onResume(); mapView.onResume(); } @Override protected void onPause() { super.onPause(); mapView.onPause(); } @Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); inputTips = null; geocodeSearch = null; } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } } 那我能否全都改用 PoiSearch 替代 InputTips,仅修改这个内容,最小量修改即可,代码中的其它内容保持原来不变。当然,能否尽量让POISearch弹出提示的速度更快一些?而且确保修改后的功能是不变的,在搜索过程中也是能够弹出提示,如果可以的话请你给我提供修改好的完整代码
最新发布
12-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值