AutoCompleteTextView全局匹配

本文介绍了如何修改Android中AutoCompleteTextView的默认匹配策略,通过重写ArrayAdapter使其支持全局匹配,而非仅限于前缀匹配。

       AutoCompleteTextView 这个东西做Android的应该都看过~没看过的去百度下吧,我就不多说了,问题是它怎么能自动补全的呢?

      这个是因为它setAdapter(adapter);,没错,你没看错,就是因为它设置了一个adapter。。。而它的过滤原则也是根据这个adapter中的Filter来的。

      一般网上的那些例子里面都是设置的一个ArrayAdapter,这个adapter里面的Filter看源码就能看出来是怎么过滤的:

 public Filter getFilter() {
        if (mFilter == null) {
            mFilter = new ArrayFilter();
        }
        return mFilter;
    }

    /**
     * <p>An array filter constrains the content of the array adapter with
     * a prefix. Each item that does not start with the supplied prefix
     * is removed from the list.</p>
     */
    private class ArrayFilter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence prefix) {
            FilterResults results = new FilterResults();

            if (mOriginalValues == null) {
                synchronized (mLock) {
                    mOriginalValues = new ArrayList<T>(mObjects);
                }
            }

            if (prefix == null || prefix.length() == 0) {
                ArrayList<T> list;
                synchronized (mLock) {
                    list = new ArrayList<T>(mOriginalValues);
                }
                results.values = list;
                results.count = list.size();
            } else {
                String prefixString = prefix.toString().toLowerCase();

                ArrayList<T> values;
                synchronized (mLock) {
                    values = new ArrayList<T>(mOriginalValues);
                }

                final int count = values.size();
                final ArrayList<T> newValues = new ArrayList<T>();

                for (int i = 0; i < count; i++) {
                    final T value = values.get(i);
                    final String valueText = value.toString().toLowerCase();

                    // First match against the whole, non-splitted value
                    if (valueText.startsWith(prefixString)) {//看到没有~这就是匹配原则。。。就是这么蛋疼,就是这么酸爽
                        newValues.add(value);
                    } else {
                        final String[] words = valueText.split(" ");
                        final int wordCount = words.length;

                        // Start at index 0, in case valueText starts with space(s)
                        for (int k = 0; k < wordCount; k++) {
                            if (words[k].startsWith(prefixString)) {
                                newValues.add(value);
                                break;
                            }
                        }
                    }
                }

                results.values = newValues;
                results.count = newValues.size();
            }

            return results;
        }

        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            //noinspection unchecked
            mObjects = (List<T>) results.values;
            if (results.count > 0) {
                notifyDataSetChanged();
            } else {
                notifyDataSetInvalidated();
            }
        }
    }

 所以,我的做法就是,直接复制整个ArrayAdapter类,然后将里面的那句原则改掉,改成  valueText.contains(prefixString)  ,这样在设置的时候设自定义的这个类,然后,然后就能看到全局匹配的AutoCompleteTextView了。

