go处理无key的jsonArray记录

本文介绍了一个使用Go语言处理JSON数据的示例程序。该程序演示了如何解析包含浮点数数组的JSON字符串,并将其转换为Go语言的数据结构进行进一步处理。

示例

package main

import (
    "encoding/json"
	"fmt"
	"github.com/bitly/go-simplejson"
	"reflect"
)

func main() {
	s := `[[11.11, 22.22]]`
	// 变量初始化
	j, _ := simplejson.NewJson([]byte(s))
	i := j.GetIndex(0).Interface()
	fmt.Printf("初始化arr 值%v  类型%T", i, i)

	//通过断言,先转换成可以循环的[]interface{}
	for _, elem := range i.([]interface{}) {
		fmt.Printf("元素的值是%v,反射类型是%v, ", elem, reflect.TypeOf(elem))
		val, err := elem.(json.Number).Float64()
		if err != nil {
			panic(err)
		}
		fmt.Printf("转换为浮点数后,值%v 类型%T \n", val, val)
	}
}

参考连接:https://segmentfault.com/q/1010000017175550

那你现在看看代码,有没有照着你总结的最终方案修改,然后看还有没有需要优化的建议,以下是代码 首先是java代码:package com.hghg.rbbj; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; import android.util.Log; import android.view.View; import android.webkit.JavascriptInterface; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.regex.Pattern; import org.json.JSONArray; import org.json.JSONObject; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private WebView mWebView; private TextToSpeech tts; private boolean isSpeaking = false; private boolean isPaused = false; private int currentSentenceIndex = 0; private int currentCharIndex = 0; private String[] sentences; private String fullOriginalText; private List<NodeInfo> nodeInfoList = new ArrayList<>(); private final Handler handler = new Handler(); private SharedPreferences sharedPreferences; private boolean isTTSInitialized = false; private int[][] sentencePositions; // 在类顶部声明 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sharedPreferences = getSharedPreferences("TTSPreferences", MODE_PRIVATE); loadProgress(); initTextToSpeech(); setupWebView(); loadWebContent("file:///android_asset/index.html"); setupButtons(); } private void setupWebView() { mWebView = findViewById(R.id.mWebView); WebSettings settings = mWebView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setAllowFileAccess(true); settings.setAllowContentAccess(true); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { settings.setAllowUniversalAccessFromFileURLs(true); } mWebView.addJavascriptInterface(new TextNodeInterface(), "TextNodeInterface"); mWebView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { // 注入高亮工具函数 injectHighlightHelper(); // 延迟采集文本节点 handler.postDelayed(() -> collectTextNodes(), 1000); } }); } private void injectHighlightHelper() { String helperJs = "(function() {" + "if (window.createHighlightAtNodeByText) return;" + // 防止重复注入 "window.createHighlightAtNodeByText = function(targetText, localStart, localEnd) {" + " var walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null);" + " var node;" + " while (node = walker.nextNode()) {" + " if (node.nodeType !== Node.TEXT_NODE || !node.parentNode || node.parentNode.nodeType === Node.ELEMENT_NODE && ['SCRIPT','STYLE'].includes(node.parentNode.tagName)) continue;" + " if (node.textContent.includes(targetText)) {" + " var text = node.textContent;" + " var before = text.substring(0, localStart);" + " var middle = text.substring(localStart, localEnd);" + " var after = text.substring(localEnd);" + " var parent = node.parentNode;" + " parent.innerHTML = '';" + " parent.appendChild(document.createTextNode(before));" + " var mark = document.createElement('mark');" + " mark.className = 'highlight';" + " mark.style.backgroundColor = 'yellow';" + " mark.style.color = 'black';" + " mark.style.padding = '2px';" + // 或者添加类名以便 CSS 控制 " mark.classList.add('tts-highlight');"+ " mark.textContent = middle;" + " parent.appendChild(mark);" + " parent.appendChild(document.createTextNode(after));" + " return;" + " }" + " }" + "};" + "})();"; mWebView.evaluateJavascript(helperJs, null); } private void collectTextNodes() { loadJsFromAssets("collect_text_nodes.js"); // 确保这个JS存在assets中 } private void loadJsFromAssets(String fileName) { try { InputStream is = getAssets().open(fileName); byte[] buffer = new byte[is.available()]; is.read(buffer); is.close(); String jsCode = new String(buffer, StandardCharsets.UTF_8); mWebView.evaluateJavascript(jsCode, null); } catch (Exception e) { Log.e(TAG, "加载JS失败: " + fileName, e); Toast.makeText(this, "脚本加载失败", Toast.LENGTH_SHORT).show(); } } private void splitSentences(String rawText) { String filteredText = rawText.replaceAll("[\\r\\n]+", "\n").trim(); List<String> sentenceList = new ArrayList<>(); List<int[]> positionList = new ArrayList<>(); // 存储 [start, end] String[] paragraphs = filteredText.split("\\n\\s*\\n|\\r\\n\\s*\\r\\n"); Pattern sentenceEnd = Pattern.compile("[。.!??!]"); int globalPos = 0; for (String para : paragraphs) { para = para.trim(); if (para.isEmpty()) { globalPos += 1; // 跳过段落分隔符 continue; } int last = 0; java.util.regex.Matcher matcher = sentenceEnd.matcher(para); while (matcher.find()) { String sentence = para.substring(last, matcher.end()).trim(); if (!sentence.isEmpty()) { int startInFull = globalPos + last; int endInFull = globalPos + matcher.end(); sentenceList.add(sentence); positionList.add(new int[]{startInFull, endInFull}); } last = matcher.end(); } if (last < para.length()) { String remaining = para.substring(last).trim(); if (!remaining.isEmpty()) { int startInFull = globalPos + last; int endInFull = globalPos + para.length(); sentenceList.add(remaining); positionList.add(new int[]{startInFull, endInFull}); } } globalPos += para.length() + 1; // 包括换行符占位 } sentences = sentenceList.toArray(new String[0]); sentencePositions = positionList.toArray(new int[0][]); // 新增字段 Log.d(TAG, "共分割出 " + sentences.length + " 个句子"); } private void highlightInOriginalText(int globalStart, int globalEnd) { Log.d(TAG, "高亮范围: [" + globalStart + ", " + globalEnd + ")"); if (fullOriginalText == null || globalStart >= globalEnd || globalEnd > fullOriginalText.length()) { return; } List<HighlightRange> ranges = findRangesInNodes(globalStart, globalEnd); if (ranges.isEmpty()) { Log.w(TAG, "未找到匹配节点进行高亮"); return; } StringBuilder js = new StringBuilder(); js.append("(function() {"); js.append(" var oldMarks = document.querySelectorAll('mark.highlight');") .append(" oldMarks.forEach(function(m) {") .append(" var p = m.parentNode;") .append(" p.replaceChild(document.createTextNode(m.textContent), m);") .append(" p.normalize();") .append(" });"); for (HighlightRange range : ranges) { String escapedText = escapeJsString(range.node.text.substring(range.startInNode, range.endInNode)); js.append(String.format( "createHighlightAtNodeByText('%s', %d, %d);", escapedText, range.startInNode, range.endInNode )); } js.append(" setTimeout(function(){") .append(" var h = document.querySelector('mark.highlight');") .append(" if(h)h.scrollIntoView({behavior:'smooth',block:'center'});") .append(" }, 50);"); js.append("})();"); mWebView.evaluateJavascript(js.toString(), null); } private List<HighlightRange> findRangesInNodes(int start, int end) { List<HighlightRange> result = new ArrayList<>(); for (NodeInfo node : nodeInfoList) { int ns = node.start; int ne = node.start + node.text.length(); if (end <= ns || start >= ne) continue; int ls = Math.max(0, start - ns); int le = Math.min(node.text.length(), end - ns); if (ls < le) { result.add(new HighlightRange(node, ls, le)); } } return result; } private String escapeJsString(String s) { return s.replace("\\", "\\\\") .replace("'", "\\'") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } private void speakFromCurrentPosition() { if (sentences == null || currentSentenceIndex >= sentences.length) { stopSpeaking(); return; } String sentence = sentences[currentSentenceIndex].trim(); if (sentence.isEmpty()) { currentSentenceIndex++; handler.postDelayed(this::speakFromCurrentPosition, 50); return; } // ✅ 直接使用预计算的位置,不再依赖 indexOf int[] range = sentencePositions[currentSentenceIndex]; int highlightStart = range[0] + currentCharIndex; int highlightEnd = range[1]; // 注意:end 是闭区间之后的位置 highlightInOriginalText(highlightStart, highlightEnd); String toSpeak = sentence.substring(currentCharIndex); Bundle params = new Bundle(); String utteranceId = UUID.randomUUID().toString(); params.putCharSequence(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, utteranceId); int status = tts.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, params, utteranceId); if (status == TextToSpeech.ERROR) { Log.e(TAG, "TTS 播放错误"); } } private void setupButtons() { Button speakButton = findViewById(R.id.speakButton); speakButton.setOnClickListener(v -> { if (isPaused && currentSentenceIndex < sentences.length) { isPaused = false; isSpeaking = true; speakFromCurrentPosition(); } else { startSpeaking(); } }); Button pauseButton = findViewById(R.id.pauseButton); pauseButton.setOnClickListener(v -> { if (isSpeaking) { tts.stop(); isSpeaking = false; isPaused = true; } }); Button stopButton = findViewById(R.id.stopButton); stopButton.setOnClickListener(v -> stopSpeaking()); } private void startSpeaking() { if (sentences == null || sentences.length == 0) { Toast.makeText(this, "尚未加载文本内容", Toast.LENGTH_SHORT).show(); return; } isSpeaking = true; isPaused = false; currentCharIndex = 0; speakFromCurrentPosition(); } private void stopSpeaking() { if (tts != null) { tts.stop(); } isSpeaking = false; isPaused = false; currentCharIndex = 0; saveProgress(); removeHighlight(); } private void removeHighlight() { mWebView.evaluateJavascript("(function(){" + "var ms=document.querySelectorAll('mark.highlight');" + "ms.forEach(m=>{m.parentNode.replaceChild(document.createTextNode(m.textContent),m);});" + "})()", null); } private void saveProgress() { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putInt("currentSentenceIndex", currentSentenceIndex); editor.putInt("currentCharIndex", currentCharIndex); editor.apply(); } private void loadProgress() { currentSentenceIndex = sharedPreferences.getInt("currentSentenceIndex", 0); currentCharIndex = sharedPreferences.getInt("currentCharIndex", 0); } private void initTextToSpeech() { tts = new TextToSpeech(this, status -> { if (status == TextToSpeech.SUCCESS) { isTTSInitialized = true; setUtteranceProgressListener(); } else { Toast.makeText(this, "TTS初始化失败", Toast.LENGTH_SHORT).show(); } }); } private void setUtteranceProgressListener() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { tts.setOnUtteranceProgressListener(new UtteranceProgressListener() { @Override public void onStart(String utteranceId) {} @Override public void onDone(String utteranceId) { handler.post(() -> { currentSentenceIndex++; currentCharIndex = 0; if (currentSentenceIndex < sentences.length) { speakFromCurrentPosition(); } else { isSpeaking = false; saveProgress(); Toast.makeText(MainActivity.this, "朗读完成", Toast.LENGTH_SHORT).show(); } }); } @Override public void onError(String utteranceId) {} }); } } @Override protected void onDestroy() { if (tts != null) { tts.stop(); tts.shutdown(); } super.onDestroy(); } // --- 数据类 --- public static class NodeInfo { String text; int start; NodeInfo(String text, int start) { this.text = text; this.start = start; } } public static class HighlightRange { NodeInfo node; int startInNode, endInNode; HighlightRange(NodeInfo node, int start, int end) { this.node = node; this.startInNode = start; this.endInNode = end; } } // --- JS 接口 --- public class TextNodeInterface { @JavascriptInterface public void onTextNodesCollected(String fullText, String nodeInfoJson) { runOnUiThread(() -> { if (fullText == null || fullText.trim().isEmpty()) { Toast.makeText(MainActivity.this, "未提取到有效文本", Toast.LENGTH_SHORT).show(); return; } fullOriginalText = fullText; nodeInfoList.clear(); try { JSONArray arr = new JSONArray(nodeInfoJson); for (int i = 0; i < arr.length(); i++) { JSONObject obj = arr.getJSONObject(i); String text = obj.getString("text"); int start = obj.getInt("start"); nodeInfoList.add(new NodeInfo(text, start)); } Log.d(TAG, "✅ 成功解析 " + nodeInfoList.size() + " 个文本节点"); } catch (Exception e) { Log.e(TAG, "解析 nodeInfo 失败", e); Toast.makeText(MainActivity.this, "结构解析失败", Toast.LENGTH_SHORT).show(); return; } splitSentences(fullOriginalText); }); } } private void loadWebContent(String url) { mWebView.loadUrl(url); } @Override public void onBackPressed() { if (mWebView.canGoBack()) { mWebView.goBack(); } else { super.onBackPressed(); } }} 接着是 js代码:// collect_text_nodes.js (function() { var textNodes = []; var fullText = ''; function traverse(node) { if (node.nodeType === Node.TEXT_NODE) { var text = node.textContent; if (text && text.trim().length > 0) { textNodes.push({ text: text, start: fullText.length }); fullText += text; } } else if (node.nodeType === Node.ELEMENT_NODE) { var tag = node.tagName.toLowerCase(); if (tag !== 'script' && tag !== 'style' && tag !== 'noscript') { for (var i = 0; i < node.childNodes.length; i++) { traverse(node.childNodes[i]); } } } } // 更丰富的主内容区域探测 var containerSelectors = [ '#content', '.content', '#main', '.main', '.article', '.post-body', '[role="main"]', 'main', '.entry-content' ]; var container = null; for (var i = 0; i < containerSelectors.length; i++) { var el = document.querySelector(containerSelectors[i]); if (el) { container = el; break; } } if (!container) container = document.body; traverse(container); // 处理 iframe(仅同源) var iframes = document.getElementsByTagName('iframe'); for (var i = 0; i < iframes.length; i++) { try { var iframeWin = iframes[i].contentWindow; var iframeDoc = iframes[i].contentDocument || iframeWin.document; if (iframeDoc && iframeDoc.body) { traverse(iframeDoc.body); } } catch (e) { console.warn("无法访问 iframe (跨域?):", iframes[i].src); } } // 回调 Java try { if (typeof window.TextNodeInterface !== 'undefined') { window.TextNodeInterface.onTextNodesCollected(fullText, JSON.stringify(textNodes)); } else { console.error("❌ 未注册 TextNodeInterface,请检查 addJavascriptInterface"); } } catch (e) { console.error("❌ 回传数据失败:", e); } console.log("✅ collect_text_nodes.js 执行完成,文本节点数:", textNodes.length, ", 总长度:", fullText.length); })();
10-15
好的,现在我的进度如下: 1、目前已经创建了CityManager:package com.example.bus; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import org.json.JSONArray; import org.json.JSONObject; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashSet; import java.util.Scanner; import java.util.Set; import java.util.TreeSet; public class CityManager { private static Set FULL_CITY_SET = new HashSet<>(); /** * 初始化:异步加载全国城市列表 */ public static void init(Context context, Runnable onReady) { if (!FULL_CITY_SET.isEmpty()) { onReady.run(); return; } new Thread(() -> { try { String urlStr = "https://restapi.amap.com/v3/config/district?" + "keywords=中国&subdistrict=2&key=你的高德KEY&extensions=base"; URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(8000); conn.setReadTimeout(8000); InputStream is = conn.getInputStream(); Scanner scanner = new Scanner(is, "UTF-8").useDelimiter("\\A"); String response = scanner.hasNext() ? scanner.next() : ""; parseAndLoadCities(response); saveToCache(context, response); // 可选:缓存到本地 } catch (Exception e) { Log.w("CityManager", "⚠️ 在线加载失败,尝试使用缓存", e); String cached = readFromCache(context); if (!cached.isEmpty()) { parseAndLoadCities(cached); } // 即使失败也继续(fallback 到空集合) } finally { if (onReady != null) { onReady.run(); } } }).start(); } private static void parseAndLoadCities(String json) { try { JSONObject root = new JSONObject(json); JSONArray districts = root.getJSONArray("districts"); if (districts.length() == 0) return; JSONArray provinces = districts.getJSONObject(0).getJSONArray("districts"); for (int i = 0; i < provinces.length(); i++) { JSONObject province = provinces.getJSONObject(i); String level = province.getString("level"); String name = province.getString("name"); // 地级市:"city" 级别 if ("city".equals(level)) { FULL_CITY_SET.add(name); // 如 “广州市” } // 直辖市本身就是 city 级别,但可能叫 “北京市”,已包含 // 特殊处理:某些“自治州”、“地区”也可以考虑加入,但先忽略 // 子区域递归(有些市下还有市辖区,但我们只关心地级市) if (province.has("districts")) { JSONArray children = province.getJSONArray("districts"); for (int j = 0; j < children.length(); j++) { JSONObject child = children.getJSONObject(j); if ("city".equals(child.getString("level"))) { FULL_CITY_SET.add(child.getString("name")); } } } } Log.d("CityManager", "✅ 成功加载 " + FULL_CITY_SET.size() + " 个地级及以上城市"); } catch (Exception e) { Log.e("CityManager", "解析城市数据失败", e); } } /** * 智能解析输入:仅当前缀或后缀为完整城市名时才提取 */ public static ParsedQuery parse(String input) { if (input == null || input.length() < 3) { return new ParsedQuery(input, ""); } String cleaned = input.trim(); // 检查前缀匹配(最长优先) for (String city : new TreeSet<>(FULL_CITY_SET)) { // 按长度排序更好?见优化建议 if (cleaned.startsWith(city)) { return new ParsedQuery(cleaned.substring(city.length()).trim(), city); } } // 检查后缀匹配 for (String city : new TreeSet<>(FULL_CITY_SET)) { if (cleaned.endsWith(city)) { int endIndex = cleaned.length() - city.length(); return new ParsedQuery(cleaned.substring(0, endIndex).trim(), city); } } return new ParsedQuery(cleaned, ""); } // ================= 缓存相关方法(可选)================= private static void saveToCache(Context context, String json) { try { SharedPreferences sp = context.getSharedPreferences("city_cache", Context.MODE_PRIVATE); sp.edit().putString("last_data", json).apply(); } catch (Exception ignored) {} } private static String readFromCache(Context context) { try { SharedPreferences sp = context.getSharedPreferences("city_cache", Context.MODE_PRIVATE); return sp.getString("last_data", ""); } catch (Exception ignored) { return ""; } } public static class ParsedQuery { public final String keyword; public final String targetCity; public ParsedQuery(String keyword, String targetCity) { this.keyword = keyword; this.targetCity = targetCity; } } } 2、目前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.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.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.PoiIntelligenceUtils; 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.List; import java.util.stream.Collectors; import com.amap.api.maps.model.BitmapDescriptorFactory; 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; 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( 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.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); 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> rawList = result.getPois(); // ✅ 是否为泛词? boolean isGeneric = PoiIntelligenceUtils.isGenericCategory(rawList); if (!isGeneric) { // ❌ 非泛词 → 直接显示,不动排序! updateResultList(rawList); return; } // ✅ 泛词 → 尝试发起 nearby 搜索 if (isLocationReady) { LatLonPoint center = new LatLonPoint(myCurrentLat, myCurrentLng); String keyword = getCurrentKeyword(); 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()) { List<PoiItem> sorted = sortByDistance(res.getPois(), myCurrentLat, myCurrentLng); updateResultList(sorted); } else { // fallback List<PoiItem> fallback = sortByDistance(rawList, myCurrentLat, myCurrentLng); updateResultList(fallback); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nearbySearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); List<PoiItem> fallback = sortByDistance(rawList, myCurrentLat, myCurrentLng); updateResultList(fallback); } } else { // 泛词但无定位 → 直接展示原始结果(不排序) updateResultList(rawList); } } 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(View.GONE); binding.resultList.setVisibility(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(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 (rCode == 1000 && result != null && result.getRegeocodeAddress() != null) { String city = result.getRegeocodeAddress().getCity(); currentCity = (city != null && !city.isEmpty()) ? city : result.getRegeocodeAddress().getProvince(); if (activeSuggestHelper != null) { activeSuggestHelper.setCurrentCity(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(); geocodeSearch = null; binding = null; } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } // ✅ 修改 RealTimePoiSuggestHelper 来支持距离排序建议 private void setupSearchSuggestion() { // 创建 suggestHelper RealTimePoiSuggestHelper suggestHelper = new RealTimePoiSuggestHelper(requireContext()); suggestHelper.setCurrentCity(currentCity); activeSuggestHelper = suggestHelper; // 缓存引用 suggestHelper.setCallback(suggestions -> { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( requireContext(), android.R.layout.simple_dropdown_item_1line, suggestions ); new Handler(Looper.getMainLooper()).post(() -> { 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(); } }); } }); // 注意:location bias 需要在定位完成后设置! if (isLocationReady) { suggestHelper.setLocationBias(myCurrentLat, myCurrentLng); } Handler handler = new Handler(Looper.getMainLooper()); Runnable[] pending1 = {null}, pending2 = {null}; binding.mapInput1.addTextChangedListener(new SimpleTextWatcher(s -> { if (pending1[0] != null) handler.removeCallbacks(pending1[0]); if (s.length() == 0) { binding.mapInput1.setAdapter(null); return; } pending1[0] = () -> suggestHelper.requestSuggestions(s.toString()); handler.postDelayed(pending1[0], 300); })); binding.mapInput2.addTextChangedListener(new SimpleTextWatcher(s -> { if (pending2[0] != null) handler.removeCallbacks(pending2[0]); if (s.length() == 0) { binding.mapInput2.setAdapter(null); return; } pending2[0] = () -> suggestHelper.requestSuggestions(s.toString()); handler.postDelayed(pending2[0], 300); })); // Enter 键搜索 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(); } 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); } } } 3、目前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.text.Editable; import android.text.TextWatcher; import android.view.View; import android.view.inputmethod.EditorInfo; import android.widget.ArrayAdapter; 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 java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; 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 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; // ✅ 实时建议助手(延迟初始化) private RealTimePoiSuggestHelper suggestHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_result); initViews(); setupMap(savedInstanceState); try { geocodeSearch = new GeocodeSearch(this); geocodeSearch.setOnGeocodeSearchListener(this); } catch (Exception e) { e.printStackTrace(); } pendingKeyword = getIntent().getStringExtra("keyword"); // ✅ 初始化 suggestHelper,但暂不设 location bias suggestHelper = new RealTimePoiSuggestHelper(this); suggestHelper.setCurrentCity(currentCity); suggestHelper.setCallback(suggestions -> { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( this, android.R.layout.simple_dropdown_item_1line, suggestions ); new Handler(Looper.getMainLooper()).post(() -> { searchInput.setAdapter(adapter); if (getCurrentFocus() == searchInput) { searchInput.showDropDown(); } }); } }); } 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); // 初始视野:中国中心 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; // 更新 suggestHelper 的 location bias suggestHelper.setLocationBias(myCurrentLat, myCurrentLng); // ✅ 先居中地图 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(); } } } private void setupSearchSuggestion() { 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; } pendingRunnable[0] = () -> suggestHelper.requestSuggestions(s.toString()); handler.postDelayed(pendingRunnable[0], 300); } @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); 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); } // ✅ 工具方法:转换 LatLonPoint → LatLng 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(Comparator.comparingDouble(poi -> com.amap.api.maps.AMapUtils.calculateLineDistance(toLatLng(poi.getLatLonPoint()), me))) .collect(Collectors.toList()); } @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { List<PoiItem> rawList = result.getPois(); boolean isGeneric = PoiIntelligenceUtils.isGenericCategory(rawList); if (!isGeneric) { updateResultList(rawList); return; } if (isLocationReady) { LatLonPoint center = new LatLonPoint(myCurrentLat, myCurrentLng); PoiSearch.Query query = new PoiSearch.Query(searchInput.getText().toString().trim(), "", ""); query.setPageSize(20); try { PoiSearch nearbySearch = new PoiSearch(this, 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()) { List<PoiItem> sorted = sortByDistance(res.getPois(), myCurrentLat, myCurrentLng); updateResultList(sorted); } else { List<PoiItem> fallback = sortByDistance(rawList, myCurrentLat, myCurrentLng); updateResultList(fallback); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nearbySearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); List<PoiItem> fallback = sortByDistance(rawList, myCurrentLat, myCurrentLng); updateResultList(fallback); } } else { updateResultList(rawList); } } else { emptyView.setText("⚠️ 未找到相关地点"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } // ✅ 统一更新方法 private void updateResultList(List<PoiItem> list) { poiList.clear(); poiList.addAll(list); adapter.notifyDataSetChanged(); resultListView.scrollToPosition(0); emptyView.setVisibility(View.GONE); resultListView.setVisibility(View.VISIBLE); if (!list.isEmpty()) { adapter.setSelected(0); onPoiItemSelected(list.get(0)); } } @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(); // 同步给 suggestHelper suggestHelper.setCurrentCity(currentCity); } } @Override public void onGeocodeSearched(com.amap.api.services.geocoder.GeocodeResult geocodeResult, int i) { // 忽略 } @Override protected void onResume() { super.onResume(); mapView.onResume(); setupSearchSuggestion(); // 重新绑定监听器 } @Override protected void onPause() { super.onPause(); mapView.onPause(); } @Override protected void onDestroy() { super.onDestroy(); mapView.onDestroy(); geocodeSearch = null; } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } } 4、目前RealTimePoiSuggestHelper代码如下:package com.example.bus; import android.content.Context; import android.os.Handler; import android.os.Looper; import androidx.annotation.NonNull; import com.amap.api.maps.AMapUtils; import com.amap.api.maps.model.LatLng; 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.poisearch.PoiSearch.OnPoiSearchListener; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class RealTimePoiSuggestHelper implements OnPoiSearchListener { private final Context context; private final Handler mainHandler = new Handler(Looper.getMainLooper()); private PoiSearch poiSearch; private SuggestionCallback callback; private String currentCity = “”; private double lat = 0, lng = 0; private boolean useLocationBias = false; private boolean useDistanceSort = false; // 是否按距离排序建议 public interface SuggestionCallback { void onSuggestionsReady(String[] suggestions); } public RealTimePoiSuggestHelper(Context context) { this.context = context; } public void setCurrentCity(String city) { this.currentCity = city; } public void setLocationBias(double lat, double lng) { this.lat = lat; this.lng = lng; this.useLocationBias = true; } public void setUseDistanceSort(boolean use) { this.useDistanceSort = use; } public void setCallback(SuggestionCallback callback) { this.callback = callback; } public void requestSuggestions(String keyword) { if (keyword.isEmpty() || callback == null) return; PoiSearch.Query query = new PoiSearch.Query(keyword, "", currentCity); query.setPageSize(20); query.requireSubPois(false); if (useLocationBias) { LatLonPoint lp = new LatLonPoint(lat, lng); query.setLocation(lp); } try { poiSearch = new PoiSearch(context, query); poiSearch.setOnPoiSearchListener(this); poiSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); notifyEmpty(); } } @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<PoiItem> pois = result.getPois(); List<String> names; if (useDistanceSort && useLocationBias) { // ✅ 正确做法:将 LatLonPoint 转为 LatLng 再计算距离 LatLng me = new LatLng(lat, lng); names = pois.stream() .sorted((a, b) -> { LatLng pointA = toLatLng(a.getLatLonPoint()); LatLng pointB = toLatLng(b.getLatLonPoint()); double distA = AMapUtils.calculateLineDistance(pointA, me); double distB = AMapUtils.calculateLineDistance(pointB, me); return Double.compare(distA, distB); }) .map(PoiItem::getTitle) .collect(Collectors.toList()); } else { names = new ArrayList<>(); for (PoiItem item : pois) { names.add(item.getTitle()); } } String[] arr = names.toArray(new String[0]); notifySuccess(arr); } else { notifyEmpty(); } } @Override public void onPoiItemSearched(com.amap.api.services.core.PoiItem item, int rCode) { // 忽略 } // ✅ 新增工具方法:将 LatLonPoint 转换为 LatLng private LatLng toLatLng(LatLonPoint point) { if (point == null) return null; return new LatLng(point.getLatitude(), point.getLongitude()); } private void notifySuccess(@NonNull String[] suggestions) { mainHandler.post(() -> callback.onSuggestionsReady(suggestions)); } private void notifyEmpty() { mainHandler.post(() -> callback.onSuggestionsReady(new String[0])); } } 5、目前AndroidManifest.xml代码如下: <!-- 必需权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 如需后台定位再开启 --> <!-- <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> --> <application android:name=".MyApplication" android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Bus" tools:replace="android:allowBackup,android:label"> <!-- 高德地图 API Key --> <meta-data android:name="com.amap.api.v2.apikey" android:value="a487b8aa9be4ecdcb101a04a9eecff8a" /> <!-- 高德定位核心服务 --> <service android:name="com.amap.api.location.APSService" android:enabled="true" android:exported="false" android:process=":maps" /> <!-- Activities --> <activity android:name=".SearchResultActivity" android:exported="true" android:parentActivityName=".MainActivity"/> <activity android:name=".RoutePlanActivity" android:exported="true" android:parentActivityName=".MainActivity"/> <activity android:name=".AboutActivity" android:exported="true" android:parentActivityName=".MainActivity" /> <activity android:name=".SurveyActivity" android:label="用户调研" android:parentActivityName=".MainActivity"> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity" /> </activity> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 那接下来我需要做什么?我现在需要1、搜索结果不加城市前缀即优先搜索本市结果,再搜索全国结果,即使本市有结果也不要漏全国结果,比如我在深圳搜索中山大学,他在深圳能搜的到但是不能漏了广州的结果,一定要把本市和全国所有相关结果都展示出来。2、加了城市前缀或后缀只搜索那个城市的结果,不搜索其他城市的结果。这个前缀一定要完整输入XX市,没有市这个字无效。同理,如果是添加XX省或者是添加XX市XX区等等道理都一样。3、这个逻辑只适用非泛词搜索,目前的泛词已有的逻辑不要改变,只要没有被判断出泛词都按照这样的逻辑进行搜索。4、不管是搜索提示框还是搜索结果都要有这样的逻辑。5、代码只做最小量修改,框架不要改变,其它功能也不要改变。6、为我提供修改好的完整代码,所有的都从import开始提供完整代码.7、如果我还有其它准备工作没有做到位请你告诉我,比如AndroidManifest.xml这些,你修改代码后改不到的地方
最新发布
12-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值