From 23fc0da5c8d4ad6cf25cea6496d4b6971454b9f5 Mon Sep 17 00:00:00 2001 From: userGyl Date: Mon, 13 Apr 2026 15:05:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ln_jq_app/android/.claude/settings.local.json | 7 + ln_jq_app/android/app/build.gradle.kts | 1 + .../android/app/src/main/AndroidManifest.xml | 7 + .../java/com/lnkj/ln_jq_app/MainActivity.java | 37 +- .../com/lnkj/ln_jq_app/NativeMapFactory.java | 10 + .../com/lnkj/ln_jq_app/NativeMapView.java | 351 ++++++------------ .../ln_jq_app/SearchDestinationActivity.java | 261 +++++++++++++ .../app/src/main/res/drawable/back.png | Bin 0 -> 705 bytes .../src/main/res/drawable/bg_search_input.xml | 9 + .../src/main/res/drawable/rounded_top_bg.xml | 9 + .../layout/activity_search_destination.xml | 52 +++ 11 files changed, 494 insertions(+), 250 deletions(-) create mode 100644 ln_jq_app/android/.claude/settings.local.json create mode 100644 ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/SearchDestinationActivity.java create mode 100644 ln_jq_app/android/app/src/main/res/drawable/back.png create mode 100644 ln_jq_app/android/app/src/main/res/drawable/bg_search_input.xml create mode 100644 ln_jq_app/android/app/src/main/res/drawable/rounded_top_bg.xml create mode 100644 ln_jq_app/android/app/src/main/res/layout/activity_search_destination.xml diff --git a/ln_jq_app/android/.claude/settings.local.json b/ln_jq_app/android/.claude/settings.local.json new file mode 100644 index 0000000..2be3421 --- /dev/null +++ b/ln_jq_app/android/.claude/settings.local.json @@ -0,0 +1,7 @@ +{ + "permissions": { + "allow": [ + "Bash(./gradlew assembleDebug --stacktrace)" + ] + } +} diff --git a/ln_jq_app/android/app/build.gradle.kts b/ln_jq_app/android/app/build.gradle.kts index 17dd78b..0f2ccc8 100644 --- a/ln_jq_app/android/app/build.gradle.kts +++ b/ln_jq_app/android/app/build.gradle.kts @@ -72,4 +72,5 @@ flutter { dependencies { implementation("com.amap.api:navi-3dmap-location-search:10.0.700_3dmap10.0.700_loc6.4.5_sea9.7.2") implementation("com.squareup.okhttp3:okhttp:4.12.0") + implementation("androidx.appcompat:appcompat:1.7.1") } diff --git a/ln_jq_app/android/app/src/main/AndroidManifest.xml b/ln_jq_app/android/app/src/main/AndroidManifest.xml index e2a1e53..b9dada1 100644 --- a/ln_jq_app/android/app/src/main/AndroidManifest.xml +++ b/ln_jq_app/android/app/src/main/AndroidManifest.xml @@ -50,6 +50,13 @@ android:theme="@android:style/Theme.NoTitleBar" android:configChanges="orientation|keyboardHidden|screenSize|navigation" android:screenOrientation="portrait" /> + + = Build.VERSION_CODES.M) { String[] requiredPermissions = getRequiredPermissions(); List deniedPermissions = new ArrayList<>(); - + for (String permission : requiredPermissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { deniedPermissions.add(permission); @@ -73,9 +77,9 @@ public class MainActivity extends FlutterActivity { if (!deniedPermissions.isEmpty()) { ActivityCompat.requestPermissions( - this, - deniedPermissions.toArray(new String[0]), - PERMISSION_REQUEST_CODE + this, + deniedPermissions.toArray(new String[0]), + PERMISSION_REQUEST_CODE ); } else { Log.d(TAG, "所有必要权限已授予"); @@ -108,6 +112,27 @@ public class MainActivity extends FlutterActivity { } } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + // 处理从搜索目的地页面返回的结果 + if (requestCode == 1000 && resultCode == RESULT_OK && data != null) { + String name = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_NAME); + String address = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_ADDRESS); + double lat = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LAT, 0); + double lon = data.getDoubleExtra(SearchDestinationActivity.EXTRA_RESULT_LON, 0); + String district = data.getStringExtra(SearchDestinationActivity.EXTRA_RESULT_DISTRICT); + + if (name != null && lat != 0 && lon != 0) { + // 获取地图实例并设置目的地 + NativeMapView mapView = NativeMapFactory.getMapView(); + if (mapView != null) { + mapView.setDestination(name, address, lat, lon, district); + } + } + } + } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { @@ -116,7 +141,7 @@ public class MainActivity extends FlutterActivity { if (requestCode == PERMISSION_REQUEST_CODE) { boolean locationGranted = false; for (int i = 0; i < permissions.length; i++) { - if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i]) + if (Manifest.permission.ACCESS_FINE_LOCATION.equals(permissions[i]) && grantResults[i] == PackageManager.PERMISSION_GRANTED) { locationGranted = true; break; diff --git a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java index 9e8ff31..c2b4577 100644 --- a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java +++ b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapFactory.java @@ -1,5 +1,6 @@ package com.lnkj.ln_jq_app; +import android.app.Activity; import android.content.Context; import io.flutter.plugin.common.MessageCodec; @@ -34,4 +35,13 @@ public class NativeMapFactory extends PlatformViewFactory { public static NativeMapView getMapView() { return mapViewInstance; } + + /** + * 设置 Activity 实例 + */ + public static void setActivity(Activity activity) { + if (mapViewInstance != null) { + mapViewInstance.setActivity(activity); + } + } } diff --git a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java index 07a23de..ae3d455 100644 --- a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java +++ b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/NativeMapView.java @@ -8,34 +8,23 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; -import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.GradientDrawable; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.text.Editable; -import android.text.InputType; import android.text.TextUtils; -import android.text.TextWatcher; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; -import android.view.MotionEvent; 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.Button; -import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -71,9 +60,6 @@ import com.amap.api.services.geocoder.GeocodeSearch; import com.amap.api.services.geocoder.RegeocodeAddress; import com.amap.api.services.geocoder.RegeocodeQuery; import com.amap.api.services.geocoder.RegeocodeResult; -import com.amap.api.services.help.Inputtips; -import com.amap.api.services.help.InputtipsQuery; -import com.amap.api.services.help.Tip; import com.amap.api.services.route.BusRouteResult; import com.amap.api.services.route.DrivePath; import com.amap.api.services.route.DriveRouteResult; @@ -91,7 +77,6 @@ import java.util.List; import java.util.Map; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import io.flutter.plugin.platform.PlatformView; import okhttp3.Call; import okhttp3.Callback; @@ -104,7 +89,7 @@ import okhttp3.Response; /** * 高德地图导航 */ -public class NativeMapView implements PlatformView, LocationSource, AMapLocationListener, GeocodeSearch.OnGeocodeSearchListener, RouteSearch.OnRouteSearchListener, AMap.OnMarkerClickListener, Inputtips.InputtipsListener { +public class NativeMapView implements PlatformView, LocationSource, AMapLocationListener, GeocodeSearch.OnGeocodeSearchListener, RouteSearch.OnRouteSearchListener, AMap.OnMarkerClickListener { private static final String TAG = "NativeMapView"; private final FrameLayout container; @@ -120,15 +105,12 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation private final OkHttpClient httpClient = new OkHttpClient(); // UI组件 - private EditText endInput; + private TextView endInput; private LinearLayout searchArea; // 规划路线面板 private LinearLayout detailPanel; // 详情面板 private View modeMenu; //模式选择 private TextView tvStationName, tvStationAddr, planToggleBtn, tvBusinessHours; - private ListView suggestionList; // 输入提示列表 - private ArrayAdapter suggestionAdapter; // 提示列表适配器 - private List currentTipList; // 当前提示列表 //时间 费用 里程 路费 private TextView tvDuration, tvDistance, tvTolls, tvTollsFuel, tvPerson, tvPrice, tvPhone; @@ -140,7 +122,6 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation private LatLng endPoint; private boolean isFirstLocation = true; private boolean isUserSelectedDestination = false; // 标识用户是否手动选择了目的地 - private boolean isProgrammaticTextChange = false; // 标识是否是程序自动设置文本 private final List stationMarkers = new ArrayList<>(); // 存储token和车牌号 @@ -161,8 +142,11 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation this.mContext = context; mActivity = getActivityFromContext(context); - MapsInitializer.updatePrivacyShow(mActivity, true, true); - MapsInitializer.updatePrivacyAgree(mActivity, true); + // 确保 mActivity 不为 null,如果为 null 则使用 context + Activity activity = mActivity != null ? mActivity : (context instanceof Activity ? (Activity) context : null); + + MapsInitializer.updatePrivacyShow(activity, true, true); + MapsInitializer.updatePrivacyAgree(activity, true); mapView = new MapView(context); mapView.onCreate(null); @@ -204,7 +188,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation bottomContainer = new LinearLayout(context); bottomContainer.setOrientation(LinearLayout.VERTICAL); bottomContainer.setGravity(Gravity.BOTTOM); - + bottomContainer.setBackgroundResource(R.drawable.rounded_top_bg); // 1. 详情面板 (用于显示加氢站详细信息,初始隐藏) detailPanel = createDetailPanel(context); detailPanel.setVisibility(View.GONE); @@ -219,122 +203,48 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation searchArea.setFocusable(true); searchArea.setFocusableInTouchMode(true); - endInput = new EditText(context); + endInput = new TextView(context); endInput.setHint("请输入目的地,不输入则自动匹配推荐加氢站"); endInput.setTextSize(13); + endInput.setTextColor(Color.parseColor("#333333")); endInput.setHintTextColor(Color.GRAY); endInput.setPadding(dp2px(12), dp2px(12), dp2px(12), dp2px(12)); endInput.setBackground(getRoundedDrawable(Color.parseColor("#F8F8F8"), 8)); endInput.setSingleLine(true); - endInput.setInputType(InputType.TYPE_CLASS_TEXT); // 明确输入类型 - endInput.setImeOptions(EditorInfo.IME_ACTION_SEARCH); - endInput.setFocusable(true); - endInput.setFocusableInTouchMode(true); - endInput.setClickable(true); - endInput.setCursorVisible(true); + endInput.setGravity(Gravity.CENTER_VERTICAL); + endInput.setOnClickListener(v -> { + try { + Log.d(TAG, "endInput clicked, mActivity=" + (mActivity != null ? "not null" : "null")); - // 针对 EditText 的特殊处理 - endInput.setOnTouchListener((v, event) -> { - if (event.getAction() == MotionEvent.ACTION_UP) { - v.requestFocus(); - v.postDelayed(() -> { - InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) { - // 使用 SHOW_FORCED 或确保结果成功 - imm.showSoftInput(v, InputMethodManager.SHOW_FORCED); + // 设置回调 + SearchDestinationActivity.setCallback((name, address, lat, lon, district) -> { + Log.d(TAG, "Callback received: " + name + ", lat=" + lat + ", lon=" + lon); + // 直接设置,不依赖 mActivity.runOnUiThread + setDestination(name, address, lat, lon, district); + }); + + // 跳转到搜索目的地页面 - 使用更安全的方式 + Intent intent = new Intent(mContext, SearchDestinationActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + Log.d(TAG, "Starting SearchDestinationActivity with intent"); + mContext.startActivity(intent); + + } catch (Exception e) { + Log.e(TAG, "Failed to start SearchDestinationActivity", e); + e.printStackTrace(); + // 显示错误提示 + try { + if (mContext != null) { + Toast.makeText(mContext, "无法打开搜索页面: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } - }, 100); - } - return false; - }); - - // 处理搜索键按下 - endInput.setOnEditorActionListener((v, actionId, event) -> { - if (actionId == EditorInfo.IME_ACTION_SEARCH) { - suggestionList.setVisibility(View.GONE); - // 可以在这里直接触发路线规划,但保持原来的按钮点击逻辑 - return true; - } - return false; - }); - - // 初始化提示列表 - currentTipList = new ArrayList<>(); - suggestionList = new ListView(context); - suggestionList.setBackgroundColor(Color.WHITE); - suggestionList.setDividerHeight(1); - suggestionList.setDivider(new ColorDrawable(Color.LTGRAY)); - suggestionList.setPadding(dp2px(5), dp2px(5), dp2px(5), dp2px(5)); - suggestionAdapter = new ArrayAdapter(context, android.R.layout.simple_list_item_1, new ArrayList<>()) { - @NonNull - @Override - public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - // 获取默认的 View - View view = super.getView(position, convertView, parent); - TextView text = view.findViewById(android.R.id.text1); - text.setTextColor(Color.parseColor("#333333")); - text.setTextSize(14); - view.setBackgroundColor(Color.TRANSPARENT); - - return view; - } - }; - suggestionList.setAdapter(suggestionAdapter); - suggestionList.setVisibility(View.GONE); - suggestionList.setOnItemClickListener((parent, view, position, id) -> { - Tip tip = currentTipList.get(position); - if (tip.getPoint() != null) { - endPoint = new LatLng(tip.getPoint().getLatitude(), tip.getPoint().getLongitude()); - String name = tip.getName(); - String district = tip.getDistrict(); - endName = name; - endAddress = tip.getAddress(); - isGetInputtips = true; - isUserSelectedDestination = true; // 标识用户手动选择了目的地 - isProgrammaticTextChange = true; // 标识是程序自动设置文本 - endInput.setText(district != null && !district.isEmpty() ? name + " " + district : name); - isProgrammaticTextChange = false; // 恢复标志 - suggestionList.setVisibility(View.GONE); - } - }); - - endInput.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 (isProgrammaticTextChange) { - return; + } catch (Exception toastException) { + Log.e(TAG, "Failed to show toast", toastException); } - - if (s.length() > 1) { - // 使用当前位置的城市进行搜索 - String city = currentLatLng != null ? "" : ""; - InputtipsQuery query = new InputtipsQuery(s.toString(), ""); - query.setCityLimit(false); - Inputtips inputtips = new Inputtips(mContext, query); - inputtips.setInputtipsListener(NativeMapView.this); - inputtips.requestInputtipsAsyn(); - } else { - suggestionList.setVisibility(View.GONE); - } - } - - @Override - public void afterTextChanged(Editable s) { - } - }); - endInput.setOnFocusChangeListener((v, hasFocus) -> { - if (!hasFocus) { - suggestionList.setVisibility(View.GONE); } }); searchArea.addView(endInput); - searchArea.addView(suggestionList); View vSpace = new View(context); searchArea.addView(vSpace, new LinearLayout.LayoutParams(1, dp2px(12))); @@ -352,8 +262,8 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation // 设置统一的底部间距 FrameLayout.LayoutParams bottomParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); bottomParams.gravity = Gravity.BOTTOM; - bottomParams.setMargins(dp2px(12), 0, dp2px(12), dp2px(65)); container.addView(bottomContainer, bottomParams); + container.setPadding(0,0,0,dp2px(65)); // --- 模式选择菜单 --- @@ -365,6 +275,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation menuParams.setMargins(0, 0, dp2px(15), dp2px(330)); // 高度根据按钮位置调整 container.addView(modeMenu, menuParams); + // 加氢规划圆形按钮 planToggleBtn = new TextView(context); planToggleBtn.setText("加氢\n规划"); @@ -408,67 +319,14 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation locParams.setMargins(0, 0, dp2px(15), dp2px(285)); // 调高一点,避开底部的面板 locParams.gravity = Gravity.BOTTOM | Gravity.END; container.addView(mLocBtn, locParams); - - //最后调用监听函数 - addKeyboardListener(); } - private int lastKeypadHeight = -1; // 记录上次高度,避免重复刷新 - - private void addKeyboardListener() { - container.getViewTreeObserver().addOnGlobalLayoutListener(() -> { - Rect r = new Rect(); - // 获取当前容器在屏幕上的可视区域 - container.getWindowVisibleDisplayFrame(r); - - // 获取容器在屏幕上的绝对位置 - int[] location = new int[2]; - container.getLocationOnScreen(location); - - // 容器底部的绝对屏幕坐标 - int containerBottomOnScreen = location[1] + container.getHeight(); - - // 键盘顶部的绝对屏幕坐标就是 r.bottom - // 计算键盘遮挡了容器多少高度 - int keypadHeight = containerBottomOnScreen - r.bottom; - - // 设置一个阈值(比如 100 像素),过滤掉系统导航栏高度的变化 - if (keypadHeight > 100) { - // 键盘弹出:此时 keypadHeight 是键盘相对于容器底部的真实高度 - if (lastKeypadHeight != keypadHeight) { - lastKeypadHeight = keypadHeight; - updateBottomMargin(keypadHeight); - } - } else { - // 键盘收起:恢复原有间距 dp2px(65) - if (lastKeypadHeight != 0) { - lastKeypadHeight = 0; - updateBottomMargin(dp2px(65)); - } - } - }); - } - - private void updateBottomMargin(int bottomPx) { - if (bottomContainer != null) { - // 使用 post 确保在布局请求之后执行,避免某些机型上的 requestLayout 冲突 - bottomContainer.post(() -> { - FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) bottomContainer.getLayoutParams(); - if (params != null) { - // 如果键盘弹出,我们通常不需要额外的 12dp 左右边距(看你 UI 需求) - // 这里的重点是 bottomPx - params.setMargins(dp2px(12), 0, dp2px(12), bottomPx); - bottomContainer.setLayoutParams(params); - } - }); - } - } private LinearLayout createDetailPanel(Context context) { LinearLayout panel = new LinearLayout(context); panel.setOrientation(LinearLayout.VERTICAL); - panel.setBackground(getRoundedDrawable(Color.WHITE, 16)); - panel.setPadding(dp2px(20), dp2px(20), dp2px(20), dp2px(20)); + int padding = dp2px(20); + panel.setPadding(padding, padding, padding, padding); // --- (包含标题和关闭按钮) --- LinearLayout titleLayout = new LinearLayout(context); @@ -562,7 +420,7 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation Button startNaviBtn = new Button(context); startNaviBtn.setText("开始导航"); startNaviBtn.setTextColor(Color.WHITE); - startNaviBtn.setBackground(getRoundedDrawable(Color.parseColor("#017143"), 10)); + startNaviBtn.setBackground(getRoundedDrawable(Color.parseColor("#017143"), 99)); startNaviBtn.setOnClickListener(v -> startRouteSearch()); LinearLayout.LayoutParams btnParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp2px(48)); @@ -588,62 +446,25 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation //如果是输入地址提示内容 if (isGetInputtips) { - // 开始规划前隐藏输入框面板 - searchArea.setVisibility(View.GONE); - RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(new LatLonPoint(startPoint.latitude, startPoint.longitude), new LatLonPoint(endPoint.latitude, endPoint.longitude)); RouteSearch.DriveRouteQuery query = new RouteSearch.DriveRouteQuery(fromAndTo, RouteSearch.DRIVING_SINGLE_DEFAULT, null, null, ""); routeSearch.calculateDriveRouteAsyn(query); + + + // 开始规划前隐藏输入框面板 + searchArea.setVisibility(View.GONE); } else { fetchTruckRouteAlgorithm(mLoc); } } - @Override - public void onGetInputtips(List tipList, int rCode) { - if (rCode == 1000 && tipList != null && !tipList.isEmpty()) { - currentTipList.clear(); - currentTipList.addAll(tipList); - - List suggestionNames = new ArrayList<>(); - for (Tip tip : tipList) { - String name = tip.getName(); - String district = tip.getDistrict(); - // 在提示中显示名称和区域 - String displayText = district != null && !district.isEmpty() ? name + " " + district : name; - suggestionNames.add(displayText); - } - - suggestionAdapter.clear(); - suggestionAdapter.addAll(suggestionNames); - suggestionAdapter.notifyDataSetChanged(); - - // 显示提示列表 - new Handler(Looper.getMainLooper()).post(() -> { - if (suggestionNames.size() > 0) { - suggestionList.setVisibility(View.VISIBLE); - - suggestionList.setBackgroundColor(Color.WHITE); - - // 限制显示的高度 - int height = Math.min(suggestionNames.size() * dp2px(45), dp2px(200)); - LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - height - ); - suggestionList.setLayoutParams(params); - } else { - suggestionList.setVisibility(View.GONE); - } - }); - } else { - // 请求失败或无结果,隐藏提示列表 - new Handler(Looper.getMainLooper()).post(() -> suggestionList.setVisibility(View.GONE)); - } - } - @Override public void onDriveRouteSearched(DriveRouteResult result, int rCode) { + if (mActivity == null) { + Log.e(TAG, "mActivity is null in onDriveRouteSearched"); + return; + } + mActivity.runOnUiThread(new Runnable() { @Override public void run() { @@ -664,14 +485,19 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation tvStationName.setText(endName); tvStationAddr.setText(endAddress); - tvBusinessHours.setText("营业时间:" + truckRouteData.destinationSite.startBusiness + "-" + - truckRouteData.destinationSite.endBusiness); + + DestinationSite destinationSite = truckRouteData.destinationSite; double distanceKm = truckRouteData.pathDto.distance / 1000f; long durationMin = truckRouteData.pathDto.duration / 60; String tolls = truckRouteData.pathDto.tolls; - String hydrogenCost = "--"; // 默认显示横线 + String hydrogenCost = "-"; // 默认显示横线 + String hydrogenPrice = "-"; // 默认显示横线 + String liaisonName = "-"; + String liaisonPhone = "-"; + String startBusiness = "-"; + String endBusiness = "-"; try { // 增加多级非空校验,防止点击搜索条目时崩溃 if (truckRouteData != null && @@ -684,10 +510,20 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation tvDuration.setText("预计时间:" + durationMin + "分钟"); tvDistance.setText("行驶里程:" + String.format("%.1f", distanceKm) + "公里"); tvTolls.setText("过路费:" + tolls + "元"); - tvTollsFuel.setText("加氢费用:" + (isGetInputtips ? "--" : hydrogenCost) + "元"); - tvPerson.setText("站联系人:"); - tvPrice.setText("加氢价格:"); - tvPhone.setText("联系方式:"); + tvTollsFuel.setText("预计加氢费用:" + (isGetInputtips ? "--" : hydrogenCost) + "元"); + + if (destinationSite != null) { + startBusiness = destinationSite.startBusiness; + endBusiness = destinationSite.endBusiness; + hydrogenPrice = destinationSite.hydrogenPrice; + liaisonName = destinationSite.liaisonName; + liaisonPhone = destinationSite.liaisonPhone; + } + + tvBusinessHours.setText("营业时间:" + startBusiness + "-" + endBusiness); + tvPerson.setText("站联系人:" + liaisonName); + tvPrice.setText("加氢价格:" + hydrogenPrice); + tvPhone.setText("联系方式:" + liaisonPhone); isGetInputtips = false; @@ -975,13 +811,6 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation aMap.setMyLocationEnabled(true); aMap.setOnMarkerClickListener(this); - // 添加地图触摸监听器,点击地图时隐藏提示列表 - aMap.setOnMapClickListener(latLng -> { - if (suggestionList != null && suggestionList.getVisibility() == View.VISIBLE) { - suggestionList.setVisibility(View.GONE); - } - }); - MyLocationStyle myLocationStyle = new MyLocationStyle(); try { Bitmap carBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.car); @@ -1025,6 +854,33 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation mlocationClient.stopLocation(); } + public void setDestination(String name, String address, double lat, double lon, String district) { + endName = name; + endAddress = address; + endPoint = new LatLng(lat, lon); + isUserSelectedDestination = true; + isGetInputtips = true; + + Log.d(TAG, "setDestination called with name=" + name); + + // 简化逻辑,直接设置文本 + endInput.post(() -> { + try { + endInput.setText(district != null && !district.isEmpty() ? name + " " + district : name); + } catch (Exception e) { + Log.e(TAG, "Failed to set text to endInput", e); + } + }); + } + + + /** + * 设置 Activity 实例 + */ + public void setActivity(Activity activity) { + this.mActivity = activity; + } + public void startLocation() { if (mlocationClient == null) { try { @@ -1515,6 +1371,9 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation destinationSite.longitude = siteJson.optString("longitude", ""); destinationSite.latitude = siteJson.optString("latitude", ""); destinationSite.distance = siteJson.optString("distance", ""); + destinationSite.hydrogenPrice = siteJson.optString("hydrogenPrice", ""); + destinationSite.liaisonName = siteJson.optString("liaisonName", ""); + destinationSite.liaisonPhone = siteJson.optString("liaisonPhone", ""); truckRouteData.destinationSite = destinationSite; } @@ -1712,5 +1571,9 @@ public class NativeMapView implements PlatformView, LocationSource, AMapLocation public String longitude; public String latitude; public String distance; + + public String hydrogenPrice; + public String liaisonName; + public String liaisonPhone; } } diff --git a/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/SearchDestinationActivity.java b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/SearchDestinationActivity.java new file mode 100644 index 0000000..89a6420 --- /dev/null +++ b/ln_jq_app/android/app/src/main/java/com/lnkj/ln_jq_app/SearchDestinationActivity.java @@ -0,0 +1,261 @@ +package com.lnkj.ln_jq_app; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.Log; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import com.amap.api.services.help.Inputtips; +import com.amap.api.services.help.InputtipsQuery; +import com.amap.api.services.help.Tip; + +import java.util.ArrayList; +import java.util.List; + +import androidx.activity.ComponentActivity; + +/** + * 搜索目的地页面 + */ +public class SearchDestinationActivity extends ComponentActivity implements Inputtips.InputtipsListener { + private static final String TAG = "SearchDestinationActivity"; + + public static final String EXTRA_RESULT_NAME = "result_name"; + public static final String EXTRA_RESULT_ADDRESS = "result_address"; + public static final String EXTRA_RESULT_LAT = "result_lat"; + public static final String EXTRA_RESULT_LON = "result_lon"; + public static final String EXTRA_RESULT_DISTRICT = "result_district"; + + // 静态回调接口 + public interface DestinationCallback { + void onDestinationSelected(String name, String address, double lat, double lon, String district); + } + + private static DestinationCallback callback; + + public static void setCallback(DestinationCallback callback) { + SearchDestinationActivity.callback = callback; + } + + private EditText searchInput; + private ListView suggestionList; + private ImageView backBtn; + + private ArrayAdapter suggestionAdapter; + private List currentTipList; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + try { + setContentView(R.layout.activity_search_destination); + + initViews(); + setupListeners(); + + // 自动显示键盘 + showKeyboard(); + } catch (Exception e) { + Log.e(TAG, "Error in onCreate", e); + e.printStackTrace(); + finish(); + } + } + + private void initViews() { + try { + Log.d(TAG, "initViews started"); + searchInput = findViewById(R.id.search_input); + suggestionList = findViewById(R.id.suggestion_list); + backBtn = findViewById(R.id.back_btn); + + currentTipList = new ArrayList<>(); + + Log.d(TAG, "initViews completed: searchInput=" + (searchInput != null ? "ok" : "null") + ", suggestionList=" + (suggestionList != null ? "ok" : "null") + ", backBtn=" + (backBtn != null ? "ok" : "null")); + } catch (Exception e) { + Log.e(TAG, "Error in initViews", e); + e.printStackTrace(); + } + + suggestionAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, new ArrayList<>()) { + @Override + public View getView(int position, android.view.View convertView, android.view.ViewGroup parent) { + View view = super.getView(position, convertView, parent); + TextView text = view.findViewById(android.R.id.text1); + text.setTextColor(Color.parseColor("#333333")); + text.setTextSize(14); + view.setBackgroundColor(Color.TRANSPARENT); + return view; + } + }; + suggestionList.setAdapter(suggestionAdapter); + suggestionList.setDivider(new ColorDrawable(Color.LTGRAY)); + suggestionList.setDividerHeight(1); + } + + + private void setupListeners() { + try { + Log.d(TAG, "setupListeners started"); + + // 返回按钮 + backBtn.setOnClickListener(v -> { + Log.d(TAG, "Back button clicked"); + hideKeyboard(); + finish(); + }); + + // 文本变化监听 + 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) { + Log.d(TAG, "Text changed: " + (s != null ? s.toString() : "null") + ", length=" + (s != null ? s.length() : 0)); + if (s.length() > 1) { + searchLocation(s.toString()); + } else { + suggestionList.setVisibility(View.GONE); + } + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + + // 列表点击 + suggestionList.setOnItemClickListener((parent, view, position, id) -> { + Log.d(TAG, "Suggestion clicked at position: " + position); + try { + Tip tip = currentTipList.get(position); + if (tip.getPoint() != null) { + String name = tip.getName(); + String address = tip.getAddress(); + String district = tip.getDistrict(); + double lat = tip.getPoint().getLatitude(); + double lon = tip.getPoint().getLongitude(); + + Log.d(TAG, "Selected destination: " + name + ", lat=" + lat + ", lon=" + lon); + + // 优先使用静态回调 + if (callback != null) { + callback.onDestinationSelected(name, address, lat, lon, district); + } else { + // 降级使用 Intent 回调 + Intent result = new Intent(); + result.putExtra(EXTRA_RESULT_NAME, name); + result.putExtra(EXTRA_RESULT_ADDRESS, address); + result.putExtra(EXTRA_RESULT_LAT, lat); + result.putExtra(EXTRA_RESULT_LON, lon); + result.putExtra(EXTRA_RESULT_DISTRICT, district); + setResult(RESULT_OK, result); + } + + hideKeyboard(); + finish(); + } + } catch (Exception e) { + Log.e(TAG, "Error handling suggestion click", e); + e.printStackTrace(); + } + }); + + Log.d(TAG, "setupListeners completed"); + } catch (Exception e) { + Log.e(TAG, "Error in setupListeners", e); + e.printStackTrace(); + } + } + + private void searchLocation(String keyword) { + try { + Log.d(TAG, "Searching for: " + keyword); + InputtipsQuery query = new InputtipsQuery(keyword, ""); + query.setCityLimit(false); + Inputtips inputtips = new Inputtips(this, query); + inputtips.setInputtipsListener(this); + inputtips.requestInputtipsAsyn(); + } catch (Exception e) { + Log.e(TAG, "Error in searchLocation", e); + e.printStackTrace(); + } + } + + + @Override + public void onGetInputtips(List tipList, int rCode) { + Log.d(TAG, "onGetInputtips called, rCode=" + rCode + ", tipList size=" + (tipList != null ? tipList.size() : "null")); + if (rCode == 1000 && tipList != null && !tipList.isEmpty()) { + currentTipList.clear(); + currentTipList.addAll(tipList); + + List suggestionNames = new ArrayList<>(); + for (Tip tip : tipList) { + String name = tip.getName(); + String district = tip.getDistrict(); + String displayText = district != null && !district.isEmpty() ? name + " " + district : name; + suggestionNames.add(displayText); + } + + runOnUiThread(() -> { + suggestionAdapter.clear(); + suggestionAdapter.addAll(suggestionNames); + suggestionAdapter.notifyDataSetChanged(); + + if (suggestionNames.size() > 0) { + suggestionList.setVisibility(View.VISIBLE); + } else { + suggestionList.setVisibility(View.GONE); + } + }); + } else { + runOnUiThread(() -> { + suggestionList.setVisibility(View.GONE); + }); + } + } + + + private void showKeyboard() { + searchInput.requestFocus(); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT); + } + } + + private void hideKeyboard() { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0); + } + } + + + @Override + public void onPointerCaptureChanged(boolean hasCapture) { + super.onPointerCaptureChanged(hasCapture); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + // 清理回调,避免内存泄漏 + callback = null; + } +} \ No newline at end of file diff --git a/ln_jq_app/android/app/src/main/res/drawable/back.png b/ln_jq_app/android/app/src/main/res/drawable/back.png new file mode 100644 index 0000000000000000000000000000000000000000..07aaa8ab962f22376fa62d3a13cd0aa1f5fb9f17 GIT binary patch literal 705 zcmV;y0zUnTP)HGfHIr8%4YoLT)J%1gwyep2w&RR_?l%520c<<~b~6HI z1ZuF# zQUh3vm*$JC>5&$I#NVX!+w!Y=tEp(d0aH!iso0-WDDKPsCsFytnZB{!joC&#G=ck9h9Y z`MI}Y02cAK%ykzJ-4Fm-#D@ULB)&TUi6`G}*i+`Z<~*XUbxVOoJms#Ak9amZ^*W?= z2O!~WaPWK;kLVQQA?^wQ@fzfZcxt>12znHMxB)QYtIBS>yE@=2ts4Lhj_1{sgBpJ* zy<{gB0P?YF_2i3xE~PmzlK!ltD9{TmUSqy#4+zEd&<; zcvuj~`!xja@i>jS0YKps2&dHo$;U#?6#&YzLvRO>9YPlX27=uTkq&mV#K*c@HvlM8 zI#3=h1pZKJ{{KG4deyoDU{XMkeW(u%HRT8^lidLnxjG2~kq}-c!vKiEP7v4^SO}~O z+~aW<3k86}Xb9l|q9LRJU?8wAaEXx)?qxC!02C`7s0 + + + + + + + + \ No newline at end of file diff --git a/ln_jq_app/android/app/src/main/res/drawable/rounded_top_bg.xml b/ln_jq_app/android/app/src/main/res/drawable/rounded_top_bg.xml new file mode 100644 index 0000000..a52964d --- /dev/null +++ b/ln_jq_app/android/app/src/main/res/drawable/rounded_top_bg.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/ln_jq_app/android/app/src/main/res/layout/activity_search_destination.xml b/ln_jq_app/android/app/src/main/res/layout/activity_search_destination.xml new file mode 100644 index 0000000..7d7a842 --- /dev/null +++ b/ln_jq_app/android/app/src/main/res/layout/activity_search_destination.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + \ No newline at end of file