如何为Places自动完成碎片添加搜索历史功能
如何为Places自动完成碎片添加搜索历史功能
嘿,这个需求挺实用的!我来给你分享几个简单可行的实现思路,帮你给你的地图搜索加上最近搜索记录的功能~
方案一:用SharedPreferences存储历史记录(轻量易实现)
SharedPreferences是Android里存储轻量数据的绝佳选择,适合保存搜索历史这种简单的列表数据。我们可以在用户选择地点后把相关信息存起来,之后再读取并展示。
步骤1:添加存储和读取历史的工具方法
先写两个工具方法,一个用来保存选中的地点到历史,另一个用来读取已有的历史记录。可以把这些方法放在你的Activity或者单独的工具类里:
// 保存搜索历史到SharedPreferences private void saveSearchHistory(Place place) { SharedPreferences prefs = getSharedPreferences("SearchHistory", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); // 先获取已有的历史列表 Set<String> historySet = prefs.getStringSet("history", new HashSet<>()); // 把地点信息拼接成字符串(用分隔符分开名称、纬度、经度) String placeInfo = place.getName() + "|" + place.getLatLng().latitude + "|" + place.getLatLng().longitude; // 去重:如果已存在就先移除,再添加到最前面(保证最新记录在顶部) historySet.remove(placeInfo); historySet.add(placeInfo); // 最多保存10条历史,可根据需求调整数量 if (historySet.size() > 10) { Iterator<String> iterator = historySet.iterator(); iterator.next(); iterator.remove(); } editor.putStringSet("history", historySet); editor.apply(); } // 读取搜索历史 private List<Map<String, Object>> getSearchHistory() { SharedPreferences prefs = getSharedPreferences("SearchHistory", MODE_PRIVATE); Set<String> historySet = prefs.getStringSet("history", new HashSet<>()); List<Map<String, Object>> historyList = new ArrayList<>(); for (String placeInfo : historySet) { String[] parts = placeInfo.split("\\|"); if (parts.length == 3) { Map<String, Object> placeMap = new HashMap<>(); placeMap.put("name", parts[0]); placeMap.put("latitude", Double.parseDouble(parts[1])); placeMap.put("longitude", Double.parseDouble(parts[2])); historyList.add(placeMap); } } // 反转列表,让最新的记录在最前面 Collections.reverse(historyList); return historyList; }
步骤2:在选择地点时保存历史
修改你现有的onPlaceSelected方法,在里面调用保存历史的方法:
@Override public void onPlaceSelected(@NonNull Place place) { // 原有的代码不变... Log.i(TAG, "Place: " + place.getName() + ", " + place.getId()); LatLng selectedPlaceLatLng = place.getLatLng(); // ...(其他地图操作代码) // 新增:保存当前选择的地点到历史 saveSearchHistory(place); }
步骤3:展示历史记录
接下来把历史记录展示给用户,最简单的方式是在AutocompleteSupportFragment上方添加一个RecyclerView,用来显示最近的搜索记录。
- 修改布局文件(比如activity_main.xml):
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <!-- 搜索历史列表 --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/history_recycler" android:layout_width="match_parent" android:layout_height="wrap_content" android:visibility="gone"/> <!-- 原有的Autocomplete碎片 --> <fragment android:id="@+id/autocomplete_fragment" android:name="com.google.android.libraries.places.widget.AutocompleteSupportFragment" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout>
- 在Activity里初始化RecyclerView,设置适配器,并监听搜索框焦点来显示/隐藏历史:
private RecyclerView historyRecycler; private HistoryAdapter historyAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化RecyclerView historyRecycler = findViewById(R.id.history_recycler); historyRecycler.setLayoutManager(new LinearLayoutManager(this)); historyAdapter = new HistoryAdapter(getSearchHistory(), place -> { // 点击历史项时执行和选择地点一样的逻辑 LatLng selectedLatLng = new LatLng((double) place.get("latitude"), (double) place.get("longitude")); CameraPosition cameraPosition = new CameraPosition.Builder() .target(selectedLatLng) .zoom(20) .build(); mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); if (markerSelectedLoc != null) { markerSelectedLoc.remove(); } markerSelectedLoc = mMap.addMarker(new MarkerOptions() .position(selectedLatLng) .flat(true)); checkDistanceToCustomRoutes(selectedLatLng, mlocation); }); historyRecycler.setAdapter(historyAdapter); // 监听搜索框焦点变化,显示/隐藏历史 autocompleteFragment.getView().findViewById(R.id.places_autocomplete_search_input).setOnFocusChangeListener((v, hasFocus) -> { if (hasFocus && !getSearchHistory().isEmpty()) { historyRecycler.setVisibility(View.VISIBLE); } else { historyRecycler.setVisibility(View.GONE); } }); // 原有的Places初始化代码... } // 自定义RecyclerView适配器 private class HistoryAdapter extends RecyclerView.Adapter<HistoryAdapter.ViewHolder> { private List<Map<String, Object>> historyList; private OnHistoryItemClickListener listener; public interface OnHistoryItemClickListener { void onHistoryItemClick(Map<String, Object> place); } public HistoryAdapter(List<Map<String, Object>> historyList, OnHistoryItemClickListener listener) { this.historyList = historyList; this.listener = listener; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_history, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { Map<String, Object> place = historyList.get(position); holder.placeName.setText((String) place.get("name")); holder.itemView.setOnClickListener(v -> listener.onHistoryItemClick(place)); } @Override public int getItemCount() { return historyList.size(); } public class ViewHolder extends RecyclerView.ViewHolder { TextView placeName; public ViewHolder(@NonNull View itemView) { super(itemView); placeName = itemView.findViewById(R.id.place_name); } } }
- 创建item_history.xml布局文件(单条历史记录的样式):
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/place_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:textSize="16sp" android:background="?attr/selectableItemBackground"/>
方案二:自定义Autocomplete视图(更灵活)
如果你希望把历史记录直接整合到Google Places的自动建议列表里,那需要自定义整个搜索框和建议列表,而不是用官方的AutocompleteSupportFragment。这种方式灵活性更高,但代码量更大,需要自己实现搜索框,然后合并本地历史和Places的搜索结果后展示。
不过对于大多数场景来说,方案一已经足够好用,而且实现起来快很多~
额外小建议
- 可以给历史记录添加删除按钮,让用户手动清除不需要的记录
- 如果需要保存更详细的地点信息,可用Gson把Place对象转成Json字符串存储,读取时再解析回来
- SharedPreferences不需要额外权限,不用担心权限问题
备注:内容来源于stack exchange,提问作者Chichi
相关产品推荐
相关产品推荐