目前SearchResultActivity代码如下:package com.example.bus; import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.drawable.GradientDrawable; 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.view.inputmethod.InputMethodManager; 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; import java.util.regex.Pattern; import androidx.activity.OnBackPressedCallback; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; import android.app.AlertDialog; import android.view.ViewGroup; import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; 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; public static final String EXTRA_SEARCH_TYPE = "search_type"; private int searchType = 0; // 0=普通,1=线路 private final Map<String, PoiItem> suggestionPoiCache = new ConcurrentHashMap<>(); // 地图相关 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); String keyword = getIntent().getStringExtra("keyword"); if (keyword != null && !keyword.isEmpty()) { searchInput.setText(keyword); // 立即显示在输入框中 } try { geocodeSearch = new GeocodeSearch(this); geocodeSearch.setOnGeocodeSearchListener(this); } catch (Exception e) { e.printStackTrace(); } // 保留原来的 pendingKeyword 用于后续搜索控制 pendingKeyword = keyword; // 接收搜索类型 searchType = getIntent().getIntExtra(EXTRA_SEARCH_TYPE, 0); // 初始化 suggestHelper suggestHelper = new RealTimePoiSuggestHelper(this); suggestHelper.setCurrentCity(currentCity); suggestHelper.setUseDistanceSort(true); suggestHelper.setPoiCallback(poiItems -> { if (poiItems.length > 0) { List<String> titles = new ArrayList<>(); suggestionPoiCache.clear(); // 清空旧缓存 for (PoiItem item : poiItems) { String title = item.getTitle(); titles.add(title); suggestionPoiCache.put(title, item); // ✅ 缓存标题 → POI } ArrayAdapter<String> adapter = new ArrayAdapter<>( SearchResultActivity.this, android.R.layout.simple_dropdown_item_1line, titles ) { @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); view.setOnClickListener(v -> { String selectedText = getItem(position); if (selectedText != null) { searchInput.setText(selectedText); searchInput.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0); // ✅ 优先使用真实 POI PoiItem cachedItem = suggestionPoiCache.get(selectedText); if (cachedItem != null) { handleSelectedPoiItem(cachedItem); } else { searchBtn.performClick(); } } }); return view; } }; new Handler(Looper.getMainLooper()).post(() -> { searchInput.setAdapter(adapter); if (getCurrentFocus() == searchInput) { searchInput.showDropDown(); } }); } else { new Handler(Looper.getMainLooper()).post(() -> searchInput.setAdapter(null)); } }); // 使用 OnBackPressedDispatcher 兼容手势返回 getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { Intent result = new Intent(); String currentText = searchInput.getText().toString().trim(); result.putExtra("updated_keyword", currentText); // 🔥 加上这一行即可! result.putExtra(SearchResultActivity.EXTRA_SEARCH_TYPE, searchType); setResult(RESULT_OK, result); finish(); } }); // =========== 初始化搜索图标与点击事件 =========== updateSearchIcon(); // 设置初始图标和 hint searchInput.setOnTouchListener((v, event) -> { if (event.getAction() == android.view.MotionEvent.ACTION_UP) { Drawable[] drawables = searchInput.getCompoundDrawables(); Drawable leftDrawable = drawables[0]; if (leftDrawable instanceof LayerDrawable) { int totalIconWidth = searchInput.getCompoundPaddingLeft(); if (event.getX() < totalIconWidth && event.getX() > 0) { showSearchTypeDialog(); return true; } } } return false; }); // ================= END ================= } private void handleSelectedPoiItem(PoiItem item) { searchInput.setText(item.getTitle()); searchInput.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0); } searchInput.setAdapter(null); poiList.clear(); nationalResults.clear(); localResults.clear(); nearbyResults.clear(); updateResultList(Arrays.asList(item)); onPoiItemSelected(item); resultListView.scrollToPosition(0); emptyView.setVisibility(View.GONE); resultListView.setVisibility(View.VISIBLE); } /** * ✅ 更新搜索框左侧图标:主图标 + 右侧小下拉箭头 + 竖线分隔符 * 与 HomeFragment 完全保持一致的视觉效果和布局逻辑 */ private void updateSearchIcon() { // 搜索类型常量(建议提取成全局常量,这里为了独立可运行保留) final int SEARCH_TYPE_NORMAL = 0; final int SEARCH_TYPE_LINE = 1; int mainIconRes = searchType == SEARCH_TYPE_NORMAL ? R.drawable.ic_location : R.drawable.ic_subway; Drawable mainDrawable = ContextCompat.getDrawable(this, mainIconRes); Drawable dropdownArrow = ContextCompat.getDrawable(this, R.drawable.ic_arrow_drop_down); if (mainDrawable == null || dropdownArrow == null) return; float density = getResources().getDisplayMetrics().density; // ======== 尺寸定义(与 HomeFragment 严格一致)======== int iconSize = (int) (20 * density); // 图标大小统一为 20dp int gapBetween = 0; // 主图标与箭头之间无间隙 int dividerWidth = (int) (1 * density); // 竖线宽度 1px int paddingAfterDivider = (int) (2 * density); // 竖线到文字的距离 = 12dp int lineHeight = searchInput.getLineHeight(); int drawableTop = (lineHeight - iconSize) / 2; // 垂直居中 // 扩展竖线高度(上下多出 6dp) int extend = (int)(6 * density); int extendedDividerHeight = lineHeight + 2 * extend; // 创建 LayerDrawable 所需图层 GradientDrawable verticalDividerShape = new GradientDrawable(); verticalDividerShape.setShape(GradientDrawable.RECTANGLE); verticalDividerShape.setColor(0xFFCCCCCC); // 浅灰 LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{ mainDrawable, dropdownArrow, verticalDividerShape }); // 计算位置 int arrowLeft = iconSize + gapBetween; // 箭头紧贴主图标右侧 int dividerLeft = arrowLeft + iconSize; // 分割线在箭头之后 int totalWidth = dividerLeft + dividerWidth + paddingAfterDivider; // 总宽 // 设置主图标 int rightOfMain = totalWidth - iconSize; layerDrawable.setLayerSize(0, iconSize, iconSize); layerDrawable.setLayerInset(0, 0, drawableTop, rightOfMain, drawableTop); // 设置箭头图标 layerDrawable.setLayerSize(1, iconSize, iconSize); layerDrawable.setLayerInset(1, arrowLeft, drawableTop, totalWidth - arrowLeft - iconSize, drawableTop); // 设置竖线 layerDrawable.setLayerSize(2, dividerWidth, extendedDividerHeight); layerDrawable.setLayerInset( 2, dividerLeft, -extend, totalWidth - dividerLeft - dividerWidth, -extend ); // 设置整个 LayerDrawable 的 bounds layerDrawable.setBounds(0, 0, totalWidth, lineHeight); // 应用到 EditText searchInput.setCompoundDrawables(layerDrawable, null, null, null); searchInput.setCompoundDrawablePadding(paddingAfterDivider); } /** * 弹出搜索类型选择对话框 */ private void showSearchTypeDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("选择搜索类型"); String[] items = {"📍 地点搜索", "🚇 公交线路搜索"}; Drawable[] icons = { ContextCompat.getDrawable(this, R.drawable.ic_location), ContextCompat.getDrawable(this, R.drawable.ic_subway) }; ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.select_dialog_singlechoice, items) { @Override public View getView(int position, View convertView, ViewGroup parent) { TextView tv = (TextView) super.getView(position, convertView, parent); tv.setCompoundDrawablesRelativeWithIntrinsicBounds(icons[position], null, null, null); tv.setCompoundDrawablePadding(16); return tv; } }; builder.setAdapter(adapter, (dialog, which) -> { int oldType = searchType; searchType = which == 0 ? 0 : 1; // 0=普通,1=线路 if (oldType != searchType) { updateSearchIcon(); Toast.makeText(this, searchType == 0 ? "已切换为【地点】模式" : "已切换为【线路】模式", Toast.LENGTH_SHORT).show(); } }); builder.show(); } 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; } String keyword = s.toString().trim(); pendingRunnable[0] = () -> fetchSuggestionTitles(keyword, searchInput); 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(); } }); } /** * 获取实时建议并按【匹配度 + 地理】排序显示(与 showCombinedResults 保持一致) */ private void fetchSuggestionTitles(String keyword, android.widget.AutoCompleteTextView inputView) { if (keyword.isEmpty()) return; CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; // 如果指定了城市,则只查该城市,不参与融合排序 if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(this, query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<PoiItem> list = result.getPois(); List<String> titles = sortAndExtractTitles(list, searchKeyword, explicitCity); updateSuggestionAdapter(titles.toArray(new String[0]), inputView); } else { updateSuggestionAdapter(new String[0], inputView); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); } catch (Exception e) { updateSuggestionAdapter(new String[0], inputView); } return; } // ====== 融合搜索:全国 + 本地 并统一排序 ====== List<PoiItem> nationalList = new ArrayList<>(); List<PoiItem> localList = new ArrayList<>(); // Step 1: 请求全国范围 POI 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) { nationalList.addAll(result.getPois()); } requestLocalForSuggestions(searchKeyword, nationalList, localList, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nationalSearch.searchPOIAsyn(); } catch (Exception e) { requestLocalForSuggestions(searchKeyword, nationalList, localList, inputView); } } /** * 请求本地 POI 并合并排序 */ private void requestLocalForSuggestions(String keyword, List<PoiItem> national, List<PoiItem> local, android.widget.AutoCompleteTextView inputView) { if (currentCity.isEmpty()) { showSuggestionResultSorted(national, local, keyword, inputView); 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) { local.addAll(result.getPois()); } showSuggestionResultSorted(national, local, keyword, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { showSuggestionResultSorted(national, local, keyword, inputView); } } /** * 合并、去重、排序并提取标题(与 showCombinedResults 完全一致) */ private void showSuggestionResultSorted(List<PoiItem> national, List<PoiItem> local, String keyword, android.widget.AutoCompleteTextView inputView) { Set<String> seenIds = new HashSet<>(); List<PoiItem> combined = new ArrayList<>(); // 先加本地再加全国,保留本地数据优先(防重复) for (PoiItem item : local) { if (seenIds.add(item.getPoiId())) { combined.add(item); } } for (PoiItem item : national) { if (seenIds.add(item.getPoiId())) { combined.add(item); } } // ✅ 使用与 showCombinedResults 完全一致的排序逻辑 final String coreKeyword = keyword.toLowerCase().trim(); combined.sort((a, b) -> { int scoreA = calculateMatchScore(a.getTitle(), coreKeyword); int scoreB = calculateMatchScore(b.getTitle(), coreKeyword); if (scoreA != scoreB) { return Integer.compare(scoreB, scoreA); // 高分优先 } boolean aIsLocal = isSameCity(getDisplayCity(a), currentCity); boolean bIsLocal = isSameCity(getDisplayCity(b), currentCity); if (aIsLocal && !bIsLocal) return -1; if (!aIsLocal && bIsLocal) return 1; return 0; }); // 提取前 10 个标题 List<String> titles = new ArrayList<>(); for (int i = 0; i < Math.min(combined.size(), 10); i++) { titles.add(combined.get(i).getTitle()); } updateSuggestionAdapter(titles.toArray(new String[0]), inputView); } /** * 对单个城市的结果排序并提取标题 */ private List<String> sortAndExtractTitles(List<PoiItem> list, String keyword, String city) { final String coreKeyword = keyword.toLowerCase().trim(); return list.stream() .sorted((a, b) -> { int scoreA = calculateMatchScore(a.getTitle(), coreKeyword); int scoreB = calculateMatchScore(b.getTitle(), coreKeyword); if (scoreA != scoreB) { return Integer.compare(scoreB, scoreA); } boolean aIsLocal = isSameCity(getDisplayCity(a), city); boolean bIsLocal = isSameCity(getDisplayCity(b), city); if (aIsLocal && !bIsLocal) return -1; if (!aIsLocal && bIsLocal) return 1; return 0; }) .map(PoiItem::getTitle) .limit(10) .collect(java.util.stream.Collectors.toList()); } private void requestLocalForSuggestion(String keyword, List<PoiItem> national, List<PoiItem> local, android.widget.AutoCompleteTextView inputView) { if (currentCity.isEmpty()) { showSuggestionResult(national, local, inputView); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(this, localQuery); localSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { local.addAll(result.getPois()); } showSuggestionResult(national, local, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { showSuggestionResult(national, local, inputView); } } private void showSuggestionResult(List<PoiItem> national, List<PoiItem> local, android.widget.AutoCompleteTextView inputView) { Set<String> seen = new HashSet<>(); List<String> combined = new ArrayList<>(); for (PoiItem item : local) { if (seen.add(item.getPoiId())) { combined.add(item.getTitle()); } } for (PoiItem item : national) { if (seen.add(item.getPoiId())) { combined.add(item.getTitle()); } } updateSuggestionAdapter(combined.toArray(new String[0]), inputView); } private void updateSuggestionAdapter(String[] suggestions, android.widget.AutoCompleteTextView inputView) { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( this, android.R.layout.simple_dropdown_item_1line, suggestions ) { @Override public View getView(int position, View convertView, android.view.ViewGroup parent) { View view = super.getView(position, convertView, parent); // 👉 关键:点击建议项 → 填文本 + 隐藏键盘 + 立刻搜索 view.setOnClickListener(v -> { String selectedText = getItem(position); if (selectedText != null) { inputView.setText(selectedText); inputView.clearFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(inputView.getWindowToken(), 0); } // 🔥 核心:直接执行搜索(闭合路径) searchBtn.performClick(); // 触发完整搜索流程 } }); return view; } }; new Handler(Looper.getMainLooper()).post(() -> { inputView.setAdapter(adapter); if (getCurrentFocus() == inputView) { inputView.showDropDown(); } }); } else { new Handler(Looper.getMainLooper()).post(() -> inputView.setAdapter(null)); } } private void performSearchWithKeyword(String keyword) { searchInput.setText(keyword); searchInput.clearFocus(); searchBtn.performClick(); } private void performSearch(String keyword) { if (keyword.isEmpty()) return; // 【保持原有状态重置】 nationalResults.clear(); localResults.clear(); nearbyResults.clear(); isNearbyLoaded = false; isInNearbyMode = false; btnToggleMode.setText("📍 附近"); emptyView.setText("🔍 搜索中..."); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); // ✅ 是否为交通线路关键词 boolean isLineQuery = keyword.matches(".*?(地铁|公交|轻轨|云巴|磁浮|有轨电车|brt|apm).*?\\d+.*") || Pattern.compile("(s|r|x)\\d+", Pattern.CASE_INSENSITIVE).matcher(keyword).find() || keyword.contains("云巴") || keyword.contains("轻轨"); if (isLineQuery) { // 提取数字部分作为核心关键词(如“14”) String numberPart = keyword.replaceAll("[^\\d]", ""); String baseKeyword = numberPart.isEmpty() ? keyword : numberPart + "号线"; // 判断类型 String poiType = keyword.contains("地铁") || keyword.contains("轨道") ? "subway" : keyword.contains("公交") ? "bus_station" : keyword.contains("云巴") ? "bus_station" : keyword.contains("轻轨") ? "light_rail" : ""; // 构造查询 PoiSearch.Query query = new PoiSearch.Query(baseKeyword, poiType, currentCity.isEmpty() ? "" : currentCity); query.setPageSize(50); // 尽量多拿 try { PoiSearch search = new PoiSearch(this, query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<PoiItem> filtered = new ArrayList<>(); String lowerKey = keyword.toLowerCase(); for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase(); if (title.contains(lowerKey) || title.contains(baseKeyword.toLowerCase())) { filtered.add(item); } } if (!filtered.isEmpty()) { updateResultList(filtered); return; } } // 失败 → 走原搜索逻辑(下面会复用原有代码) fallbackToOriginalSearch(keyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); return; // ✅ 提前返回,等待回调 } catch (Exception e) { // 异常则继续走原逻辑 } } // ❌ 不是线路 → 执行原有完整逻辑(从这里开始复制原逻辑) CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(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 fallbackToOriginalSearch(String keyword) { // 直接调用原搜索逻辑 CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(this, query); search.setOnPoiSearchListener(SearchResultActivity.this); search.searchPOIAsyn(); } catch (Exception e) { emptyView.setText("⚠️ 未找到相关地点"); emptyView.setVisibility(View.VISIBLE); resultListView.setVisibility(View.GONE); } } else { nationalResults.clear(); localResults.clear(); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch ns = new PoiSearch(this, nationalQuery); ns.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); ns.searchPOIAsyn(); } catch (Exception e) { requestLocalSearch(searchKeyword); } } } private void requestLocalSearch(String keyword) { if (currentCity.isEmpty()) { showCombinedResults(); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(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(); } } /** * 排序规则: * 1. 核心关键词匹配度最高优先 * 2. 若匹配度相同 → 本市结果优先 * 3. 高匹配度全国结果可排在低匹配度本地结果前 */ private void showCombinedResults() { String keyword = searchInput.getText().toString().trim(); CityManager.ParsedQuery parsed = CityManager.parse(keyword); final String coreKeyword = (parsed.keyword.isEmpty() ? keyword : parsed.keyword).toLowerCase().trim(); Set<String> seen = new HashSet<>(); List<PoiItem> combined = new ArrayList<>(); // 合并去重 for (PoiItem item : localResults) { if (seen.add(item.getPoiId())) { combined.add(item); } } for (PoiItem item : nationalResults) { if (seen.add(item.getPoiId())) { combined.add(item); } } // ✅ 排序:使用 final 变量,并将评分逻辑封装在局部 final 引用中 final String finalCoreKeyword = coreKeyword; combined.sort((a, b) -> { int scoreA = calculateMatchScore(a.getTitle(), finalCoreKeyword); int scoreB = calculateMatchScore(b.getTitle(), finalCoreKeyword); if (scoreA != scoreB) { return Integer.compare(scoreB, scoreA); // 高分优先 } boolean aIsLocal = isSameCity(getDisplayCity(a), currentCity); boolean bIsLocal = isSameCity(getDisplayCity(b), currentCity); if (aIsLocal && !bIsLocal) return -1; if (!aIsLocal && bIsLocal) return 1; return 0; }); adapter.clearExtraText(); for (PoiItem item : combined) { String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } updateResultList(combined); } /** * 计算标题与关键词的核心匹配得分 * 规则: * - 完全包含关键词:+50 * - 开头匹配加分 * - 分词命中加分 */ private int calculateMatchScore(String title, String keyword) { if (title == null || keyword == null || title.isEmpty() || keyword.isEmpty()) { return 0; } title = title.toLowerCase(); int score = 0; // 完整包含关键词 if (title.contains(keyword)) { score += 50; // 越靠前分越高 int index = title.indexOf(keyword); if (index == 0) score += 20; else if (index < 4) score += 10; } // 分词命中(如“北京 大学”拆开都出现) String[] words = keyword.split("\\s+"); int matchCount = 0; for (String word : words) { if (word.length() > 1 && title.contains(word)) { matchCount++; } } if (matchCount == words.length) { score += 30; // 全部命中 } else { score += matchCount * 8; // 部分命中也有分 } return score; } 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 { if (rCode == 1000) { emptyView.setText("⚠️ 未找到相关地点"); } else { // 是 API 错误 handleSearchError(rCode); 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); } } 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(this, msg, Toast.LENGTH_SHORT).show(); } @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; } } 警告:Private method 'requestLocalForSuggestion(java.lang.String, java.util.List<com.amap.api.services.core.PoiItem>, java.util.List<com.amap.api.services.core.PoiItem>, android.widget.AutoCompleteTextView)' is never used
最新发布
12-17
package com.example.kucun2.ui.jinhuo; import android.app.AlertDialog; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.Spinner; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.example.kucun2.DataPreserver.Data; import com.example.kucun2.R; import com.example.kucun2.entity.*; import com.example.kucun2.function.Adapter; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; public class AddInventoryFragment extends Fragment implements Data.OnDataChangeListener { // 视图组件 private AutoCompleteTextView actvDingdan, actvChanpin, actvZujian, actvBancai; private EditText etQuantity; private RadioGroup rgType; private Button btnNewDingdan, btnAddChanpin, btnAddZujian, btnSubmit; // 适配器 private Adapter.FilterableAdapter<Dingdan> dingdanAdapter; private Adapter.FilterableAdapter<Chanpin> chanpinAdapter; private Adapter.FilterableAdapter<Zujian> zujianAdapter; private Adapter.FilterableAdapter<Bancai> bancaiAdapter; // 当前选择 private Dingdan selectedDingdan; private Chanpin selectedChanpin; private Zujian selectedZujian; private Bancai selectedBancai; // 数据列表 private List<Dingdan> dingdanList; private List<Chanpin> chanpinList; private List<Zujian> zujianList; private List<Bancai> bancaiList; // 当前用户 private User currentUser; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取当前用户 currentUser = Data.getCurrentUser(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_add_inventory, container, false); initViews(view); initData(); setupSpinners(); setupListeners(); applyPermissionRestrictions(); // 应用权限限制 return view; } /** * 初始化视图组件 */ private void initViews(View view) { actvDingdan = view.findViewById(R.id.actv_dingdan); actvChanpin = view.findViewById(R.id.actv_chanpin); actvZujian = view.findViewById(R.id.actv_zujian); actvBancai = view.findViewById(R.id.actv_bancai); etQuantity = view.findViewById(R.id.et_shuliang); rgType = view.findViewById(R.id.rg_type); btnNewDingdan = view.findViewById(R.id.btn_new_dingdan); btnAddChanpin = view.findViewById(R.id.btn_add_chanpin); btnAddZujian = view.findViewById(R.id.btn_add_zujian); btnSubmit = view.findViewById(R.id.btn_submit); // 初始禁用状态 actvChanpin.setEnabled(false); actvZujian.setEnabled(false); etQuantity.setEnabled(false); } private void initData() { // 从全局数据获取列表 dingdanList = Data.dingdans().getViewList(); chanpinList = Data.chanpins().getViewList(); zujianList = Data.zujians().getViewList(); bancaiList = Data.bancais().getViewList(); } /** * 设置下拉框适配器 */ private void setupSpinners() { // 3. 创建支持筛选的适配器 dingdanAdapter = Adapter.createDingdanFilterableAdapter(requireContext(), dingdanList); actvDingdan.setAdapter(dingdanAdapter); chanpinAdapter = Adapter.createChanpinFilterableAdapter(requireContext(), new ArrayList<>()); actvChanpin.setAdapter(chanpinAdapter); zujianAdapter = Adapter.createZujianFilterableAdapter(requireContext(), new ArrayList<>()); actvZujian.setAdapter(zujianAdapter); bancaiAdapter = Adapter.createBancaiFilterableAdapter(requireContext(), bancaiList); actvBancai.setAdapter(bancaiAdapter); } /** * 设置事件监听器 */ private void setupListeners() { // 4. 设置新的点击事件监听器 actvDingdan.setOnItemClickListener((parent, view, position, id) -> { selectedDingdan = dingdanAdapter.getItem(position); updateChanpinSpinner(); actvChanpin.setEnabled(selectedDingdan != null); if (selectedDingdan == null) { // 清空后续选择 actvChanpin.setText(""); selectedChanpin = null; actvZujian.setText(""); selectedZujian = null; actvBancai.setText(""); selectedBancai = null; etQuantity.setText(""); etQuantity.setEnabled(false); } }); // 产品选择监听 actvChanpin.setOnItemClickListener((parent, view, position, id) -> { selectedChanpin = chanpinAdapter.getItem(position); updateZujianSpinner(); actvZujian.setEnabled(selectedChanpin != null); if (selectedChanpin == null) { // 清空后续选择 actvZujian.setText(""); selectedZujian = null; actvBancai.setText(""); selectedBancai = null; etQuantity.setText(""); etQuantity.setEnabled(false); } }); // 组件选择监听 actvZujian.setOnItemClickListener((parent, view, position, id) -> { selectedZujian = zujianAdapter.getItem(position); updateBancaiSpinner(); // 组件选择后锁定板材下拉框 actvBancai.setEnabled(false); }); // 板材选择监听 actvBancai.setOnItemClickListener((parent, view, position, id) -> { selectedBancai = bancaiAdapter.getItem(position); etQuantity.setEnabled(selectedBancai != null); if (selectedBancai == null) { etQuantity.setText(""); } }); // 新建订单 btnNewDingdan.setOnClickListener(v -> showNewDingdanDialog()); // 添加产品 btnAddChanpin.setOnClickListener(v -> showAddChanpinDialog()); // 添加组件 btnAddZujian.setOnClickListener(v -> showAddZujianDialog()); // 提交 btnSubmit.setOnClickListener(v -> submitInventory()); } /** * 根据用户角色应用权限限制 */ private void applyPermissionRestrictions() { if (currentUser == null) return; int role = currentUser.getRole(); if (role == 0) { // 普通用户 // 只能消耗,不能进货 rgType.check(R.id.rb_xiaohao); rgType.getChildAt(0).setEnabled(false); // 禁用进货选项 // 禁用新建订单、添加产品按钮 btnNewDingdan.setEnabled(false); btnAddChanpin.setEnabled(false); } } /** * 根据选定订单更新产品下拉框 */ private void updateChanpinSpinner() { List<Chanpin> filtered = new ArrayList<>(); if (selectedDingdan != null) { for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { filtered.add(dc.getChanpin()); } } // 5. 使用适配器的updateList方法更新数据 chanpinAdapter.updateList(filtered); } /** * 根据选定产品更新组件下拉框 */ private void updateZujianSpinner() { List<Zujian> filtered = new ArrayList<>(); if (selectedChanpin != null) { for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { filtered.add(cz.getZujian()); } } zujianAdapter.updateList(filtered); } /** * 根据选定组件更新板材下拉框 */ private void updateBancaiSpinner() { List<Bancai> filtered = new ArrayList<>(); if (selectedZujian != null && selectedChanpin != null) { // 查找组件关联的板材 for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { if (cz.getZujian().equals(selectedZujian)) { filtered.add(cz.getBancai()); // 自动选中关联的板材 selectedBancai = cz.getBancai(); actvBancai.setText(selectedBancai.TableText()); etQuantity.setEnabled(true); break; } } bancaiAdapter.updateList(filtered); } else { // 没有选择组件时显示所有板材 filtered = new ArrayList<>(bancaiList); bancaiAdapter.updateList(filtered); } } /** * 显示新建订单对话框 */ private void showNewDingdanDialog() { // 权限检查 if (currentUser != null && currentUser.getRole() == 0) { Toast.makeText(requireContext(), "您无权创建新订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_dingdan, null); EditText etNumber = view.findViewById(R.id.et_order_number); builder.setView(view) .setTitle("新建订单") .setPositiveButton("保存", (dialog, which) -> { Dingdan newDingdan = new Dingdan(); newDingdan.setNumber(etNumber.getText().toString()); // 添加到全局数据 Data.add(newDingdan); }) .setNegativeButton("取消", null) .show(); } /** * 显示添加产品对话框 */ private void showAddChanpinDialog() { // 权限检查 if (currentUser != null && currentUser.getRole() == 0) { Toast.makeText(requireContext(), "您无权添加产品", Toast.LENGTH_SHORT).show(); return; } if (selectedDingdan == null) { Toast.makeText(requireContext(), "请先选择订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_chanpin, null); Spinner spinner = view.findViewById(R.id.spinner_chanpin_selection); EditText etQuantity = view.findViewById(R.id.et_chanpin_quantity); // 设置产品列表(排除已关联的) List<Chanpin> available = new ArrayList<>(chanpinList); for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { available.remove(dc.getChanpin()); } Adapter.setupChanpinSpinner(spinner, available, requireContext()); // 添加新建产品按钮 Button btnNewChanpin = view.findViewById(R.id.btn_new_chanpin); builder.setView(view) .setTitle("添加产品到订单") .setPositiveButton("添加", (dialog, which) -> { Chanpin selected = (Chanpin) spinner.getSelectedItem(); int quantity = Integer.parseInt(etQuantity.getText().toString().trim()); // 检查是否已存在关联 for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { if (dc.getChanpin().equals(selected)) { Toast.makeText(requireContext(), "该产品已添加到订单", Toast.LENGTH_SHORT).show(); return; } } // 创建订单-产品关联 Dingdan_chanpin dc = new Dingdan_chanpin(); dc.setDingdan(selectedDingdan); dc.setChanpin(selected); dc.setShuliang(quantity); // 添加到全局数据 Data.add(dc); selectedDingdan.getDingdan_chanpin().add(dc); }) .show(); // 新建产品按钮点击事件 btnNewChanpin.setOnClickListener(v -> showNewChanpinDialog(available, spinner)); } // 实现新建产品对话框 private void showNewChanpinDialog(List<Chanpin> available, Spinner spinner) { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_chanpin, null); EditText etBianhao = view.findViewById(R.id.et_chanpin_name); builder.setView(view) .setTitle("新建产品") .setPositiveButton("保存", (dialog, which) -> { String bianhao = etBianhao.getText().toString().trim(); if (bianhao.isEmpty()) { Toast.makeText(requireContext(), "产品编号不能为空", Toast.LENGTH_SHORT).show(); return; } // 创建新产品 Chanpin newChanpin = new Chanpin(); newChanpin.setBianhao(bianhao); // 添加到全局数据 Data.add(newChanpin); // 更新可用列表和适配器 available.add(newChanpin); }) .setNegativeButton("取消", null) .show(); } /** * 显示添加组件对话框 */ private void showAddZujianDialog() { if (selectedChanpin == null) { Toast.makeText(requireContext(), "请先选择产品", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_create_zujian_bancai, null); Spinner spinnerZujian = view.findViewById(R.id.et_zujian_name); Spinner spinnerBancai = view.findViewById(R.id.spinner_bancai); EditText etOneHowmany = view.findViewById(R.id.number_one_howmany); // 设置组件下拉框 Adapter.setupZujianSpinner(spinnerZujian, zujianList, requireContext()); // 设置板材下拉框 Adapter.setupBancaiSpinners(spinnerBancai, bancaiList, requireContext()); builder.setView(view) .setTitle("添加组件到产品") .setPositiveButton("添加", (dialog, which) -> { Zujian zujian = (Zujian) spinnerZujian.getSelectedItem(); Bancai bancai = (Bancai) spinnerBancai.getSelectedItem(); double oneHowmany = Double.parseDouble(etOneHowmany.getText().toString()); // 检查是否已存在关联 for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { if (cz.getZujian().equals(zujian) && cz.getBancai().equals(bancai)) { Toast.makeText(requireContext(), "该组件已添加到产品", Toast.LENGTH_SHORT).show(); return; } } // 创建产品-组件关联 Chanpin_Zujian cz = new Chanpin_Zujian(); cz.setChanpin(selectedChanpin); cz.setZujian(zujian); cz.setBancai(bancai); cz.setOne_howmany(oneHowmany); zujian.getChanpin_zujian().add(cz); selectedChanpin.getChanpin_zujian().add(cz); // 添加到全局数据 Data.add(cz); }) .show(); } /** * 提交库存操作(进货/消耗) */ private void submitInventory() { // 获取数量 int quantity; try { quantity = Integer.parseInt(etQuantity.getText().toString()); } catch (NumberFormatException e) { Toast.makeText(requireContext(), "请输入有效数量", Toast.LENGTH_SHORT).show(); return; } // 获取操作类型 boolean isJinhuo = rgType.getCheckedRadioButtonId() == R.id.rb_jinhuo; // 权限检查:普通用户只能消耗 if (currentUser != null && currentUser.getRole() == 0 && isJinhuo) { Toast.makeText(requireContext(), "您只能执行生产操作", Toast.LENGTH_SHORT).show(); return; } // 创建库存记录 Jinhuo record = new Jinhuo(); record.setShuliang(isJinhuo ? quantity : -quantity); // 正数为进货,负数为消耗 record.setDate(new Date()); record.setUser(currentUser); // 设置关联关系 if (selectedBancai != null) { // 创建订单-板材关联(如果不存在) Dingdan_bancai db = createOrUpdateDingdanBancai(); record.setDingdan_bancai(db); } // 添加到全局数据 Data.add(record); Toast.makeText(requireContext(), "操作成功", Toast.LENGTH_SHORT).show(); resetForm(); } /** * 创建或更新订单-板材关联记录 */ private Dingdan_bancai createOrUpdateDingdanBancai() { // 检查是否已存在关联 Dingdan_bancai existing = findExistingDingdanBancai(); if (existing != null) { // 更新现有记录 return existing; } // 创建新关联 Dingdan_bancai db = new Dingdan_bancai(); if (selectedDingdan != null) db.setDingdan(selectedDingdan); if (selectedChanpin != null) db.setChanpin(selectedChanpin); if (selectedZujian != null) db.setZujian(selectedZujian); if (selectedBancai != null) db.setBancai(selectedBancai); // 添加到全局数据 Data.add(db); return db; } /** * 查找现有订单-板材关联记录 */ private Dingdan_bancai findExistingDingdanBancai() { for (Dingdan_bancai db : Data.Dingdan_bancais().getViewList()) { boolean matchDingdan = (selectedDingdan == null && db.getDingdan() == null) || (selectedDingdan != null && selectedDingdan.equals(db.getDingdan())); boolean matchChanpin = (selectedChanpin == null && db.getChanpin() == null) || (selectedChanpin != null && selectedChanpin.equals(db.getChanpin())); boolean matchZujian = (selectedZujian == null && db.getZujian() == null) || (selectedZujian != null && selectedZujian.equals(db.getZujian())); boolean matchBancai = selectedBancai != null && selectedBancai.equals(db.getBancai()); if (matchDingdan && matchChanpin && matchZujian && matchBancai) { return db; } } return null; } /** * 重置表单到初始状态 */ private void resetForm() { actvDingdan.setSelection(0); actvChanpin.setSelection(0); actvZujian.setSelection(0); actvBancai.setSelection(0); etQuantity.setText(""); rgType.check(R.id.rb_jinhuo); } @Override public void onResume() { super.onResume(); Data.addDataChangeListener(this); } @Override public void onPause() { super.onPause(); Data.removeDataChangeListener(this); } @Override public void onDataChanged(Class<?> entityClass, String operationType, Integer itemId) { // 6. 更新适配器数据 if (entityClass == Dingdan.class) { dingdanList = Data.dingdans().getViewList(); dingdanAdapter.updateList(dingdanList); // 尝试选中新添加的订单 if (operationType.equals("add")) { for (int i = 0; i < dingdanList.size(); i++) { if (Objects.equals(dingdanList.get(i).getId(), itemId)) { actvDingdan.setText(dingdanList.get(i).getNumber(), false); selectedDingdan = dingdanList.get(i); break; } } } } else if (entityClass == Chanpin.class) { chanpinList = Data.chanpins().getViewList(); updateChanpinSpinner(); } else if (entityClass == Zujian.class) { zujianList = Data.zujians().getViewList(); updateZujianSpinner(); } else if (entityClass == Bancai.class) { bancaiList = Data.bancais().getViewList(); bancaiAdapter.updateList(bancaiList); } } } 进入页面之后默认时消耗,消耗逻辑时查看dingdan_bancai中有没有订单产品组件都相同的,有就消耗该dingdan_bancai数量,不够或没有就查看订单产品相同,在不够或没有就查看订单,订单数量不够就剪成负数,订单也没有就只能创建一个订单板材记录负数,板材必须相同,dingdan_bancai中订单产品组件都可能为空,匹配时忽略的匹配
07-01
代码如下:package com.example.bus.ui.map; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.Editable; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.constraintlayout.widget.ConstraintSet; import com.amap.api.maps.AMap; import com.amap.api.maps.AMapUtils; import com.amap.api.maps.CameraUpdateFactory; import com.amap.api.maps.MapView; import com.amap.api.maps.UiSettings; import com.amap.api.maps.model.BitmapDescriptorFactory; import com.amap.api.maps.model.LatLng; import com.amap.api.maps.model.Marker; import com.amap.api.maps.model.MarkerOptions; import com.amap.api.maps.model.MyLocationStyle; import com.amap.api.services.core.LatLonPoint; import com.amap.api.services.core.PoiItem; import com.amap.api.services.poisearch.PoiResult; import com.amap.api.services.poisearch.PoiSearch; import com.amap.api.services.geocoder.GeocodeSearch; import com.amap.api.services.geocoder.RegeocodeQuery; import com.amap.api.services.geocoder.RegeocodeResult; import com.example.bus.CityManager; import com.example.bus.R; import com.example.bus.RealTimePoiSuggestHelper; import com.example.bus.RoutePlanActivity; import com.example.bus.ResultAdapter; import com.example.bus.databinding.FragmentMapBinding; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import java.util.Arrays; import android.text.TextWatcher; 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(); activeSuggestHelper = new RealTimePoiSuggestHelper(requireContext()); activeSuggestHelper.setCurrentCity(currentCity); activeSuggestHelper.setUseDistanceSort(true); mapView = binding.mapView; mapView.onCreate(savedInstanceState); initViews(); setupMap(savedInstanceState); setupSearchSuggestion(); // ← 这个方法被重写了 return root; } private void initViews() { adapter = new ResultAdapter(poiList, this::onPoiItemSelected); binding.resultList.setLayoutManager(new LinearLayoutManager(requireContext())); binding.resultList.setAdapter(adapter); binding.mapSearch.setOnClickListener(v -> performSearch()); binding.btnSwitchTarget.setOnClickListener(v -> { if (selectionStage == 1) { showEndpointSelection(binding.mapInput2.getText().toString().trim()); } else if (selectionStage == 2) { showStartpointSelection(binding.mapInput1.getText().toString().trim()); } }); binding.btnGoTo.setOnClickListener(v -> { if (selectedStartPoi != null && selectedEndPoi != null) { Intent intent = new Intent(requireContext(), RoutePlanActivity.class); intent.putExtra(RoutePlanActivity.EXTRA_SOURCE, RoutePlanActivity.SOURCE_FROM_MAP_DIRECT); intent.putExtra("start_lat", selectedStartPoi.getLatLonPoint().getLatitude()); intent.putExtra("start_lng", selectedStartPoi.getLatLonPoint().getLongitude()); intent.putExtra("target_lat", selectedEndPoi.getLatLonPoint().getLatitude()); intent.putExtra("target_lng", selectedEndPoi.getLatLonPoint().getLongitude()); intent.putExtra("target_title", selectedEndPoi.getTitle()); startActivity(intent); } else { Toast.makeText(requireContext(), "请完成起点和终点的选择", Toast.LENGTH_SHORT).show(); } }); binding.btnToggleMode.setOnClickListener(v -> { if (isInNearbyMode) { exitNearbyMode(); } else { enterNearbyMode(); } }); } private void performSearch() { String startKeyword = binding.mapInput1.getText().toString().trim(); String endKeyword = binding.mapInput2.getText().toString().trim(); if (startKeyword.isEmpty()) { Toast.makeText(requireContext(), "请输入起点", Toast.LENGTH_SHORT).show(); return; } if (endKeyword.isEmpty()) { Toast.makeText(requireContext(), "请输入终点", Toast.LENGTH_SHORT).show(); return; } if (startKeyword.equals(lastStartKeyword) && endKeyword.equals(lastEndKeyword) && selectedStartPoi != null && selectedEndPoi != null) { binding.btnGoTo.performClick(); return; } binding.containerResultList.setVisibility(View.VISIBLE); binding.buttonGroup.setVisibility(View.VISIBLE); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(binding.getRoot()); constraintSet.connect( R.id.map_view, ConstraintSet.BOTTOM, R.id.container_result_list, ConstraintSet.TOP, 0 ); constraintSet.applyTo(binding.getRoot()); userHasInteracted = true; View currentFocus = requireActivity().getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0); } if (!startKeyword.equals(lastStartKeyword)) { lastStartKeyword = startKeyword; lastEndKeyword = endKeyword; showStartpointSelection(startKeyword); } else if (!endKeyword.equals(lastEndKeyword)) { lastEndKeyword = endKeyword; showEndpointSelection(endKeyword); } else if (selectedStartPoi == null) { showStartpointSelection(startKeyword); } else { showEndpointSelection(endKeyword); } } private void showStartpointSelection(String keyword) { selectionStage = 1; binding.btnSwitchTarget.setText("选择终点"); binding.btnGoTo.setEnabled(false); binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索起点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void showEndpointSelection(String keyword) { selectionStage = 2; binding.btnSwitchTarget.setText("选择起点"); binding.btnGoTo.setEnabled(false); binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索终点中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); doSearch(keyword); } private void doSearch(String keyword) { if (keyword.isEmpty()) return; nationalResults.clear(); localResults.clear(); nearbyResults.clear(); isNearbyLoaded = false; isInNearbyMode = false; binding.btnToggleMode.setText("📍 附近"); binding.emptyView.setText("🔍 搜索中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); // ✅ 线路判断 boolean isLineQuery = keyword.matches(".*?(地铁|公交|轻轨|云巴|磁浮|有轨电车|brt|apm).*?\\d+.*") || Pattern.compile("(s|r|x)\\d+", Pattern.CASE_INSENSITIVE).matcher(keyword).find() || keyword.contains("云巴") || keyword.contains("轻轨"); if (isLineQuery) { String numberPart = keyword.replaceAll("[^\\d]", ""); String baseKeyword = numberPart.isEmpty() ? keyword : numberPart + "号线"; String poiType = keyword.contains("地铁") || keyword.contains("轨道") ? "subway" : keyword.contains("公交") ? "bus_station" : keyword.contains("云巴") ? "bus_station" : keyword.contains("轻轨") ? "light_rail" : ""; PoiSearch.Query query = new PoiSearch.Query(baseKeyword, poiType, currentCity.isEmpty() ? "" : currentCity); query.setPageSize(50); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<PoiItem> filtered = new ArrayList<>(); String lowerKey = keyword.toLowerCase(); for (PoiItem item : result.getPois()) { String title = item.getTitle().toLowerCase(); if (title.contains(lowerKey) || title.contains(baseKeyword.toLowerCase())) { filtered.add(item); } } if (!filtered.isEmpty()) { updateResultList(filtered); return; } } // 失败 → 降级 fallbackToOriginalSearch(keyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); return; } catch (Exception e) { // 忽略异常,继续走原逻辑 } } // ❌ 原有逻辑(完全保留) CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(this); search.searchPOIAsyn(); } catch (Exception e) { Toast.makeText(requireContext(), "搜索失败", Toast.LENGTH_SHORT).show(); } } else { binding.emptyView.setText("🔍 搜索中..."); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch nationalSearch = new PoiSearch(requireContext(), nationalQuery); nationalSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); nationalSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); requestLocalSearch(searchKeyword); } } } private void fallbackToOriginalSearch(String keyword) { CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(MapFragment.this); search.searchPOIAsyn(); } catch (Exception e) { binding.emptyView.setText("⚠️ 未找到相关地点"); binding.emptyView.setVisibility(View.VISIBLE); binding.resultList.setVisibility(View.GONE); } } else { nationalResults.clear(); localResults.clear(); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch ns = new PoiSearch(requireContext(), nationalQuery); ns.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalResults.clear(); nationalResults.addAll(result.getPois()); } requestLocalSearch(searchKeyword); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); ns.searchPOIAsyn(); } catch (Exception e) { requestLocalSearch(searchKeyword); } } } private void requestLocalSearch(String keyword) { if (currentCity.isEmpty()) { showCombinedResults(); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch localSearch = new PoiSearch(requireContext(), localQuery); localSearch.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { localResults.clear(); localResults.addAll(result.getPois()); } showCombinedResults(); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); localSearch.searchPOIAsyn(); } catch (Exception e) { e.printStackTrace(); showCombinedResults(); } } /** * 排序规则: * 1. 名称匹配度最高优先 * 2. 相同匹配度下本市优先 */ private void showCombinedResults() { String keyword = getCurrentKeyword(); // MapFragment 使用这个;SearchResultActivity 改为 searchInput.getText() CityManager.ParsedQuery parsed = CityManager.parse(keyword); final String coreKeyword = (parsed.keyword.isEmpty() ? keyword : parsed.keyword).toLowerCase().trim(); Set<String> seen = new HashSet<>(); List<PoiItem> combined = new ArrayList<>(); // 合并去重 for (PoiItem item : localResults) { if (seen.add(item.getPoiId())) { combined.add(item); } } for (PoiItem item : nationalResults) { if (seen.add(item.getPoiId())) { combined.add(item); } } // ✅ 排序:使用 final 变量,并将评分逻辑封装在局部 final 引用中 final String finalCoreKeyword = coreKeyword; combined.sort((a, b) -> { int scoreA = calculateMatchScore(a.getTitle(), finalCoreKeyword); int scoreB = calculateMatchScore(b.getTitle(), finalCoreKeyword); if (scoreA != scoreB) { return Integer.compare(scoreB, scoreA); // 高分优先 } boolean aIsLocal = isSameCity(getDisplayCity(a), currentCity); boolean bIsLocal = isSameCity(getDisplayCity(b), currentCity); if (aIsLocal && !bIsLocal) return -1; if (!aIsLocal && bIsLocal) return 1; return 0; }); adapter.clearExtraText(); for (PoiItem item : combined) { String city = getDisplayCity(item); adapter.setExtraText(item, " | " + city); } updateResultList(combined); } /** * ✅ 与 SearchResultActivity 一致的匹配评分函数 */ private int calculateMatchScore(String title, String keyword) { if (title == null || keyword == null || title.isEmpty() || keyword.isEmpty()) { return 0; } title = title.toLowerCase(); int score = 0; if (title.contains(keyword)) { score += 50; int index = title.indexOf(keyword); if (index == 0) score += 20; else if (index < 4) score += 10; } String[] words = keyword.split("\\s+"); int matchCount = 0; for (String word : words) { if (word.length() > 1 && title.contains(word)) { matchCount++; } } if (matchCount == words.length) { score += 30; } else { score += matchCount * 8; } return score; } 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); } } /** * 【重写】使用本地 POI 查询 + 统一排序替代 suggestHelper * 保证建议排序与 showCombinedResults 一致 */ private void setupSearchSuggestion() { Handler handler = new Handler(Looper.getMainLooper()); Runnable[] pendingRunnable1 = {null}; Runnable[] pendingRunnable2 = {null}; // 输入框1监听 binding.mapInput1.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (pendingRunnable1[0] != null) handler.removeCallbacks(pendingRunnable1[0]); if (s.length() == 0) { binding.mapInput1.setAdapter(null); return; } String keyword = s.toString().trim(); pendingRunnable1[0] = () -> fetchSuggestionTitles(keyword, binding.mapInput1); handler.postDelayed(pendingRunnable1[0], 300); } @Override public void afterTextChanged(Editable s) {} }); // 输入框2监听 binding.mapInput2.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (pendingRunnable2[0] != null) handler.removeCallbacks(pendingRunnable2[0]); if (s.length() == 0) { binding.mapInput2.setAdapter(null); return; } String keyword = s.toString().trim(); pendingRunnable2[0] = () -> fetchSuggestionTitles(keyword, binding.mapInput2); handler.postDelayed(pendingRunnable2[0], 300); } @Override public void afterTextChanged(Editable s) {} }); // 软键盘事件保持不变 binding.mapInput1.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { performSearch(); return true; } return false; }); binding.mapInput2.setOnEditorActionListener((v, actionId, event) -> { if ((actionId & EditorInfo.IME_MASK_ACTION) == EditorInfo.IME_ACTION_SEARCH) { performSearch(); return true; } return false; }); // 点击建议项处理 binding.mapInput1.setOnItemClickListener((parent, view, position, id) -> { String selectedText = (String) parent.getItemAtPosition(position); handleSelectedFromInput(selectedText, binding.mapInput1); }); binding.mapInput2.setOnItemClickListener((parent, view, position, id) -> { String selectedText = (String) parent.getItemAtPosition(position); handleSelectedFromInput(selectedText, binding.mapInput2); }); } /** * 获取建议并统一排序(MapFragment 版本) */ private void fetchSuggestionTitles(String keyword, android.widget.AutoCompleteTextView inputView) { if (keyword.isEmpty()) return; CityManager.ParsedQuery parsed = CityManager.parse(keyword); String searchKeyword = parsed.keyword.isEmpty() ? keyword : parsed.keyword; String explicitCity = parsed.targetCity; if (!explicitCity.isEmpty()) { PoiSearch.Query query = new PoiSearch.Query(searchKeyword, "", explicitCity); query.setPageSize(20); try { PoiSearch search = new PoiSearch(requireContext(), query); search.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { List<PoiItem> list = result.getPois(); List<String> titles = sortAndExtractTitles(list, searchKeyword, explicitCity); updateSuggestionAdapter(titles.toArray(new String[0]), inputView); } else { updateSuggestionAdapter(new String[0], inputView); } } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); search.searchPOIAsyn(); } catch (Exception e) { updateSuggestionAdapter(new String[0], inputView); } return; } List<PoiItem> nationalList = new ArrayList<>(); List<PoiItem> localList = new ArrayList<>(); PoiSearch.Query nationalQuery = new PoiSearch.Query(searchKeyword, "", ""); nationalQuery.setPageSize(20); try { PoiSearch ns = new PoiSearch(requireContext(), nationalQuery); ns.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { nationalList.addAll(result.getPois()); } requestLocalForSuggestions(searchKeyword, nationalList, localList, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); ns.searchPOIAsyn(); } catch (Exception e) { requestLocalForSuggestions(searchKeyword, nationalList, localList, inputView); } } private void requestLocalForSuggestions(String keyword, List<PoiItem> national, List<PoiItem> local, android.widget.AutoCompleteTextView inputView) { if (currentCity.isEmpty()) { showSuggestionResultSorted(national, local, keyword, inputView); return; } PoiSearch.Query localQuery = new PoiSearch.Query(keyword, "", currentCity); localQuery.setPageSize(20); try { PoiSearch ls = new PoiSearch(requireContext(), localQuery); ls.setOnPoiSearchListener(new PoiSearch.OnPoiSearchListener() { @Override public void onPoiSearched(PoiResult result, int rCode) { if (rCode == 1000 && result != null && result.getPois() != null) { local.addAll(result.getPois()); } showSuggestionResultSorted(national, local, keyword, inputView); } @Override public void onPoiItemSearched(PoiItem item, int rCode) {} }); ls.searchPOIAsyn(); } catch (Exception e) { showSuggestionResultSorted(national, local, keyword, inputView); } } private void showSuggestionResultSorted(List<PoiItem> national, List<PoiItem> local, String keyword, android.widget.AutoCompleteTextView inputView) { Set<String> seen = new HashSet<>(); List<PoiItem> combined = new ArrayList<>(); for (PoiItem item : local) if (seen.add(item.getPoiId())) combined.add(item); for (PoiItem item : national) if (seen.add(item.getPoiId())) combined.add(item); final String coreKeyword = keyword.toLowerCase().trim(); combined.sort((a, b) -> { int scoreA = calculateMatchScore(a.getTitle(), coreKeyword); int scoreB = calculateMatchScore(b.getTitle(), coreKeyword); if (scoreA != scoreB) return Integer.compare(scoreB, scoreA); boolean aIsLocal = isSameCity(getDisplayCity(a), currentCity); boolean bIsLocal = isSameCity(getDisplayCity(b), currentCity); if (aIsLocal && !bIsLocal) return -1; if (!aIsLocal && bIsLocal) return 1; return 0; }); List<String> titles = new ArrayList<>(); for (int i = 0; i < Math.min(combined.size(), 10); i++) { titles.add(combined.get(i).getTitle()); } updateSuggestionAdapter(titles.toArray(new String[0]), inputView); } private List<String> sortAndExtractTitles(List<PoiItem> list, String keyword, String city) { final String coreKeyword = keyword.toLowerCase().trim(); return list.stream() .sorted((a, b) -> { int scoreA = calculateMatchScore(a.getTitle(), coreKeyword); int scoreB = calculateMatchScore(b.getTitle(), coreKeyword); if (scoreA != scoreB) return Integer.compare(scoreB, scoreA); boolean aIsLocal = isSameCity(getDisplayCity(a), city); boolean bIsLocal = isSameCity(getDisplayCity(b), city); if (aIsLocal && !bIsLocal) return -1; if (!aIsLocal && bIsLocal) return 1; return 0; }) .map(PoiItem::getTitle) .limit(10) .collect(java.util.stream.Collectors.toList()); } private void handleSelectedFromInput(String selectedText, android.widget.AutoCompleteTextView inputView) { inputView.setText(selectedText); inputView.clearFocus(); InputMethodManager imm = (InputMethodManager) requireContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(inputView.getWindowToken(), 0); } inputView.setAdapter(null); // 判断是起点还是终点 if (inputView == binding.mapInput1) { selectionStage = 1; showStartpointSelection(selectedText); } else { selectionStage = 2; showEndpointSelection(selectedText); } } /** * 获取当前获得焦点的输入框 */ private android.widget.AutoCompleteTextView getCurrentFocusedInput() { if (binding.mapInput1.hasFocus()) return binding.mapInput1; if (binding.mapInput2.hasFocus()) return binding.mapInput2; return null; } private void handleSelectedPoiItem(PoiItem item) { android.widget.AutoCompleteTextView inputView = selectionStage == 1 ? binding.mapInput1 : binding.mapInput2; inputView.setText(item.getTitle()); inputView.clearFocus(); InputMethodManager imm = (InputMethodManager) requireContext() .getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(inputView.getWindowToken(), 0); } inputView.setAdapter(null); poiList.clear(); nationalResults.clear(); localResults.clear(); nearbyResults.clear(); updateResultList(Arrays.asList(item)); onPoiItemSelected(item); binding.resultList.scrollToPosition(0); binding.emptyView.setVisibility(View.GONE); binding.resultList.setVisibility(View.VISIBLE); } private void updateSuggestionAdapter(String[] suggestions, android.widget.AutoCompleteTextView inputView) { if (suggestions.length > 0) { ArrayAdapter<String> adapter = new ArrayAdapter<>( requireContext(), android.R.layout.simple_dropdown_item_1line, suggestions ); new Handler(Looper.getMainLooper()).post(() -> { inputView.setAdapter(adapter); if (requireActivity().getCurrentFocus() == inputView) { inputView.showDropDown(); } }); } else { new Handler(Looper.getMainLooper()).post(() -> inputView.setAdapter(null)); } } private void handleSearchError(int rCode) { String msg; switch (rCode) { case 12: msg = "API Key 错误"; break; case 27: msg = "网络连接失败"; break; case 30: msg = "SHA1 或包名错误"; break; case 33: msg = "请求频繁"; break; default: msg = "搜索失败: " + rCode; break; } Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void onGeocodeSearched(com.amap.api.services.geocoder.GeocodeResult geocodeResult, int i) {} @Override public void onResume() { super.onResume(); mapView.onResume(); if (!userHasInteracted) { enableMyLocationLayer(); } setupSearchSuggestion(); } @Override public void onPause() { super.onPause(); mapView.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); mapView.onDestroy(); geocodeSearch = null; binding = null; } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); mapView.onSaveInstanceState(outState); } } 问题是还有这个咋办?Private method 'getCurrentFocusedInput()' is never used
12-17
package com.example.kucun2.ui.jinhuo; import android.app.AlertDialog; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.EditText; import android.widget.RadioGroup; import android.widget.Spinner; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.example.kucun2.DataPreserver.Data; import com.example.kucun2.R; import com.example.kucun2.entity.*; import com.example.kucun2.function.Adapter; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; public class AddInventoryFragment extends Fragment implements Data.OnDataChangeListener { // 视图组件 private AutoCompleteTextView actvDingdan, actvChanpin, actvZujian, actvBancai; private EditText etQuantity; private RadioGroup rgType; private Button btnNewDingdan, btnAddChanpin, btnAddZujian, btnSubmit; // 适配器 private Adapter.FilterableAdapter<Dingdan> dingdanAdapter; private Adapter.FilterableAdapter<Chanpin> chanpinAdapter; private Adapter.FilterableAdapter<Zujian> zujianAdapter; private Adapter.FilterableAdapter<Bancai> bancaiAdapter; // 当前选择 private Dingdan selectedDingdan; private Chanpin selectedChanpin; private Zujian selectedZujian; private Bancai selectedBancai; // 数据列表 private List<Dingdan> dingdanList; private List<Chanpin> chanpinList; private List<Zujian> zujianList; private List<Bancai> bancaiList; // 当前用户 private User currentUser; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取当前用户 currentUser = Data.getCurrentUser(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_add_inventory, container, false); initViews(view); initData(); setupSpinners(); setupListeners(); applyPermissionRestrictions(); // 应用权限限制 return view; } /** * 初始化视图组件 */ private void initViews(View view) { actvDingdan = view.findViewById(R.id.actv_dingdan); actvChanpin = view.findViewById(R.id.actv_chanpin); actvZujian = view.findViewById(R.id.actv_zujian); actvBancai = view.findViewById(R.id.actv_bancai); etQuantity = view.findViewById(R.id.et_shuliang); rgType = view.findViewById(R.id.rg_type); btnNewDingdan = view.findViewById(R.id.btn_new_dingdan); btnAddChanpin = view.findViewById(R.id.btn_add_chanpin); btnAddZujian = view.findViewById(R.id.btn_add_zujian); btnSubmit = view.findViewById(R.id.btn_submit); // 初始禁用状态 actvChanpin.setEnabled(false); actvZujian.setEnabled(false); etQuantity.setEnabled(false); } private void initData() { // 从全局数据获取列表 dingdanList = Data.dingdans().getViewList(); chanpinList = Data.chanpins().getViewList(); zujianList = Data.zujians().getViewList(); bancaiList = Data.bancais().getViewList(); } /** * 设置下拉框适配器 */ private void setupSpinners() { // 3. 创建支持筛选的适配器 dingdanAdapter = Adapter.createDingdanFilterableAdapter(requireContext(), dingdanList); actvDingdan.setAdapter(dingdanAdapter); chanpinAdapter = Adapter.createChanpinFilterableAdapter(requireContext(), new ArrayList<>()); actvChanpin.setAdapter(chanpinAdapter); zujianAdapter = Adapter.createZujianFilterableAdapter(requireContext(), new ArrayList<>()); actvZujian.setAdapter(zujianAdapter); bancaiAdapter = Adapter.createBancaiFilterableAdapter(requireContext(), bancaiList); actvBancai.setAdapter(bancaiAdapter); } /** * 设置事件监听器 */ private void setupListeners() { // 4. 设置新的点击事件监听器 actvDingdan.setOnItemClickListener((parent, view, position, id) -> { selectedDingdan = dingdanAdapter.getItem(position); updateChanpinSpinner(); actvChanpin.setEnabled(selectedDingdan != null); if (selectedDingdan == null) { // 清空后续选择 actvChanpin.setText(""); selectedChanpin = null; actvZujian.setText(""); selectedZujian = null; actvBancai.setText(""); selectedBancai = null; etQuantity.setText(""); etQuantity.setEnabled(false); } }); // 产品选择监听 actvChanpin.setOnItemClickListener((parent, view, position, id) -> { selectedChanpin = chanpinAdapter.getItem(position); updateZujianSpinner(); actvZujian.setEnabled(selectedChanpin != null); if (selectedChanpin == null) { // 清空后续选择 actvZujian.setText(""); selectedZujian = null; actvBancai.setText(""); selectedBancai = null; etQuantity.setText(""); etQuantity.setEnabled(false); } }); // 组件选择监听 actvZujian.setOnItemClickListener((parent, view, position, id) -> { selectedZujian = zujianAdapter.getItem(position); updateBancaiSpinner(); // 组件选择后锁定板材下拉框 actvBancai.setEnabled(false); }); // 板材选择监听 actvBancai.setOnItemClickListener((parent, view, position, id) -> { selectedBancai = bancaiAdapter.getItem(position); etQuantity.setEnabled(selectedBancai != null); if (selectedBancai == null) { etQuantity.setText(""); } }); // 新建订单 btnNewDingdan.setOnClickListener(v -> showNewDingdanDialog()); // 添加产品 btnAddChanpin.setOnClickListener(v -> showAddChanpinDialog()); // 添加组件 btnAddZujian.setOnClickListener(v -> showAddZujianDialog()); // 提交 btnSubmit.setOnClickListener(v -> submitInventory()); } /** * 根据用户角色应用权限限制 */ private void applyPermissionRestrictions() { if (currentUser == null) return; int role = currentUser.getRole(); if (role == 0) { // 普通用户 // 只能消耗,不能进货 rgType.check(R.id.rb_xiaohao); rgType.getChildAt(0).setEnabled(false); // 禁用进货选项 // 禁用新建订单、添加产品按钮 btnNewDingdan.setEnabled(false); btnAddChanpin.setEnabled(false); } } /** * 根据选定订单更新产品下拉框 */ private void updateChanpinSpinner() { List<Chanpin> filtered = new ArrayList<>(); if (selectedDingdan != null) { for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { filtered.add(dc.getChanpin()); } } // 5. 使用适配器的updateList方法更新数据 chanpinAdapter.updateList(filtered); } /** * 根据选定产品更新组件下拉框 */ private void updateZujianSpinner() { List<Zujian> filtered = new ArrayList<>(); if (selectedChanpin != null) { for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { filtered.add(cz.getZujian()); } } zujianAdapter.updateList(filtered); } /** * 根据选定组件更新板材下拉框 */ private void updateBancaiSpinner() { List<Bancai> filtered = new ArrayList<>(); if (selectedZujian != null && selectedChanpin != null) { // 查找组件关联的板材 for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { if (cz.getZujian().equals(selectedZujian)) { filtered.add(cz.getBancai()); // 自动选中关联的板材 selectedBancai = cz.getBancai(); actvBancai.setText(selectedBancai.TableText()); etQuantity.setEnabled(true); break; } } bancaiAdapter.updateList(filtered); } else { // 没有选择组件时显示所有板材 filtered = new ArrayList<>(bancaiList); bancaiAdapter.updateList(filtered); } } /** * 显示新建订单对话框 */ private void showNewDingdanDialog() { // 权限检查 if (currentUser != null && currentUser.getRole() == 0) { Toast.makeText(requireContext(), "您无权创建新订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_dingdan, null); EditText etNumber = view.findViewById(R.id.et_order_number); builder.setView(view) .setTitle("新建订单") .setPositiveButton("保存", (dialog, which) -> { Dingdan newDingdan = new Dingdan(); newDingdan.setNumber(etNumber.getText().toString()); // 添加到全局数据 Data.add(newDingdan); }) .setNegativeButton("取消", null) .show(); } /** * 显示添加产品对话框 */ private void showAddChanpinDialog() { // 权限检查 if (currentUser != null && currentUser.getRole() == 0) { Toast.makeText(requireContext(), "您无权添加产品", Toast.LENGTH_SHORT).show(); return; } if (selectedDingdan == null) { Toast.makeText(requireContext(), "请先选择订单", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_add_chanpin, null); Spinner spinner = view.findViewById(R.id.spinner_chanpin_selection); EditText etQuantity = view.findViewById(R.id.et_chanpin_quantity); // 设置产品列表(排除已关联的) List<Chanpin> available = new ArrayList<>(chanpinList); for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { available.remove(dc.getChanpin()); } Adapter.setupChanpinSpinner(spinner, available, requireContext()); // 添加新建产品按钮 Button btnNewChanpin = view.findViewById(R.id.btn_new_chanpin); builder.setView(view) .setTitle("添加产品到订单") .setPositiveButton("添加", (dialog, which) -> { Chanpin selected = (Chanpin) spinner.getSelectedItem(); int quantity = Integer.parseInt(etQuantity.getText().toString().trim()); // 检查是否已存在关联 for (Dingdan_chanpin dc : selectedDingdan.getDingdan_chanpin()) { if (dc.getChanpin().equals(selected)) { Toast.makeText(requireContext(), "该产品已添加到订单", Toast.LENGTH_SHORT).show(); return; } } // 创建订单-产品关联 Dingdan_chanpin dc = new Dingdan_chanpin(); dc.setDingdan(selectedDingdan); dc.setChanpin(selected); dc.setShuliang(quantity); // 添加到全局数据 Data.add(dc); selectedDingdan.getDingdan_chanpin().add(dc); }) .show(); // 新建产品按钮点击事件 btnNewChanpin.setOnClickListener(v -> showNewChanpinDialog(available, spinner)); } // 实现新建产品对话框 private void showNewChanpinDialog(List<Chanpin> available, Spinner spinner) { AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_new_chanpin, null); EditText etBianhao = view.findViewById(R.id.et_chanpin_name); builder.setView(view) .setTitle("新建产品") .setPositiveButton("保存", (dialog, which) -> { String bianhao = etBianhao.getText().toString().trim(); if (bianhao.isEmpty()) { Toast.makeText(requireContext(), "产品编号不能为空", Toast.LENGTH_SHORT).show(); return; } // 创建新产品 Chanpin newChanpin = new Chanpin(); newChanpin.setBianhao(bianhao); // 添加到全局数据 Data.add(newChanpin); // 更新可用列表和适配器 available.add(newChanpin); }) .setNegativeButton("取消", null) .show(); } /** * 显示添加组件对话框 */ private void showAddZujianDialog() { if (selectedChanpin == null) { Toast.makeText(requireContext(), "请先选择产品", Toast.LENGTH_SHORT).show(); return; } AlertDialog.Builder builder = new AlertDialog.Builder(requireContext()); View view = LayoutInflater.from(requireContext()).inflate(R.layout.dialog_create_zujian_bancai, null); Spinner spinnerZujian = view.findViewById(R.id.et_zujian_name); Spinner spinnerBancai = view.findViewById(R.id.spinner_bancai); EditText etOneHowmany = view.findViewById(R.id.number_one_howmany); // 设置组件下拉框 Adapter.setupZujianSpinner(spinnerZujian, zujianList, requireContext()); // 设置板材下拉框 Adapter.setupBancaiSpinners(spinnerBancai, bancaiList, requireContext()); builder.setView(view) .setTitle("添加组件到产品") .setPositiveButton("添加", (dialog, which) -> { Zujian zujian = (Zujian) spinnerZujian.getSelectedItem(); Bancai bancai = (Bancai) spinnerBancai.getSelectedItem(); double oneHowmany = Double.parseDouble(etOneHowmany.getText().toString()); // 检查是否已存在关联 for (Chanpin_Zujian cz : selectedChanpin.getChanpin_zujian()) { if (cz.getZujian().equals(zujian) && cz.getBancai().equals(bancai)) { Toast.makeText(requireContext(), "该组件已添加到产品", Toast.LENGTH_SHORT).show(); return; } } // 创建产品-组件关联 Chanpin_Zujian cz = new Chanpin_Zujian(); cz.setChanpin(selectedChanpin); cz.setZujian(zujian); cz.setBancai(bancai); cz.setOne_howmany(oneHowmany); zujian.getChanpin_zujian().add(cz); selectedChanpin.getChanpin_zujian().add(cz); // 添加到全局数据 Data.add(cz); }) .show(); } /** * 提交库存操作(进货/消耗) */ private void submitInventory() { // 获取数量 int quantity; try { quantity = Integer.parseInt(etQuantity.getText().toString()); if (quantity <= 0) { Toast.makeText(requireContext(), "数量必须大于0", Toast.LENGTH_SHORT).show(); return; } } catch (NumberFormatException e) { Toast.makeText(requireContext(), "请输入有效数量", Toast.LENGTH_SHORT).show(); return; } // 获取操作类型 boolean isJinhuo = rgType.getCheckedRadioButtonId() == R.id.rb_jinhuo; // 权限检查:普通用户只能消耗 if (currentUser != null && currentUser.getRole() == 0 && isJinhuo) { Toast.makeText(requireContext(), "您只能执行生产操作", Toast.LENGTH_SHORT).show(); return; } // 处理进货/消耗逻辑 if (isJinhuo) { handleJinhuo(quantity); } else { handleXiaohao(quantity); } resetForm(); } private void handleJinhuo(int quantity) { // 1. 查找或创建dingdan_bancai记录 Dingdan_bancai db = findOrCreateDingdanBancai(); // 2. 更新dingdan_bancai数量 db.setShuliang(db.getShuliang() + quantity); // 3. 创建进货记录 Jinhuo record = new Jinhuo(); record.setShuliang(quantity); record.setDate(new Date()); record.setUser(currentUser); record.setDingdan_bancai(db); Data.add(record); // 4. 更新库存 updateKucun(db.getBancai(), quantity); Toast.makeText(requireContext(), "进货操作成功", Toast.LENGTH_SHORT).show(); } private Dingdan_bancai findOrCreateDingdanBancai() { // 尝试查找完全匹配的记录 Dingdan_bancai db = findExistingDingdanBancai(true); if (db == null) { // 创建新记录 db = new Dingdan_bancai(); db.setDingdan(selectedDingdan); db.setChanpin(selectedChanpin); db.setZujian(selectedZujian); db.setBancai(selectedBancai); db.setShuliang(0); // 初始数量为0 Data.add(db); } return db; } private void handleXiaohao(int quantity) { // 1. 按优先级查找dingdan_bancai记录 Dingdan_bancai db = findMatchingDingdanBancai(); int remaining = quantity; if (db != null) { // 2. 计算实际可消耗数量 int available = Math.max(0, db.getShuliang()); int consumed = Math.min(available, remaining); // 3. 更新dingdan_bancai数量 db.setShuliang(db.getShuliang() - consumed); remaining -= consumed; } // 4. 如果还有剩余数量,创建负记录 if (remaining > 0) { Dingdan_bancai negativeDb = new Dingdan_bancai(); negativeDb.setDingdan(selectedDingdan); negativeDb.setChanpin(selectedChanpin); negativeDb.setZujian(selectedZujian); negativeDb.setBancai(selectedBancai); negativeDb.setShuliang(-remaining); // 负数量 Data.add(negativeDb); db = negativeDb; // 用于库存更新 } // 5. 创建消耗记录 Jinhuo record = new Jinhuo(); record.setShuliang(-quantity); record.setDate(new Date()); record.setUser(currentUser); record.setDingdan_bancai(db); Data.add(record); // 6. 更新库存 updateKucun(selectedBancai, -quantity); Toast.makeText(requireContext(), "消耗操作成功", Toast.LENGTH_SHORT).show(); } private Dingdan_bancai findMatchingDingdanBancai() { List<Dingdan_bancai> allDB = Data.dingdan_bancais().getViewList(); // 匹配规则1: 完全匹配 (订单+产品+组件+板材) for (Dingdan_bancai db : allDB) { if (matchesExactly(db) && db.getShuliang() > 0) { return db; } } // 匹配规则2: 忽略组件 (订单+产品+板材) for (Dingdan_bancai db : allDB) { if (matchesWithoutComponent(db) && db.getShuliang() > 0) { return db; } } // 匹配规则3: 忽略产品和组件 (订单+板材) for (Dingdan_bancai db : allDB) { if (matchesOnlyOrderAndMaterial(db) && db.getShuliang() > 0) { return db; } } return null; // 无匹配 } private void updateKucun(Bancai bancai, int delta) { // 查找该板材的库存记录 Kucun kucun = findKucunByBancai(bancai); if (kucun == null) { // 创建新库存记录 kucun = new Kucun(); kucun.setBancai(bancai); kucun.setShuliang(0); Data.add(kucun); } // 更新库存数量 kucun.setShuliang(kucun.getShuliang() + delta); } private Kucun findKucunByBancai(Bancai bancai) { for (Kucun k : Data.kucuns().getViewList()) { if (k.getBancai().equals(bancai)) { return k; } } return null; } // 完全匹配 private boolean matchesExactly(Dingdan_bancai db) { return Objects.equals(db.getDingdan(), selectedDingdan) && Objects.equals(db.getChanpin(), selectedChanpin) && Objects.equals(db.getZujian(), selectedZujian) && db.getBancai().equals(selectedBancai); } // 忽略组件匹配 private boolean matchesWithoutComponent(Dingdan_bancai db) { return Objects.equals(db.getDingdan(), selectedDingdan) && Objects.equals(db.getChanpin(), selectedChanpin) && db.getZujian() == null && // 组件必须为空 db.getBancai().equals(selectedBancai); } // 仅订单和板材匹配 private boolean matchesOnlyOrderAndMaterial(Dingdan_bancai db) { return Objects.equals(db.getDingdan(), selectedDingdan) && db.getChanpin() == null && // 产品必须为空 db.getZujian() == null && // 组件必须为空 db.getBancai().equals(selectedBancai); } /** * 查找现有订单-板材关联记录 */ private Dingdan_bancai findExistingDingdanBancai(boolean b) { for (Dingdan_bancai db : Data.dingdan_bancais().getViewList()) { boolean matchDingdan = (selectedDingdan == null && db.getDingdan() == null) || (selectedDingdan != null && selectedDingdan.equals(db.getDingdan())); boolean matchChanpin = (selectedChanpin == null && db.getChanpin() == null) || (selectedChanpin != null && selectedChanpin.equals(db.getChanpin())); boolean matchZujian = (selectedZujian == null && db.getZujian() == null) || (selectedZujian != null && selectedZujian.equals(db.getZujian())); boolean matchBancai = selectedBancai != null && selectedBancai.equals(db.getBancai()); if (matchDingdan && matchChanpin && matchZujian && matchBancai) { return db; } } return null; } /** * 重置表单到初始状态 */ private void resetForm() { actvDingdan.setSelection(0); actvChanpin.setSelection(0); actvZujian.setSelection(0); actvBancai.setSelection(0); etQuantity.setText(""); rgType.check(R.id.rb_jinhuo); } @Override public void onResume() { super.onResume(); Data.addDataChangeListener(this); } @Override public void onPause() { super.onPause(); Data.removeDataChangeListener(this); } @Override public void onDataChanged(Class<?> entityClass, String operationType, Integer itemId) { // 6. 更新适配器数据 if (entityClass == Dingdan.class) { dingdanList = Data.dingdans().getViewList(); dingdanAdapter.updateList(dingdanList); // 尝试选中新添加的订单 if (operationType.equals("add")) { for (int i = 0; i < dingdanList.size(); i++) { if (Objects.equals(dingdanList.get(i).getId(), itemId)) { actvDingdan.setText(dingdanList.get(i).getNumber(), false); selectedDingdan = dingdanList.get(i); break; } } } } else if (entityClass == Chanpin.class) { chanpinList = Data.chanpins().getViewList(); updateChanpinSpinner(); } else if (entityClass == Zujian.class) { zujianList = Data.zujians().getViewList(); updateZujianSpinner(); } else if (entityClass == Bancai.class) { bancaiList = Data.bancais().getViewList(); bancaiAdapter.updateList(bancaiList); } } } 给出完整版的AddInventoryFragment类,参考原始代码和InventoryServicepackage com.example.kucun2.Servicer; import android.util.Log; import com.example.kucun2.DataPreserver.Data; import com.example.kucun2.entity.*; import java.util.*; public class InventoryService { // 日志级别常量 public static final int LOG_LEVEL_DEBUG = 0; public static final int LOG_LEVEL_INFO = 1; public static final int LOG_LEVEL_WARN = 2; public static final int LOG_LEVEL_ERROR = 3; private static final String TAG = "InventoryService"; private int logLevel = LOG_LEVEL_INFO; // 默认日志级别 // 设置日志级别 public void setLogLevel(int level) { this.logLevel = level; } // 日志记录方法 private void log(int level, String message) { if (level < logLevel) return; switch (level) { case LOG_LEVEL_DEBUG: Log.d(TAG, message); break; case LOG_LEVEL_INFO: Log.i(TAG, message); break; case LOG_LEVEL_WARN: Log.w(TAG, message); break; case LOG_LEVEL_ERROR: Log.e(TAG, message); break; } } //=== 库存操作 ===// /** * 处理进货操作 */ public OperationResult<Void> handleJinhuo(Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, int quantity, User user) { long startTime = System.currentTimeMillis(); try { log(LOG_LEVEL_DEBUG, "开始处理进货操作: 订单=" + dingdan.getNumber() + ", 产品=" + chanpin.getBianhao() + ", 组件=" + zujian.getName() + ", 板材=" + bancai.TableText() + ", 数量=" + quantity); // 查找或创建订单-板材关联 Dingdan_bancai db = findOrCreateDingdanBancai(dingdan, chanpin, zujian, bancai); // 更新数量 db.setShuliang(db.getShuliang() + quantity); // 创建进货记录 Jinhuo jinhuo = new Jinhuo(); jinhuo.setShuliang(quantity); jinhuo.setDate(new Date()); jinhuo.setUser(user); jinhuo.setDingdan_bancai(db); Data.add(jinhuo); // 更新库存 updateKucun(bancai, quantity); log(LOG_LEVEL_INFO, "进货操作成功: 增加了 " + quantity + " 件板材"); return OperationResult.success("进货操作成功", null); } catch (Exception e) { log(LOG_LEVEL_ERROR, "进货操作失败: " + e.getMessage()); return OperationResult.error("进货操作失败: " + e.getMessage()); } finally { long duration = System.currentTimeMillis() - startTime; log(LOG_LEVEL_DEBUG, "进货操作耗时: " + duration + "ms"); } } /** * 处理消耗操作 */ public OperationResult<Void> handleXiaohao(Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai, int quantity, User user) { long startTime = System.currentTimeMillis(); try { log(LOG_LEVEL_DEBUG, "开始处理消耗操作: 订单=" + dingdan.getNumber() + ", 产品=" + chanpin.getBianhao() + ", 组件=" + zujian.getName() + ", 板材=" + bancai.TableText() + ", 数量=" + quantity); // 查找订单-板材关联 Dingdan_bancai db = findDingdanBancai(dingdan, chanpin, zujian, bancai); if (db == null) { String msg = "未找到相关的订单-板材关联"; log(LOG_LEVEL_WARN, msg); return OperationResult.error(msg); } // 检查库存是否足够 if (db.getShuliang() < quantity) { String msg = "库存不足: 当前库存 " + db.getShuliang() + ",请求消耗 " + quantity; log(LOG_LEVEL_WARN, msg); return OperationResult.error(msg); } // 更新数量 db.setShuliang(db.getShuliang() - quantity); // 创建消耗记录 Jinhuo xiaohao = new Jinhuo(); xiaohao.setShuliang(quantity); xiaohao.setDate(new Date()); xiaohao.setUser(user); xiaohao.setDingdan_bancai(db); Data.add(xiaohao); // 更新库存 updateKucun(bancai, -quantity); log(LOG_LEVEL_INFO, "消耗操作成功: 减少了 " + quantity + " 件板材"); return OperationResult.success("消耗操作成功", null); } catch (Exception e) { log(LOG_LEVEL_ERROR, "消耗操作失败: " + e.getMessage()); return OperationResult.error("消耗操作失败: " + e.getMessage()); } finally { long duration = System.currentTimeMillis() - startTime; log(LOG_LEVEL_DEBUG, "消耗操作耗时: " + duration + "ms"); } } //=== 新建数据操作 ===// /** * 创建新订单 */ public OperationResult<Dingdan> createNewOrder(String orderNumber) { try { if (orderNumber == null || orderNumber.trim().isEmpty()) { log(LOG_LEVEL_WARN, "创建订单失败: 订单号不能为空"); return OperationResult.error("订单号不能为空"); } // 检查订单号是否已存在 for (Dingdan order : Data.dingdans().getViewList()) { if (orderNumber.equals(order.getNumber())) { log(LOG_LEVEL_WARN, "创建订单失败: 订单号已存在 - " + orderNumber); return OperationResult.error("订单号已存在"); } } Dingdan newOrder = new Dingdan(); newOrder.setNumber(orderNumber); Data.add(newOrder); log(LOG_LEVEL_INFO, "订单创建成功: " + orderNumber); return OperationResult.success(newOrder); } catch (Exception e) { log(LOG_LEVEL_ERROR, "创建订单失败: " + e.getMessage()); return OperationResult.error("创建订单失败: " + e.getMessage()); } } /** * 创建新产品 */ public OperationResult<Chanpin> createNewProduct(String productCode) { try { if (productCode == null || productCode.trim().isEmpty()) { log(LOG_LEVEL_WARN, "创建产品失败: 产品编号不能为空"); return OperationResult.error("产品编号不能为空"); } // 检查产品编号是否已存在 for (Chanpin product : Data.chanpins().getViewList()) { if (productCode.equals(product.getBianhao())) { log(LOG_LEVEL_WARN, "创建产品失败: 产品编号已存在 - " + productCode); return OperationResult.error("产品编号已存在"); } } Chanpin newProduct = new Chanpin(); newProduct.setBianhao(productCode); Data.add(newProduct); log(LOG_LEVEL_INFO, "产品创建成功: " + productCode); return OperationResult.success(newProduct); } catch (Exception e) { log(LOG_LEVEL_ERROR, "创建产品失败: " + e.getMessage()); return OperationResult.error("创建产品失败: " + e.getMessage()); } } /** * 添加产品到订单 */ public OperationResult<Dingdan_chanpin> addProductToOrder(Dingdan order, Chanpin product, int quantity) { try { if (order == null) { log(LOG_LEVEL_WARN, "添加产品失败: 订单不能为空"); return OperationResult.error("订单不能为空"); } if (product == null) { log(LOG_LEVEL_WARN, "添加产品失败: 产品不能为空"); return OperationResult.error("产品不能为空"); } if (quantity <= 0) { log(LOG_LEVEL_WARN, "添加产品失败: 数量必须大于0"); return OperationResult.error("数量必须大于0"); } // 检查是否已存在关联 for (Dingdan_chanpin dc : order.getDingdan_chanpin()) { if (dc.getChanpin().equals(product)) { String msg = "产品 " + product.getBianhao() + " 已添加到订单"; log(LOG_LEVEL_WARN, msg); return OperationResult.error(msg); } } // 创建订单-产品关联 Dingdan_chanpin dc = new Dingdan_chanpin(); dc.setDingdan(order); dc.setChanpin(product); dc.setShuliang(quantity); Data.add(dc); order.getDingdan_chanpin().add(dc); log(LOG_LEVEL_INFO, "产品添加成功: " + product.getBianhao() + " 数量: " + quantity); return OperationResult.success(dc); } catch (Exception e) { log(LOG_LEVEL_ERROR, "添加产品失败: " + e.getMessage()); return OperationResult.error("添加产品失败: " + e.getMessage()); } } /** * 添加组件到产品 */ public OperationResult<Chanpin_Zujian> addComponentToProduct(Chanpin product, Zujian component, Bancai material, double componentsPerBoard) { try { if (product == null) { log(LOG_LEVEL_WARN, "添加组件失败: 产品不能为空"); return OperationResult.error("产品不能为空"); } if (component == null) { log(LOG_LEVEL_WARN, "添加组件失败: 组件不能为空"); return OperationResult.error("组件不能为空"); } if (material == null) { log(LOG_LEVEL_WARN, "添加组件失败: 板材不能为空"); return OperationResult.error("板材不能为空"); } if (componentsPerBoard <= 0) { log(LOG_LEVEL_WARN, "添加组件失败: 每张板材可生产数量必须大于0"); return OperationResult.error("每张板材可生产数量必须大于0"); } // 检查是否已存在关联 for (Chanpin_Zujian cz : product.getChanpin_zujian()) { if (cz.getZujian().equals(component) && cz.getBancai().equals(material)) { String msg = "组件 " + component.getName() + " 已添加到产品"; log(LOG_LEVEL_WARN, msg); return OperationResult.error(msg); } } // 创建产品-组件关联 Chanpin_Zujian cz = new Chanpin_Zujian(); cz.setChanpin(product); cz.setZujian(component); cz.setBancai(material); cz.setOne_howmany(componentsPerBoard); Data.add(cz); product.getChanpin_zujian().add(cz); log(LOG_LEVEL_INFO, "组件添加成功: " + component.getName() + " 使用板材: " + material.TableText() + " 每张生产: " + componentsPerBoard); return OperationResult.success(cz); } catch (Exception e) { log(LOG_LEVEL_ERROR, "添加组件失败: " + e.getMessage()); return OperationResult.error("添加组件失败: " + e.getMessage()); } } //=== 数据处理 ===// /** * 获取订单关联的产品列表 */ public List<Chanpin> getProductsForOrder(Dingdan order) { List<Chanpin> products = new ArrayList<>(); if (order != null) { for (Dingdan_chanpin dc : order.getDingdan_chanpin()) { products.add(dc.getChanpin()); } } log(LOG_LEVEL_DEBUG, "获取订单 " + (order != null ? order.getNumber() : "null") + " 关联的产品: " + products.size() + " 个"); return products; } /** * 获取产品关联的组件列表 */ public List<Zujian> getComponentsForProduct(Chanpin product) { List<Zujian> components = new ArrayList<>(); if (product != null) { for (Chanpin_Zujian cz : product.getChanpin_zujian()) { components.add(cz.getZujian()); } } log(LOG_LEVEL_DEBUG, "获取产品 " + (product != null ? product.getBianhao() : "null") + " 关联的组件: " + components.size() + " 个"); return components; } /** * 获取组件关联的板材列表 */ public List<Bancai> getMaterialsForComponent(Chanpin product, Zujian component) { List<Bancai> materials = new ArrayList<>(); if (product != null && component != null) { for (Chanpin_Zujian cz : product.getChanpin_zujian()) { if (cz.getZujian().equals(component)) { materials.add(cz.getBancai()); } } } log(LOG_LEVEL_DEBUG, "获取组件 " + (component != null ? component.getName() : "null") + " 关联的板材: " + materials.size() + " 种"); return materials; } //=== 私有辅助方法 ===// /** * 查找或创建订单-板材关联 */ private Dingdan_bancai findOrCreateDingdanBancai(Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai) { // 尝试查找现有关联 for (Dingdan_bancai db : Data.dingdan_bancais().getViewList()) { if (Objects.equals(db.getDingdan(), dingdan) && Objects.equals(db.getChanpin(), chanpin) && Objects.equals(db.getZujian(), zujian) && Objects.equals(db.getBancai(), bancai)) { log(LOG_LEVEL_DEBUG, "找到现有订单-板材关联"); return db; } } // 创建新的关联 log(LOG_LEVEL_DEBUG, "创建新订单-板材关联"); Dingdan_bancai newDb = new Dingdan_bancai(); newDb.setDingdan(dingdan); newDb.setChanpin(chanpin); newDb.setZujian(zujian); newDb.setBancai(bancai); newDb.setShuliang(0); Data.add(newDb); return newDb; } /** * 查找订单-板材关联 */ private Dingdan_bancai findDingdanBancai(Dingdan dingdan, Chanpin chanpin, Zujian zujian, Bancai bancai) { for (Dingdan_bancai db : Data.dingdan_bancais().getViewList()) { if (Objects.equals(db.getDingdan(), dingdan) && Objects.equals(db.getChanpin(), chanpin) && Objects.equals(db.getZujian(), zujian) && Objects.equals(db.getBancai(), bancai)) { return db; } } return null; } /** * 更新库存 */ private void updateKucun(Bancai bancai, int quantityDelta) { Kucun kucun = findOrCreateKucun(bancai); kucun.setShuliang(kucun.getShuliang() + quantityDelta); log(LOG_LEVEL_DEBUG, "更新库存: " + bancai.TableText() + " 数量变化: " + quantityDelta + " 新库存: " + kucun.getShuliang()); } /** * 查找或创建库存记录 */ private Kucun findOrCreateKucun(Bancai bancai) { for (Kucun k : Data.kucuns().getViewList()) { if (k.getBancai().equals(bancai)) { return k; } } // 创建新库存记录 Kucun newKucun = new Kucun(); newKucun.setBancai(bancai); newKucun.setShuliang(0); Data.add(newKucun); return newKucun; } } 类
07-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值