java 中你可能忽略或者还不是很明白的== 和 .equals()

本文详细解释了Java中如何正确比较String对象。通过对比==运算符与.equals方法的区别,阐述了String对象引用与内容比较的不同场景及注意事项。
equals()是object的一个方法,用来比较两个对象的地址,但string和Integer重写了这个方法,所以这两个类的equals()方法是比较的两个对象的内容,至于其他的类,只要没重写equals方法的肯定还是继承的object类里的equals方法,那就是比较地址,即使对象内容一样,返回的还是false
 
 

1.10  “==”不等于“.equals”

这里举出一个Java编程程序员经常碰到的问题。例如现在是凌晨3点,在你喝完第4杯咖啡后,你设法找到正确的逻辑来解决复杂的编程问题。到目前,你几乎不能思考String和Object引用,因为你已经昏昏欲睡了。然后糟糕的事情发生了……不,并不是Java溢出,而是如下所示。

String name = getName();
if (name == "Sleepy") // oops!
{
doSomething();
}

你快速编译并测试代码后,代码似乎正常运行。终于到下班回家休息的时候了!然而,一段时间后,应用程序测试发现了一个间歇性错误,并跟踪到此错误的来源恰好是这段代码。

“怎么会这样?”你可能会愤怒地说,“前几天我还试验过类似的String比较,并且能够正确运行!”。但是,你需要首先重温一下Java对象引用的概念。一个对象变量是一个指向存储在堆内存(heap memory)中实际对象的引用(指针)。当为另一个变量分配一个变量时,事实上分配的是引用而不是实际的对象(如图1-1所示):

String a, b, c, d;
a = "123";
b = a;
c = new String("123");
d = "WCJ";

图 1-1  对象引用

Java中,“==”运算符用来比较两个引用以查看它们是否指向同一个内存对象。而对于String实例,运行时状态会尽可能地确保任意两个具有相同字符信息的String字面值指向同一个内部对象。此过程称为驻留(interning),但是它并不有助于每个String的比较。一个原因是垃圾收集器线程删除了驻留值,另一个原因是String所在的位置可能被一个由String构造函数创建的新实例占用。如果是这样,“==”将总是返回false。
可以设计equals方法来比较两个对象的状态(state)或每个对象的内容。对你自己的类,必须重写此方法来使它正确操作。但是如果使用equals方法,String实例总是能够正确地比较。假定所有的String值是驻留的,下面的代码段说明了此问题:

String name1, name2, name3;
name1 = "123";

name2 = name1; if (name1 == name2) {}         // true if (name1.equals(name2)) {}    // true

name2 = "123"; if (name1 == name2) {}         // usually true if (name1.equals(name2)) {}    // true

name3 = new String("123"); if (name1 == name3) {}         // false if (name1.equals(name3)) {}    // true


注意:

总是使用.equals来比较两个String值,尽管使用“==”运算符看似能够正确操作。对于大多数应用程序而言,即使它能正确运行,但“==”代码事实上是错误的,而只有equals是正确的。

能否别乱添加功能,我的PoiSearch就这些:// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.amap.api.services.poisearch; import android.content.Context; import com.amap.api.col.3nsl.jq; import com.amap.api.col.3nsl.lr; import com.amap.api.services.core.AMapException; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.interfaces.IPoiSearch; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** @deprecated */ public class PoiSearch { public static final String ENGLISH = "en"; public static final String CHINESE = "zh-CN"; public static final String EXTENSIONS_ALL = "all"; public static final String EXTENSIONS_BASE = "base"; private IPoiSearch a = null; public PoiSearch(Context var1, Query var2) throws AMapException { if (this.a == null) { try { this.a = new lr(var1, var2); return; } catch (Exception var3) { var3.printStackTrace(); if (var3 instanceof AMapException) { throw (AMapException)var3; } } } } public void setOnPoiSearchListener(OnPoiSearchListener var1) { if (this.a != null) { this.a.setOnPoiSearchListener(var1); } } /** @deprecated */ public void setLanguage(String var1) { if (this.a != null) { this.a.setLanguage(var1); } } /** @deprecated */ public String getLanguage() { return this.a != null ? this.a.getLanguage() : null; } public PoiResult searchPOI() throws AMapException { return this.a != null ? this.a.searchPOI() : null; } public void searchPOIAsyn() { if (this.a != null) { this.a.searchPOIAsyn(); } } public PoiItem searchPOIId(String var1) throws AMapException { return this.a != null ? this.a.searchPOIId(var1) : null; } public void searchPOIIdAsyn(String var1) { if (this.a != null) { this.a.searchPOIIdAsyn(var1); } } public void setQuery(Query var1) { if (this.a != null) { this.a.setQuery(var1); } } public void setBound(SearchBound var1) { if (this.a != null) { this.a.setBound(var1); } } public Query getQuery() { return this.a != null ? this.a.getQuery() : null; } public SearchBound getBound() { return this.a != null ? this.a.getBound() : null; } private static boolean b(String var0, String var1) { if (var0 == null && var1 == null) { return true; } else { return var0 != null && var1 != null ? var0.equals(var1) : false; } } public static class Query implements Cloneable { private String a; private String b; private String c; private int d; private int e; private String f; private boolean g; private boolean h; private String i; private boolean j; private LatLonPoint k; private boolean l; private Map<String, String> m; private String n; public Query(String var1, String var2) { this(var1, var2, (String)null); } public Query(String var1, String var2, String var3) { this.d = 1; this.e = 20; this.f = "zh-CN"; this.g = false; this.h = false; this.j = true; this.l = true; this.m = new HashMap(); this.n = "base"; this.a = var1; this.b = var2; this.c = var3; } public String getBuilding() { return this.i; } public void setBuilding(String var1) { this.i = var1; } public String getQueryString() { return this.a; } /** @deprecated */ public void setQueryLanguage(String var1) { if ("en".equals(var1)) { this.f = "en"; } else { this.f = "zh-CN"; } } protected String getQueryLanguage() { return this.f; } public String getCategory() { return this.b != null && !this.b.equals("00") && !this.b.equals("00|") ? this.b : a(); } private static String a() { return ""; } public String getCity() { return this.c; } public int getPageNum() { return this.d; } public void setPageNum(int var1) { if (var1 <= 0) { var1 = 1; } this.d = var1; } public void setPageSize(int var1) { if (var1 <= 0) { this.e = 20; } else if (var1 > 30) { this.e = 30; } else { this.e = var1; } } public int getPageSize() { return this.e; } public void setCityLimit(boolean var1) { this.g = var1; } public boolean getCityLimit() { return this.g; } public void requireSubPois(boolean var1) { this.h = var1; } public boolean isRequireSubPois() { return this.h; } public boolean isDistanceSort() { return this.j; } public void setDistanceSort(boolean var1) { this.j = var1; } public LatLonPoint getLocation() { return this.k; } public void setLocation(LatLonPoint var1) { this.k = var1; } public boolean isSpecial() { return this.l; } public void setSpecial(boolean var1) { this.l = var1; } public String getExtensions() { return this.n; } public void setExtensions(String var1) { this.n = var1; } public void setCustomParams(Map<String, String> var1) { if (var1 != null) { this.m.putAll(var1); } } public Map<String, String> getCustomParams() { return this.m; } public boolean queryEquals(Query var1) { if (var1 == null) { return false; } else if (var1 == this) { return true; } else { return PoiSearch.b(var1.a, this.a) && PoiSearch.b(var1.b, this.b) && PoiSearch.b(var1.f, this.f) && PoiSearch.b(var1.c, this.c) && PoiSearch.b(var1.n, this.n) && PoiSearch.b(var1.i, this.i) && var1.g == this.g && var1.e == this.e && var1.j == this.j && var1.m.equals(this.m) && var1.l == this.l; } } public int hashCode() { int var1 = this.a != null ? this.a.hashCode() : 0; var1 = 31 * var1 + (this.b != null ? this.b.hashCode() : 0); var1 = 31 * var1 + (this.c != null ? this.c.hashCode() : 0); var1 = 31 * var1 + this.d; var1 = 31 * var1 + this.e; var1 = 31 * var1 + (this.f != null ? this.f.hashCode() : 0); var1 = 31 * var1 + (this.g ? 1 : 0); var1 = 31 * var1 + (this.h ? 1 : 0); var1 = 31 * var1 + (this.i != null ? this.i.hashCode() : 0); var1 = 31 * var1 + (this.j ? 1 : 0); var1 = 31 * var1 + (this.k != null ? this.k.hashCode() : 0); var1 = 31 * var1 + (this.l ? 1 : 0); var1 = 31 * var1 + (this.m != null ? this.m.hashCode() : 0); return 31 * var1 + (this.n != null ? this.n.hashCode() : 0); } public boolean equals(Object var1) { if (this == var1) { return true; } else if (var1 != null && this.getClass() == var1.getClass()) { var1 = var1; if (this.d != var1.d) { return false; } else if (this.e != var1.e) { return false; } else if (this.g != var1.g) { return false; } else if (this.h != var1.h) { return false; } else if (this.j != var1.j) { return false; } else if (this.l != var1.l) { return false; } else if (!Objects.equals(this.a, var1.a)) { return false; } else if (!Objects.equals(this.b, var1.b)) { return false; } else if (!Objects.equals(this.c, var1.c)) { return false; } else if (!Objects.equals(this.f, var1.f)) { return false; } else if (!Objects.equals(this.i, var1.i)) { return false; } else if (!Objects.equals(this.k, var1.k)) { return false; } else { return !Objects.equals(this.m, var1.m) ? false : Objects.equals(this.n, var1.n); } } else { return false; } } public Query clone() { try { super.clone(); } catch (CloneNotSupportedException var2) { jq.a(var2, "PoiSearch", "queryclone"); } Query var1; (var1 = new Query(this.a, this.b, this.c)).setPageNum(this.d); var1.setPageSize(this.e); var1.setQueryLanguage(this.f); var1.setCityLimit(this.g); var1.requireSubPois(this.h); var1.setBuilding(this.i); var1.setLocation(this.k); var1.setDistanceSort(this.j); var1.setSpecial(this.l); var1.setExtensions(this.n); var1.setCustomParams(this.m); return var1; } } public static class SearchBound implements Cloneable { private LatLonPoint a; private LatLonPoint b; private int c = 1500; private LatLonPoint d; private String e; private boolean f = true; private List<LatLonPoint> g; public static final String BOUND_SHAPE = "Bound"; public static final String POLYGON_SHAPE = "Polygon"; public static final String RECTANGLE_SHAPE = "Rectangle"; public static final String ELLIPSE_SHAPE = "Ellipse"; public SearchBound(LatLonPoint var1, int var2) { this.e = "Bound"; this.c = var2; this.d = var1; } public SearchBound(LatLonPoint var1, int var2, boolean var3) { this.e = "Bound"; this.c = var2; this.d = var1; this.f = var3; } public SearchBound(LatLonPoint var1, LatLonPoint var2) { this.e = "Rectangle"; this.a(var1, var2); } public SearchBound(List<LatLonPoint> var1) { this.e = "Polygon"; this.g = var1; } private SearchBound(LatLonPoint var1, LatLonPoint var2, int var3, LatLonPoint var4, String var5, List<LatLonPoint> var6, boolean var7) { this.a = var1; this.b = var2; this.c = var3; this.d = var4; this.e = var5; this.g = var6; this.f = var7; } private void a(LatLonPoint var1, LatLonPoint var2) { this.a = var1; this.b = var2; if (this.a.getLatitude() >= this.b.getLatitude() || this.a.getLongitude() >= this.b.getLongitude()) { (new IllegalArgumentException("invalid rect ")).printStackTrace(); } this.d = new LatLonPoint((this.a.getLatitude() + this.b.getLatitude()) / (double)2.0F, (this.a.getLongitude() + this.b.getLongitude()) / (double)2.0F); } public LatLonPoint getLowerLeft() { return this.a; } public LatLonPoint getUpperRight() { return this.b; } public LatLonPoint getCenter() { return this.d; } public int getRange() { return this.c; } public String getShape() { return this.e; } public boolean isDistanceSort() { return this.f; } public List<LatLonPoint> getPolyGonList() { return this.g; } public int hashCode() { int var1 = 31 + (this.d == null ? 0 : this.d.hashCode()); var1 = 31 * var1 + (this.f ? 1231 : 1237); var1 = 31 * var1 + (this.a == null ? 0 : this.a.hashCode()); var1 = 31 * var1 + (this.b == null ? 0 : this.b.hashCode()); var1 = 31 * var1 + (this.g == null ? 0 : this.g.hashCode()); var1 = 31 * var1 + this.c; return 31 * var1 + (this.e == null ? 0 : this.e.hashCode()); } public boolean equals(Object var1) { if (this == var1) { return true; } else if (var1 == null) { return false; } else if (this.getClass() != var1.getClass()) { return false; } else { var1 = var1; if (this.d == null) { if (var1.d != null) { return false; } } else if (!this.d.equals(var1.d)) { return false; } if (this.f != var1.f) { return false; } else { if (this.a == null) { if (var1.a != null) { return false; } } else if (!this.a.equals(var1.a)) { return false; } if (this.b == null) { if (var1.b != null) { return false; } } else if (!this.b.equals(var1.b)) { return false; } if (this.g == null) { if (var1.g != null) { return false; } } else if (!this.g.equals(var1.g)) { return false; } if (this.c != var1.c) { return false; } else { if (this.e == null) { if (var1.e != null) { return false; } } else if (!this.e.equals(var1.e)) { return false; } return true; } } } } public SearchBound clone() { try { super.clone(); } catch (CloneNotSupportedException var1) { jq.a(var1, "PoiSearch", "SearchBoundClone"); } return new SearchBound(this.a, this.b, this.c, this.d, this.e, this.g, this.f); } } public interface OnPoiSearchListener { void onPoiSearched(PoiResult var1, int var2); void onPoiItemSearched(PoiItem var1, int var2); } } PoiItem就这些:// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.amap.api.services.core; import android.os.Parcel; import android.os.Parcelable; import com.amap.api.services.poisearch.IndoorData; import com.amap.api.services.poisearch.Photo; import com.amap.api.services.poisearch.PoiItemExtension; import com.amap.api.services.poisearch.SubPoiItem; import java.util.ArrayList; import java.util.List; public class PoiItem implements Parcelable { private String a; private String b; private String c; private String d; private String e = ""; private int f = -1; private final LatLonPoint g; private final String h; private final String i; private LatLonPoint j; private LatLonPoint k; private String l; private String m; private String n; private String o; private String p; private String q; private String r; private boolean s; private IndoorData t; private String u; private String v; private String w; private List<SubPoiItem> x = new ArrayList(); private List<Photo> y = new ArrayList(); private PoiItemExtension z; private String A; private String B; public static final Parcelable.Creator<PoiItem> CREATOR = new Parcelable.Creator<PoiItem>() { private static PoiItem a(Parcel var0) { return new PoiItem(var0); } private static PoiItem[] a(int var0) { return new PoiItem[var0]; } }; public PoiItem(String var1, LatLonPoint var2, String var3, String var4) { this.a = var1; this.g = var2; this.h = var3; this.i = var4; } public String getBusinessArea() { return this.v; } public void setBusinessArea(String var1) { this.v = var1; } public String getAdName() { return this.r; } public void setAdName(String var1) { this.r = var1; } public String getCityName() { return this.q; } public void setCityName(String var1) { this.q = var1; } public String getProvinceName() { return this.p; } public void setProvinceName(String var1) { this.p = var1; } public String getTypeDes() { return this.e; } public void setTypeDes(String var1) { this.e = var1; } public String getTel() { return this.b; } public void setTel(String var1) { this.b = var1; } public String getAdCode() { return this.c; } public void setAdCode(String var1) { this.c = var1; } public String getPoiId() { return this.a; } public int getDistance() { return this.f; } public void setDistance(int var1) { this.f = var1; } public String getTitle() { return this.h; } public String getSnippet() { return this.i; } public LatLonPoint getLatLonPoint() { return this.g; } public String getCityCode() { return this.d; } public void setCityCode(String var1) { this.d = var1; } public LatLonPoint getEnter() { return this.j; } public void setEnter(LatLonPoint var1) { this.j = var1; } public LatLonPoint getExit() { return this.k; } public void setExit(LatLonPoint var1) { this.k = var1; } public String getWebsite() { return this.l; } public void setWebsite(String var1) { this.l = var1; } public String getPostcode() { return this.m; } public void setPostcode(String var1) { this.m = var1; } public String getEmail() { return this.n; } public void setEmail(String var1) { this.n = var1; } public String getDirection() { return this.o; } public void setDirection(String var1) { this.o = var1; } public void setIndoorMap(boolean var1) { this.s = var1; } public boolean isIndoorMap() { return this.s; } public void setProvinceCode(String var1) { this.u = var1; } public String getProvinceCode() { return this.u; } public void setParkingType(String var1) { this.w = var1; } public String getParkingType() { return this.w; } public void setSubPois(List<SubPoiItem> var1) { this.x = var1; } public List<SubPoiItem> getSubPois() { return this.x; } public IndoorData getIndoorData() { return this.t; } public void setIndoorDate(IndoorData var1) { this.t = var1; } public List<Photo> getPhotos() { return this.y; } public void setPhotos(List<Photo> var1) { this.y = var1; } public PoiItemExtension getPoiExtension() { return this.z; } public void setPoiExtension(PoiItemExtension var1) { this.z = var1; } public String getTypeCode() { return this.A; } public void setTypeCode(String var1) { this.A = var1; } public String getShopID() { return this.B; } public void setShopID(String var1) { this.B = var1; } protected PoiItem(Parcel var1) { this.a = var1.readString(); this.c = var1.readString(); this.b = var1.readString(); this.e = var1.readString(); this.f = var1.readInt(); this.g = (LatLonPoint)var1.readValue(LatLonPoint.class.getClassLoader()); this.h = var1.readString(); this.i = var1.readString(); this.d = var1.readString(); this.j = (LatLonPoint)var1.readValue(LatLonPoint.class.getClassLoader()); this.k = (LatLonPoint)var1.readValue(LatLonPoint.class.getClassLoader()); this.l = var1.readString(); this.m = var1.readString(); this.n = var1.readString(); boolean[] var2 = new boolean[1]; var1.readBooleanArray(var2); this.s = var2[0]; this.o = var1.readString(); this.p = var1.readString(); this.q = var1.readString(); this.r = var1.readString(); this.u = var1.readString(); this.v = var1.readString(); this.w = var1.readString(); this.x = var1.readArrayList(SubPoiItem.class.getClassLoader()); this.t = (IndoorData)var1.readValue(IndoorData.class.getClassLoader()); this.y = var1.createTypedArrayList(Photo.CREATOR); this.z = (PoiItemExtension)var1.readParcelable(PoiItemExtension.class.getClassLoader()); this.A = var1.readString(); this.B = var1.readString(); } public int describeContents() { return 0; } public void writeToParcel(Parcel var1, int var2) { var1.writeString(this.a); var1.writeString(this.c); var1.writeString(this.b); var1.writeString(this.e); var1.writeInt(this.f); var1.writeValue(this.g); var1.writeString(this.h); var1.writeString(this.i); var1.writeString(this.d); var1.writeValue(this.j); var1.writeValue(this.k); var1.writeString(this.l); var1.writeString(this.m); var1.writeString(this.n); var1.writeBooleanArray(new boolean[]{this.s}); var1.writeString(this.o); var1.writeString(this.p); var1.writeString(this.q); var1.writeString(this.r); var1.writeString(this.u); var1.writeString(this.v); var1.writeString(this.w); var1.writeList(this.x); var1.writeValue(this.t); var1.writeTypedList(this.y); var1.writeParcelable(this.z, var2); var1.writeString(this.A); var1.writeString(this.B); } public boolean equals(Object var1) { if (this == var1) { return true; } else if (var1 == null) { return false; } else if (this.getClass() != var1.getClass()) { return false; } else { var1 = var1; if (this.a == null) { if (var1.a != null) { return false; } } else if (!this.a.equals(var1.a)) { return false; } return true; } } public int hashCode() { return 31 + (this.a == null ? 0 : this.a.hashCode()); } public String toString() { return this.h; } } PoiResult就这些:// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.amap.api.services.poisearch; import com.amap.api.services.core.PoiItem; import com.amap.api.services.core.SuggestionCity; import java.util.ArrayList; import java.util.List; public final class PoiResult { private int a; private ArrayList<PoiItem> b = new ArrayList(); private PoiSearch.Query c; private PoiSearch.SearchBound d; private List<String> e; private List<SuggestionCity> f; private int g; public static PoiResult createPagedResult(PoiSearch.Query var0, PoiSearch.SearchBound var1, List<String> var2, List<SuggestionCity> var3, int var4, int var5, ArrayList<PoiItem> var6) { return new PoiResult(var0, var1, var2, var3, var4, var5, var6); } private PoiResult(PoiSearch.Query var1, PoiSearch.SearchBound var2, List<String> var3, List<SuggestionCity> var4, int var5, int var6, ArrayList<PoiItem> var7) { this.c = var1; this.d = var2; this.e = var3; this.f = var4; this.g = var5; this.a = this.a(var6); this.b = var7; } public final int getPageCount() { return this.a; } public final PoiSearch.Query getQuery() { return this.c; } public final PoiSearch.SearchBound getBound() { return this.d; } public final ArrayList<PoiItem> getPois() { return this.b; } public final List<String> getSearchSuggestionKeywords() { return this.e; } public final List<SuggestionCity> getSearchSuggestionCitys() { return this.f; } private int a(int var1) { return (var1 + this.g - 1) / this.g; } }
12-03
怎么打造?目前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 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 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"); } 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() { RealTimePoiSuggestHelper suggestHelper = new RealTimePoiSuggestHelper(this); suggestHelper.setCurrentCity(currentCity); suggestHelper.setLocationBias(myCurrentLat, myCurrentLng); // 使用定位偏置 suggestHelper.setCallback(suggestions -> { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( this, android.R.layout.simple_dropdown_item_1line, suggestions ); searchInput.setAdapter(adapter); searchInput.showDropDown(); } }); 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.setOnItemClickListener((parent, view, position, id) -> { String keyword = (String) parent.getItemAtPosition(position); if (keyword != null && !keyword.isEmpty()) { performSearch(keyword); // 触发真实搜索 } }); 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<PoiItemWithScore> rankedList = new ArrayList<>(); String keyword = searchInput.getText().toString().trim().toLowerCase(); float[] distance = new float[1]; LatLng userPos = isLocationReady ? new LatLng(myCurrentLat, myCurrentLng) : new LatLng(35.8617, 104.1954); for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase(); int matchScore = 0; if (title.equals(keyword)) { matchScore = 3; } else if (title.startsWith(keyword)) { matchScore = 2; } else if (title.contains(keyword)) { matchScore = 1; } else { matchScore = 0; } LatLonPoint lp = item.getLatLonPoint(); android.location.Location.distanceBetween( userPos.latitude, userPos.longitude, lp.getLatitude(), lp.getLongitude(), distance); rankedList.add(new PoiItemWithScore(item, matchScore, distance[0])); } rankedList.sort((a, b) -> { if (a.matchScore != b.matchScore) { return Integer.compare(b.matchScore, a.matchScore); } return Float.compare(a.distance, b.distance); }); poiList.clear(); for (PoiItemWithScore rp : rankedList) { poiList.add(rp.item); } adapter.notifyDataSetChanged(); resultListView.scrollToPosition(0); resultListView.setVisibility(View.VISIBLE); emptyView.setVisibility(View.GONE); adapter.setSelected(0); onPoiItemSelected(poiList.get(0)); } else { emptyView.setText("⚠️ 未找到相关地点"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } // 新增辅助类 private static class PoiItemWithScore { final PoiItem item; final int matchScore; final float distance; PoiItemWithScore(PoiItem item, int matchScore, float distance) { this.item = item; this.matchScore = matchScore; this.distance = distance; } } @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(); geocodeSearch = null; } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } } MapFragment代码如下:package com.example.bus.ui.map; import android.Manifest; import android.content.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.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; // 用于设置 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.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.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 android.view.inputmethod.EditorInfo; public class MapFragment extends Fragment implements PoiSearch.OnPoiSearchListener, GeocodeSearch.OnGeocodeSearchListener { private FragmentMapBinding binding; private MapView mapView; private AMap aMap; // 数据 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); // ✅ 使用缓存的位置(仅当真实定位完成后) 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<PoiItemWithScore> rankedList = new ArrayList<>(); String keyword = getCurrentInputKeyword().toLowerCase(); float[] distance = new float[1]; LatLng userPos = isLocationReady ? new LatLng(myCurrentLat, myCurrentLng) : new LatLng(39.909186, 116.397411); for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase(); int matchScore = 0; if (title.equals(keyword)) { matchScore = 3; } else if (title.startsWith(keyword)) { matchScore = 2; } else if (title.contains(keyword)) { matchScore = 1; } else { matchScore = 0; // ⚠️ 不丢弃! } LatLonPoint lp = item.getLatLonPoint(); android.location.Location.distanceBetween( userPos.latitude, userPos.longitude, lp.getLatitude(), lp.getLongitude(), distance); rankedList.add(new PoiItemWithScore(item, matchScore, distance[0])); } rankedList.sort((a, b) -> { if (a.matchScore != b.matchScore) { return Integer.compare(b.matchScore, a.matchScore); } return Float.compare(a.distance, b.distance); }); poiList.clear(); for (PoiItemWithScore rp : rankedList) { poiList.add(rp.item); } adapter.notifyDataSetChanged(); binding.resultList.scrollToPosition(0); binding.emptyView.setVisibility(View.GONE); binding.resultList.setVisibility(View.VISIBLE); adapter.setSelected(0); onPoiItemSelected(poiList.get(0)); } else { handleSearchError(rCode); binding.emptyView.setText(selectionStage == 1 ? "⚠️ 未找到相关起点" : "⚠️ 未找到相关终点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } // 辅助方法:获取当前阶段的关键词 private String getCurrentInputKeyword() { return selectionStage == 1 ? binding.mapInput1.getText().toString().trim() : binding.mapInput2.getText().toString().trim(); } // 新增:排序用辅助类(放在 MapFragment 类末尾即可) private static class PoiItemWithScore { final PoiItem item; final int matchScore; final float distance; PoiItemWithScore(PoiItem item, int matchScore, float distance) { this.item = item; this.matchScore = matchScore; this.distance = distance; } } @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(); geocodeSearch = null; binding = null; } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } // ✅ 完整保留原始的 setupSearchSuggestion 实现 private void setupSearchSuggestion() { // 创建实时 POI 建议助手 RealTimePoiSuggestHelper suggestHelper = new RealTimePoiSuggestHelper(requireContext()); suggestHelper.setCurrentCity(currentCity); 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(); } }); } }); Handler handler = new Handler(Looper.getMainLooper()); Runnable[] pending1 = {null}, pending2 = {null}; // 使用 SimpleTextWatcher 简化注册 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); })); // 点击建议项时触发对应阶段的搜索 binding.mapInput1.setOnItemClickListener((parent, view, position, id) -> { String keyword = (String) parent.getItemAtPosition(position); if (keyword != null && !keyword.isEmpty()) { binding.mapInput1.setText(keyword); showStartpointSelection(keyword); // 直接触发起点搜索 } }); binding.mapInput2.setOnItemClickListener((parent, view, position, id) -> { String keyword = (String) parent.getItemAtPosition(position); if (keyword != null && !keyword.isEmpty()) { binding.mapInput2.setText(keyword); showEndpointSelection(keyword); // 直接触发终点搜索 } }); // 回车事件保持不变 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); } } } 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.services.core.LatLonPoint; 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; 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 String currentKeyword = ""; 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 setCallback(SuggestionCallback callback) { this.callback = callback; } // 外部调用此方法发起实时建议查询 public void requestSuggestions(String keyword) { // ✅ 缓存关键词用于后续排序 this.currentKeyword = keyword; if (keyword.isEmpty() || callback == null) return; PoiSearch.Query query = new PoiSearch.Query(keyword, "", currentCity); query.setPageSize(20); query.requireSubPois(false); if (useLocationBias && lat != 0 && lng != 0) { 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 && !result.getPois().isEmpty()) { List<PoiItemWithScore> rankedList = new ArrayList<>(); float[] distance = new float[1]; String keywordLower = currentKeyword.toLowerCase(); for (com.amap.api.services.core.PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase(); // 匹配评分(不丢弃任何结果) int matchScore = 0; if (title.equals(keywordLower)) { matchScore = 3; } else if (title.startsWith(keywordLower)) { matchScore = 2; } else if (title.contains(keywordLower)) { matchScore = 1; } else { matchScore = 0; // 不匹配也保留,但分数最低 } // 计算距离 if (useLocationBias && lat != 0 && lng != 0) { LatLonPoint lp = item.getLatLonPoint(); android.location.Location.distanceBetween(lat, lng, lp.getLatitude(), lp.getLongitude(), distance); } else { distance[0] = Float.MAX_VALUE; } rankedList.add(new PoiItemWithScore(item, matchScore, distance[0])); } // 排序:先看匹配度,再看距离 rankedList.sort((a, b) -> { if (a.matchScore != b.matchScore) { return Integer.compare(b.matchScore, a.matchScore); } return Float.compare(a.distance, b.distance); }); // 取前8个作为建议 int count = Math.min(rankedList.size(), 8); String[] suggestions = new String[count]; for (int i = 0; i < count; i++) { suggestions[i] = rankedList.get(i).item.getTitle(); } notifySuccess(suggestions); } else { notifyEmpty(); } } @Override public void onPoiItemSearched(com.amap.api.services.core.PoiItem item, int rCode) { // 忽略 } private void notifySuccess(@NonNull String[] suggestions) { mainHandler.post(() -> callback.onSuggestionsReady(suggestions)); } private void notifyEmpty() { mainHandler.post(() -> callback.onSuggestionsReady(new String[0])); } // 辅助类:用于排序 private static class PoiItemWithScore { final com.amap.api.services.core.PoiItem item; final int matchScore; final float distance; PoiItemWithScore(com.amap.api.services.core.PoiItem item, int matchScore, float distance) { this.item = item; this.matchScore = matchScore; this.distance = distance; } } } 请你为我提供修改好的完整代码,最小量修改即可。其它未提到的内容保留原来的样子不变
12-02
但请你注意一点,这个是搜索提示框,他还拥有一个补全信息的功能,是含有猜测的功能所在的,他需要预测用户需要搜索的信息,目前代码中确实含有这样的功能,只不过我希望他能够跟搜索结果的逻辑保持一致而已。所以不同于搜索结果,这里每输入一个字都要进行实时更新而且响应要快 而目前我的代码还没有做更改,这也是我的疏忽,刚刚没跟你说明白这些东西。目前各部分代码如下: 1、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; // 使用 CityManager 解析是否有显式城市 CityManager.ParsedQuery parsed = CityManager.parse(keyword); String actualKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String city = !parsed.targetCity.isEmpty() ? parsed.targetCity : currentCity; // 显式城市 > 当前城市 PoiSearch.Query query = new PoiSearch.Query(actualKeyword, "", city); 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])); } } 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.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.constraintlayout.widget.ConstraintSet; import com.amap.api.maps.AMap; import com.amap.api.maps.AMapUtils; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.MapView; import com.amap.api.maps.UiSettings; import com.amap.api.maps.model.BitmapDescriptorFactory; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.Marker; import com.amap.api.maps.model.MarkerOptions; import com.amap.api.maps.model.MyLocationStyle; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import com.amap.api.services.geocoder.GeocodeSearch; import com.amap.api.services.geocoder.RegeocodeQuery; import com.amap.api.services.geocoder.RegeocodeResult; import com.example.bus.CityManager; import com.example.bus.R; import com.example.bus.RealTimePoiSuggestHelper; import com.example.bus.RoutePlanActivity; import com.example.bus.ResultAdapter; import com.example.bus.databinding.FragmentMapBinding; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class MapFragment extends Fragment implements PoiSearch.OnPoiSearchListener, GeocodeSearch.OnGeocodeSearchListener { private FragmentMapBinding binding; private MapView mapView; private AMap aMap; private RealTimePoiSuggestHelper activeSuggestHelper; // 数据 private List<PoiItem> poiList = new ArrayList<>(); private ResultAdapter adapter; // 当前阶段:1=选择起点, 2=选择终点 private int selectionStage = 0; // 缓存已选 POI private PoiItem selectedStartPoi = null; private PoiItem selectedEndPoi = null; private Marker startMarker = null; private Marker endMarker = null; // 缓存关键词 private String lastStartKeyword = ""; private String lastEndKeyword = ""; // ✅ 当前城市 private String currentCity = ""; private static final int LOCATION_PERMISSION_REQUEST_CODE = 1001; // ✅ 标记是否已居中我的位置 private boolean userHasInteracted = false; // ✅ 反地理编码(仅用于“我的位置”) private GeocodeSearch geocodeSearch; // 【关键新增】保存定位得到的“我的位置” private double myCurrentLat = 0; private double myCurrentLng = 0; private boolean isLocationReady = false; // 定位是否完成 // 🔽 新增缓存字段 private List<PoiItem> nationalResults = new ArrayList<>(); // 全国结果 private List<PoiItem> localResults = new ArrayList<>(); // 本市结果 private List<PoiItem> nearbyResults = new ArrayList<>(); // 附近结果 private boolean isNearbyLoaded = false; // 是否已加载 nearby 数据 private boolean isInNearbyMode = false; // 是否处于附近模式 @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentMapBinding.inflate(inflater, container, false); View root = binding.getRoot(); mapView = binding.mapView; mapView.onCreate(savedInstanceState); initViews(); setupMap(savedInstanceState); setupSearchSuggestion(); return root; } private void initViews() { adapter = new ResultAdapter(poiList, this::onPoiItemSelected); binding.resultList.setLayoutManager(new LinearLayoutManager(requireContext())); binding.resultList.setAdapter(adapter); binding.mapSearch.setOnClickListener(v -> performSearch()); binding.btnSwitchTarget.setOnClickListener(v -> { if (selectionStage == 1) { showEndpointSelection(binding.mapInput2.getText().toString().trim()); } else if (selectionStage == 2) { showStartpointSelection(binding.mapInput1.getText().toString().trim()); } }); binding.btnGoTo.setOnClickListener(v -> { if (selectedStartPoi != null && selectedEndPoi != null) { Intent intent = new Intent(requireContext(), RoutePlanActivity.class); intent.putExtra(RoutePlanActivity.EXTRA_SOURCE, RoutePlanActivity.SOURCE_FROM_MAP_DIRECT); intent.putExtra("start_lat", selectedStartPoi.getLatLonPoint().getLatitude()); intent.putExtra("start_lng", selectedStartPoi.getLatLonPoint().getLongitude()); intent.putExtra("target_lat", selectedEndPoi.getLatLonPoint().getLatitude()); intent.putExtra("target_lng", selectedEndPoi.getLatLonPoint().getLongitude()); intent.putExtra("target_title", selectedEndPoi.getTitle()); startActivity(intent); } else { Toast.makeText(requireContext(), "请完成起点终点的选择", Toast.LENGTH_SHORT).show(); } }); binding.btnToggleMode.setOnClickListener(v -> { if (isInNearbyMode) { exitNearbyMode(); } else { enterNearbyMode(); } }); } private void performSearch() { String startKeyword = binding.mapInput1.getText().toString().trim(); String endKeyword = binding.mapInput2.getText().toString().trim(); if (startKeyword.isEmpty()) { Toast.makeText(requireContext(), "请输入起点", Toast.LENGTH_SHORT).show(); return; } if (endKeyword.isEmpty()) { Toast.makeText(requireContext(), "请输入终点", Toast.LENGTH_SHORT).show(); return; } if (startKeyword.equals(lastStartKeyword) && endKeyword.equals(lastEndKeyword) && selectedStartPoi != null && selectedEndPoi != null) { binding.btnGoTo.performClick(); return; } binding.containerResultList.setVisibility(View.VISIBLE); binding.buttonGroup.setVisibility(View.VISIBLE); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(binding.getRoot()); constraintSet.connect( R.id.map_view, ConstraintSet.BOTTOM, R.id.container_result_list, ConstraintSet.TOP, 0 ); constraintSet.applyTo(binding.getRoot()); userHasInteracted = true; View currentFocus = requireActivity().getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0); } if (!startKeyword.equals(lastStartKeyword)) { lastStartKeyword = startKeyword; lastEndKeyword = endKeyword; showStartpointSelection(startKeyword); } else if (!endKeyword.equals(lastEndKeyword)) { lastEndKeyword = endKeyword; showEndpointSelection(endKeyword); } else if (selectedStartPoi == null) { showStartpointSelection(startKeyword); } else { showEndpointSelection(endKeyword); } } private void showStartpointSelection(String keyword) { selectionStage = 1; binding.btnSwitchTarget.setText("前往选择终点"); binding.btnGoTo.setEnabled(false); binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索起点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void showEndpointSelection(String keyword) { selectionStage = 2; binding.btnSwitchTarget.setText("回到选择起点"); binding.btnGoTo.setEnabled(false); binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索终点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void doSearch(String keyword) { if (keyword.isEmpty()) return; nationalResults.clear(); localResults.clear(); nearbyResults.clear(); // ✅ 清除历史 nearby isNearbyLoaded = false; isInNearbyMode = false; binding.btnToggleMode.setText("📍 附近"); CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(this); search.searchPOIAsyn(); } catch (Exception e) { Toast.makeText(requireContext(), "搜索失败", Toast.LENGTH_SHORT).show(); } } else { binding.emptyView.setText("🔍 搜索中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch nationalSearch = new PoiSearch(requireContext(), nationalQuery); nationalSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nationalSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); requestLocalSearch(searchKeyword); } } } private void requestLocalSearch(String keyword) { if (currentCity.isEmpty()) { showCombinedResults(); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(requireContext(), localQuery); localSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { localResults.clear(); localResults.addAll(result.getPois()); } showCombinedResults(); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); showCombinedResults(); } } private void showCombinedResults() { List<PoiItem> combined = new ArrayList<>(); Set<String> seen = new HashSet<>(); adapter.clearExtraText(); for (PoiItem item : localResults) { if (seen.add(item.getPoiId())) { combined.add(item); String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } } for (PoiItem item : nationalResults) { if (seen.add(item.getPoiId())) { combined.add(item); String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } } updateResultList(combined); } private String getDisplayCity(PoiItem item) { if (item == null) return "未知城市"; String city = item.getCityName(); if (city != null && !city.isEmpty() && !city.equals("[]")) { return city; } String adName = item.getAdName(); if (adName != null && !adName.isEmpty() && !adName.equals("[]")) { return adName; } String province = item.getProvinceName(); if (province != null && !province.isEmpty()) { return province; } return "未知城市"; } private void enterNearbyMode() { if (!isLocationReady) { Toast.makeText(requireContext(), "正在获取位置...", Toast.LENGTH_SHORT).show(); return; } String keyword = getCurrentKeyword(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); String explicitCity = parsed.targetCity; String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; if (!explicitCity.isEmpty() && !isSameCity(explicitCity, currentCity)) { nearbyResults.clear(); localResults.clear(); nationalResults.clear(); poiList.clear(); adapter.notifyDataSetChanged(); binding.emptyView.setText("📍 所选城市非当前所在城市\n无法搜索附近"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); isInNearbyMode = false; return; } if (!isNearbyLoaded) { startNearbySearch(searchKeyword); } else { showNearbyResults(); } isInNearbyMode = true; binding.btnToggleMode.setText("🌐 全范围"); } private boolean isSameCity(String city1, String city2) { if (city1 == null || city2 == null) return false; String c1 = city1.endsWith("市") ? city1.substring(0, city1.length() - 1) : city1; String c2 = city2.endsWith("市") ? city2.substring(0, city2.length() - 1) : city2; return c1.equals(c2); } private void exitNearbyMode() { showCombinedResults(); isInNearbyMode = false; binding.btnToggleMode.setText("📍 附近"); } // ✅【重点增强】增加内部防护 private void startNearbySearch(String keyword) { String rawKeyword = getCurrentKeyword(); CityManager.ParsedQuery parsed = CityManager.parse(rawKeyword); String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty() && !isSameCity(explicitCity, currentCity)) { Log.w("MapFragment", "拒绝发起跨城 nearby 搜索: " + explicitCity); binding.emptyView.setText("📍 所选城市非当前所在城市\n无法搜索附近"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); return; } LatLonPoint center = new LatLonPoint(myCurrentLat, myCurrentLng); PoiSearch.Query query = new PoiSearch.Query(keyword, "", ""); query.setPageSize(20); try { PoiSearch nearbySearch = new PoiSearch(requireContext(), query); nearbySearch.setBound(new PoiSearch.SearchBound(center, 3000)); nearbySearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult res, int code) { if (code == 1000 && res != null && res.getPois() != null && !res.getPois().isEmpty()) { nearbyResults.clear(); nearbyResults.addAll(sortByDistance(res.getPois(), myCurrentLat, myCurrentLng)); isNearbyLoaded = true; showNearbyResults(); } else { binding.emptyView.setText("⚠️ 附近未找到地点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nearbySearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); binding.emptyView.setText("⚠️ 附近搜索失败"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } private void showNearbyResults() { List<PoiItem> list = new ArrayList<>(nearbyResults); adapter.clearExtraText(); LatLng me = new LatLng(myCurrentLat, myCurrentLng); for (PoiItem item : nearbyResults) { double dist = AMapUtils.calculateLineDistance(toLatLng(item.getLatLonPoint()), me); String distText = dist < 1000 ? ((int) dist) + "m" : String.format("%.1fkm", dist / 1000); adapter.setExtraText(item, " | " + distText); } updateResultList(list); } private void onPoiItemSelected(PoiItem item) { LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); if (selectionStage == 1) { if (startMarker != null) startMarker.remove(); startMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("起点:" + item.getTitle()) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))); selectedStartPoi = item; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); updateGoToButtonState(); } else if (selectionStage == 2) { if (endMarker != null) endMarker.remove(); endMarker = aMap.addMarker(new MarkerOptions() .position(latLng) .title("终点:" + item.getTitle()) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))); selectedEndPoi = item; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 14f)); updateGoToButtonState(); } userHasInteracted = true; } private void updateGoToButtonState() { binding.btnGoTo.setEnabled(selectedStartPoi != null && selectedEndPoi != null); } @Override public void onPoiSearched(PoiResult result, int rCode) { String keyword = getCurrentKeyword(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); if (parsed.targetCity.isEmpty()) return; if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { updateResultList(result.getPois()); } else { handleSearchError(rCode); binding.emptyView.setText("⚠️ 未找到相关地点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } private String getCurrentKeyword() { return selectionStage == 1 ? binding.mapInput1.getText().toString().trim() : binding.mapInput2.getText().toString().trim(); } private void updateResultList(List<PoiItem> list) { poiList.clear(); poiList.addAll(list); adapter.notifyDataSetChanged(); binding.resultList.scrollToPosition(0); binding.emptyView.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE); binding.resultList.setVisibility(list.isEmpty() ? View.GONE : View.VISIBLE); if (!list.isEmpty()) { adapter.setSelected(0); onPoiItemSelected(list.get(0)); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} private LatLng toLatLng(LatLonPoint point) { if (point == null) return null; return new LatLng(point.getLatitude(), point.getLongitude()); } private List<PoiItem> sortByDistance(List<PoiItem> list, double lat, double lng) { LatLng me = new LatLng(lat, lng); return list.stream() .sorted((a, b) -> { double da = AMapUtils.calculateLineDistance(toLatLng(a.getLatLonPoint()), me); double db = AMapUtils.calculateLineDistance(toLatLng(b.getLatLonPoint()), me); return Double.compare(da, db); }) .collect(java.util.stream.Collectors.toList()); } private void setupMap(Bundle savedInstanceState) { mapView.onCreate(savedInstanceState); aMap = mapView.getMap(); if (aMap != null) { initMapSettings(); } else { new Handler(Looper.getMainLooper()).post(() -> { aMap = mapView.getMap(); if (aMap != null) { initMapSettings(); } else { waitAMapReady(); } }); } try { geocodeSearch = new GeocodeSearch(requireContext()); geocodeSearch.setOnGeocodeSearchListener(this); } catch (Exception e) { e.printStackTrace(); } } private void waitAMapReady() { new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { int retry = 0; @Override public void run() { if (mapView == null) return; aMap = mapView.getMap(); if (aMap != null) { initMapSettings(); } else if (retry++ < 30) { new Handler(Looper.getMainLooper()).postDelayed(this, 100); } } }, 100); } private void initMapSettings() { UiSettings uiSettings = aMap.getUiSettings(); uiSettings.setZoomControlsEnabled(true); uiSettings.setCompassEnabled(true); uiSettings.setScrollGesturesEnabled(true); uiSettings.setMyLocationButtonEnabled(true); new Handler(Looper.getMainLooper()).post(() -> aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.909186, 116.397411), 10f)) ); enableMyLocationLayer(); } private void enableMyLocationLayer() { if (aMap == null) return; if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { MyLocationStyle myLocationStyle = new MyLocationStyle(); myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER); aMap.setMyLocationStyle(myLocationStyle); aMap.setMyLocationEnabled(true); AMap.OnMyLocationChangeListener listener = location -> { if (location != null && !userHasInteracted) { LatLng curLatlng = new LatLng(location.getLatitude(), location.getLongitude()); if (activeSuggestHelper != null) { activeSuggestHelper.setLocationBias(location.getLatitude(), location.getLongitude()); } myCurrentLat = location.getLatitude(); myCurrentLng = location.getLongitude(); isLocationReady = true; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 16f)); userHasInteracted = true; LatLonPoint point = new LatLonPoint(myCurrentLat, myCurrentLng); RegeocodeQuery query = new RegeocodeQuery(point, 200, GeocodeSearch.AMAP); try { geocodeSearch.getFromLocationAsyn(query); } catch (Exception e) { e.printStackTrace(); } aMap.setOnMyLocationChangeListener(null); } }; aMap.setOnMyLocationChangeListener(listener); } else { ActivityCompat.requestPermissions(requireActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (aMap != null) { MyLocationStyle myLocationStyle = new MyLocationStyle(); myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER); aMap.setMyLocationStyle(myLocationStyle); aMap.setMyLocationEnabled(true); AMap.OnMyLocationChangeListener listener = location -> { if (location != null && !userHasInteracted) { LatLng curLatlng = new LatLng(location.getLatitude(), location.getLongitude()); if (activeSuggestHelper != null) { activeSuggestHelper.setLocationBias(location.getLatitude(), location.getLongitude()); } myCurrentLat = location.getLatitude(); myCurrentLng = location.getLongitude(); isLocationReady = true; aMap.animateCamera(CameraUpdateFactory.newLatLngZoom(curLatlng, 16f)); userHasInteracted = true; LatLonPoint point = new LatLonPoint(myCurrentLat, myCurrentLng); RegeocodeQuery query = new RegeocodeQuery(point, 200, GeocodeSearch.AMAP); try { geocodeSearch.getFromLocationAsyn(query); } catch (Exception e) { e.printStackTrace(); } aMap.setOnMyLocationChangeListener(null); } }; aMap.setOnMyLocationChangeListener(listener); } } } } @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (result == null || result.getRegeocodeQuery() == null) return; LatLonPoint point = result.getRegeocodeQuery().getPoint(); if (rCode == 1000 && result.getRegeocodeAddress() != null) { String city = result.getRegeocodeAddress().getCity(); String updatedCity = (city != null && !city.isEmpty()) ? city : result.getRegeocodeAddress().getProvince(); if (Math.abs(point.getLatitude() - myCurrentLat) < 0.0001 && Math.abs(point.getLongitude() - myCurrentLng) < 0.0001) { currentCity = updatedCity; if (activeSuggestHelper != null) { activeSuggestHelper.setCurrentCity(currentCity); } Log.d("MapFragment", "🎯 currentCity 已更新为: " + currentCity); } } else { Log.e("MapFragment", "❌ 反编译失败: rCode=" + rCode); } } @Override public void onGeocodeSearched(com.amap.api.services.geocoder.GeocodeResult geocodeResult, int i) {} @Override public void onResume() { super.onResume(); mapView.onResume(); if (!userHasInteracted) { enableMyLocationLayer(); } } @Override public void onPause() { super.onPause(); mapView.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); mapView.onDestroy(); geocodeSearch = null; binding = null; } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } private void setupSearchSuggestion() { 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(); } }); } }); 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); })); 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.util.Log; 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.HashSet; import java.util.List; import java.util.Set; public class SearchResultActivity extends AppCompatActivity implements PoiSearch.OnPoiSearchListener, GeocodeSearch.OnGeocodeSearchListener { private Button searchBtn, goToBtn, btnToggleMode; private RecyclerView resultListView; private List<PoiItem> poiList = new ArrayList<>(); private ResultAdapter adapter; // 地图相关 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; // 缓存关键词 private String pendingKeyword = null; // ✅ 实时建议助手 private RealTimePoiSuggestHelper suggestHelper; // 🔽 新增缓存字段 private List<PoiItem> nationalResults = new ArrayList<>(); private List<PoiItem> localResults = new ArrayList<>(); private List<PoiItem> nearbyResults = new ArrayList<>(); private boolean isNearbyLoaded = false; private boolean isInNearbyMode = false; // ✅ 将搜索输入框提升为成员变量 private android.widget.AutoCompleteTextView searchInput; @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 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); btnToggleMode = findViewById(R.id.btn_toggle_mode); 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(); }); btnToggleMode.setOnClickListener(v -> { if (isInNearbyMode) { exitNearbyMode(); } else { enterNearbyMode(); } }); } 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.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(); } 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(); } private void performSearch(String keyword) { if (keyword.isEmpty()) return; // ✅【新增】重置所有状态(含 nearby) nationalResults.clear(); localResults.clear(); nearbyResults.clear(); // 清除历史 nearby 结果 isNearbyLoaded = false; isInNearbyMode = false; btnToggleMode.setText("📍 附近"); emptyView.setText("🔍 搜索中..."); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(this, query); search.setOnPoiSearchListener(this); search.searchPOIAsyn(); } catch (Exception e) { Toast.makeText(this, "搜索失败", Toast.LENGTH_SHORT).show(); } } else { nationalResults.clear(); localResults.clear(); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch nationalSearch = new PoiSearch(this, nationalQuery); nationalSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nationalSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); requestLocalSearch(searchKeyword); } } } private void requestLocalSearch(String keyword) { if (currentCity.isEmpty()) { showCombinedResults(); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(this, localQuery); localSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { localResults.clear(); localResults.addAll(result.getPois()); } showCombinedResults(); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); showCombinedResults(); } } private void showCombinedResults() { List<PoiItem> combined = new ArrayList<>(); Set<String> seen = new HashSet<>(); adapter.clearExtraText(); for (PoiItem item : localResults) { if (seen.add(item.getPoiId())) { combined.add(item); String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } } for (PoiItem item : nationalResults) { if (seen.add(item.getPoiId())) { combined.add(item); String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } } updateResultList(combined); } private String getDisplayCity(PoiItem item) { if (item == null) return "未知城市"; String city = item.getCityName(); if (city != null && !city.isEmpty() && !city.equals("[]")) { return city; } String adName = item.getAdName(); if (adName != null && !adName.isEmpty() && !adName.equals("[]")) { return adName; } String province = item.getProvinceName(); if (province != null && !province.isEmpty()) { return province; } return "未知城市"; } private void enterNearbyMode() { if (!isLocationReady) { Toast.makeText(this, "正在获取位置...", Toast.LENGTH_SHORT).show(); return; } String keyword = searchInput.getText().toString().trim(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); String explicitCity = parsed.targetCity; String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; // 🔴【关键】如果指定城市非当前城市,则禁止 nearby if (!explicitCity.isEmpty() && !isSameCity(explicitCity, currentCity)) { nearbyResults.clear(); localResults.clear(); nationalResults.clear(); poiList.clear(); adapter.notifyDataSetChanged(); emptyView.setText("📍 所选城市非当前所在城市\n无法搜索附近"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); isInNearbyMode = false; return; } // 只有同城才允许加载 nearby if (!isNearbyLoaded) { startNearbySearch(searchKeyword); } else { showNearbyResults(); } isInNearbyMode = true; btnToggleMode.setText("🌐 全范围"); } private boolean isSameCity(String city1, String city2) { if (city1 == null || city2 == null) return false; String c1 = city1.endsWith("市") ? city1.substring(0, city1.length() - 1) : city1; String c2 = city2.endsWith("市") ? city2.substring(0, city2.length() - 1) : city2; return c1.equals(c2); } private void exitNearbyMode() { showCombinedResults(); isInNearbyMode = false; btnToggleMode.setText("📍 附近"); } // ✅【重点增强】增加内部防护 private void startNearbySearch(String keyword) { String rawKeyword = searchInput.getText().toString().trim(); CityManager.ParsedQuery parsed = CityManager.parse(rawKeyword); String explicitCity = parsed.targetCity; // 🔒 再次检查是否跨城(防御性编程) if (!explicitCity.isEmpty() && !isSameCity(explicitCity, currentCity)) { Log.w("SearchResult", "拒绝发起跨城 nearby 搜索: " + explicitCity); emptyView.setText("📍 所选城市非当前所在城市\n无法搜索附近"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); return; } LatLonPoint center = new LatLonPoint(myCurrentLat, myCurrentLng); PoiSearch.Query query = new PoiSearch.Query(keyword, "", ""); query.setPageSize(20); try { PoiSearch nearbySearch = new PoiSearch(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()) { nearbyResults.clear(); nearbyResults.addAll(sortByDistance(res.getPois(), myCurrentLat, myCurrentLng)); isNearbyLoaded = true; showNearbyResults(); } else { emptyView.setText("⚠️ 附近未找到地点"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nearbySearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); emptyView.setText("⚠️ 附近搜索失败"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } private void showNearbyResults() { List<PoiItem> list = new ArrayList<>(nearbyResults); adapter.clearExtraText(); LatLng me = new LatLng(myCurrentLat, myCurrentLng); for (PoiItem item : nearbyResults) { double dist = com.amap.api.maps.AMapUtils.calculateLineDistance(toLatLng(item.getLatLonPoint()), me); String distText = dist < 1000 ? ((int) dist) + "m" : String.format("%.1fkm", dist / 1000); adapter.setExtraText(item, " | " + distText); } updateResultList(list); } private void onPoiItemSelected(PoiItem item) { LatLng latLng = new LatLng(item.getLatLonPoint().getLatitude(), item.getLatLonPoint().getLongitude()); if (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 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 = com.amap.api.maps.AMapUtils.calculateLineDistance(toLatLng(a.getLatLonPoint()), me); double db = com.amap.api.maps.AMapUtils.calculateLineDistance(toLatLng(b.getLatLonPoint()), me); return Double.compare(da, db); }) .collect(java.util.stream.Collectors.toList()); } @Override public void onPoiSearched(PoiResult result, int rCode) { String keyword = searchInput.getText().toString().trim(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); if (parsed.targetCity.isEmpty()) return; if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { updateResultList(result.getPois()); } else { 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(list.isEmpty() ? View.VISIBLE : View.GONE); resultListView.setVisibility(list.isEmpty() ? View.GONE : View.VISIBLE); if (!list.isEmpty()) { adapter.setSelected(0); onPoiItemSelected(list.get(0)); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} @Override public void onRegeocodeSearched(RegeocodeResult result, int rCode) { if (result == null || result.getRegeocodeQuery() == null) return; LatLonPoint point = result.getRegeocodeQuery().getPoint(); if (rCode == 1000 && result.getRegeocodeAddress() != null) { String city = result.getRegeocodeAddress().getCity(); String updatedCity = (city != null && !city.isEmpty()) ? city : result.getRegeocodeAddress().getProvince(); if (Math.abs(point.getLatitude() - myCurrentLat) < 0.0001 && Math.abs(point.getLongitude() - myCurrentLng) < 0.0001) { currentCity = updatedCity; if (suggestHelper != null) { suggestHelper.setCurrentCity(currentCity); } Log.d("SearchResultActivity", "🎯 currentCity 已更新为: " + currentCity); } } else { Log.e("SearchResultActivity", "❌ 反编译失败: rCode=" + rCode); } } @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; } } 请你按照我的要求对我的代码进行最小量的修改,为我提供修改好的完整代码,所有文件都从import开始提供
最新发布
12-09
你为什么不能一次性的改好呢,我目前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.util.Log; 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 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 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; // ✅ 新增:将 suggestHelper 提升为成员变量,以便后续更新 private RealTimePoiSuggestHelper suggestHelper; // ← 关键改动 @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(); // ← 在这里初始化 suggestHelper try { geocodeSearch = new GeocodeSearch(this); geocodeSearch.setOnGeocodeSearchListener(this); } catch (Exception e) { e.printStackTrace(); } pendingKeyword = getIntent().getStringExtra("keyword"); } 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(); } // ✅ 延迟触发搜索 new Handler(Looper.getMainLooper()).postDelayed(() -> { if (pendingKeyword != null && !pendingKeyword.isEmpty()) { performSearchWithKeyword(pendingKeyword); } }, 800); // ✅ 更新 suggestHelper 的位置偏移 if (this.suggestHelper != null) { this.suggestHelper.setLocationBias(myCurrentLat, myCurrentLng); } 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 = "全国"; } } } // ✅ 修改:setupSearchSuggestion 中使用成员变量,并动态更新 private void setupSearchSuggestion() { suggestHelper = new RealTimePoiSuggestHelper(this); suggestHelper.setCurrentCity(currentCity); if (isLocationReady) { suggestHelper.setLocationBias(myCurrentLat, myCurrentLng); } suggestHelper.setCallback(suggestions -> { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( this, android.R.layout.simple_dropdown_item_1line, suggestions ); searchInput.setAdapter(adapter); searchInput.showDropDown(); } }); 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.setOnItemClickListener((parent, view, position, id) -> { String keyword = (String) parent.getItemAtPosition(position); if (keyword != null && !keyword.isEmpty()) { performSearch(keyword); } }); 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(); } 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(50); // 保持与 suggestHelper 一致 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) { e.printStackTrace(); 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); } // ✅ 核心修改:完全复用 RealTimePoiSuggestHelper 的筛选逻辑 @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null && !result.getPois().isEmpty()) { String keywordLower = searchInput.getText().toString().trim().toLowerCase(); if (keywordLower.isEmpty()) return; List<RealTimePoiSuggestHelper.PoiItemWithScore> rankedList = new ArrayList<>(); float[] distance = new float[1]; for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase().trim(); // ✅ 严格复刻 suggestHelper 的逻辑 if (!title.contains(keywordLower)) { continue; } float userDistance = Float.MAX_VALUE; if (isLocationReady) { LatLonPoint lp = item.getLatLonPoint(); android.location.Location.distanceBetween( myCurrentLat, myCurrentLng, lp.getLatitude(), lp.getLongitude(), distance ); userDistance = distance[0]; } int matchScore = 0; if (title.equals(keywordLower)) { matchScore = 3; } else if (title.startsWith(keywordLower)) { matchScore = 2; } else { matchScore = 1; } rankedList.add(new RealTimePoiSuggestHelper.PoiItemWithScore(item, matchScore, userDistance)); } // 排序一致 rankedList.sort((a, b) -> { if (a.matchScore != b.matchScore) { return Integer.compare(b.matchScore, a.matchScore); } return Float.compare(a.distance, b.distance); }); // 显示前8个 int count = Math.min(rankedList.size(), 8); if (count > 0) { poiList.clear(); for (int i = 0; i < count; i++) { poiList.add(rankedList.get(i).item); } adapter.notifyDataSetChanged(); resultListView.scrollToPosition(0); resultListView.setVisibility(View.VISIBLE); emptyView.setVisibility(View.GONE); adapter.setSelected(0); onPoiItemSelected(poiList.get(0)); } else { emptyView.setText("🔍 未找到与 \"" + keywordLower + "\" 相关的结果"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } else { emptyView.setText("⚠️ 搜索失败: " + rCode); 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 = "全国"; } if (suggestHelper != null) { suggestHelper.setCurrentCity(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(); geocodeSearch = null; } @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } } 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.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import java.util.ArrayList; import java.util.List; public class RealTimePoiSuggestHelper implements PoiSearch.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 String currentKeyword = ""; 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 setCallback(SuggestionCallback callback) { this.callback = callback; } public void requestSuggestions(String keyword) { this.currentKeyword = keyword; if (keyword.isEmpty() || callback == null) return; PoiSearch.Query query = new PoiSearch.Query(keyword, "", currentCity); query.setPageSize(50); // 多拿一点,防止漏掉好结果 query.requireSubPois(false); if (useLocationBias && lat != 0 && lng != 0) { 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 && !result.getPois().isEmpty()) { List<PoiItemWithScore> rankedList = new ArrayList<>(); float[] distance = new float[1]; String keywordLower = currentKeyword.toLowerCase().trim(); for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase().trim(); // ✅ 放宽条件:只要“标题包含关键词”就视为候选 if (!title.contains(keywordLower)) { continue; } // 计算距离(用于排序) float userDistance = Float.MAX_VALUE; if (useLocationBias && lat != 0 && lng != 0) { LatLonPoint lp = item.getLatLonPoint(); android.location.Location.distanceBetween( lat, lng, lp.getLatitude(), lp.getLongitude(), distance ); userDistance = distance[0]; } // 匹配分:前缀最高,完全一致次之,包含最低 int matchScore = 0; if (title.equals(keywordLower)) { matchScore = 3; } else if (title.startsWith(keywordLower)) { matchScore = 2; } else { matchScore = 1; // 包含即可 } rankedList.add(new PoiItemWithScore(item, matchScore, userDistance)); } // 排序:先按匹配度降序,再按距离升序 rankedList.sort((a, b) -> { if (a.matchScore != b.matchScore) { return Integer.compare(b.matchScore, a.matchScore); } return Float.compare(a.distance, b.distance); }); // 取前8个 int count = Math.min(rankedList.size(), 8); String[] suggestions = new String[count]; for (int i = 0; i < count; i++) { suggestions[i] = rankedList.get(i).item.getTitle(); // 返回原始标题 } notifySuccess(suggestions); } else { notifyEmpty(); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) { // 忽略 } private void notifySuccess(@NonNull String[] suggestions) { mainHandler.post(() -> callback.onSuggestionsReady(suggestions)); } private void notifyEmpty() { mainHandler.post(() -> callback.onSuggestionsReady(new String[0])); } private static class PoiItemWithScore { final PoiItem item; final int matchScore; final float distance; PoiItemWithScore(PoiItem item, int matchScore, float distance) { this.item = item; this.matchScore = matchScore; this.distance = distance; } } } 我也说了,修改的话大体框架不要随意改变不然容易报错
12-03
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值