鸿蒙实现滴滴出行项目之侧边抽屉栏以及权限以及搜索定位功能

目录:

1、侧边抽屉栏效果以及实现伪代码

typescript 复制代码
@Entry
@Component
struct SideBarDemo {
  @State showSideBar: boolean = false;
  @State selectedIndex: number = 0;
  private menuList: string[] = ["首页", "消息", "收藏", "设置"];
  @State userBalance: number = 239.99;
  @State userCoupons: number = 20;

  build() {
    // 根容器:层叠布局
    Stack() {
      // 1. 主内容区域
      Column() {
        Text(`当前选中:${this.menuList[this.selectedIndex]}`)
          .fontSize(24)
          .margin(30);

        Button("打开侧边栏")
          .width(150)
          .height(40)
          .onClick(() => {
            this.showSideBar = true; // 点击按钮显示侧边栏
          });
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#FFFFFF')
      // 点击主内容区关闭侧边栏
      .gesture(
        TapGesture()
          .onAction(() => {
            if (this.showSideBar) this.showSideBar = false;
          })
      );

      // 2. 侧边栏(条件渲染)
      if (this.showSideBar) {
        Column() {
          // 用户信息区域
          Column() {
            // 用户头像和登录状态
            Row() {
              // 用户头像
              Circle({ width: 60, height: 60 })
                .fill('#E0E0E0')
                .margin({ right: 15 });
              
              Column() {
                Text("立即登录")
                  .fontSize(18)
                  .fontWeight(500)
                  .fontColor('#333333')
                  .margin({ bottom: 5 });
                
                Text("点击登录享受更多服务")
                  .fontSize(12)
                  .fontColor('#666666');
              }
              .alignItems(HorizontalAlign.Start);
            }
            .width('100%')
            .padding({ top: 30, bottom: 20, left: 20, right: 20 })
            .alignItems(VerticalAlign.Center);

            // 余额和优惠券区域
            Row() {
              // 余额区域
              Column() {
                Text("余额 (元)")
                  .fontSize(14)
                  .fontColor('#666666')
                  .margin({ bottom: 8 });
                Text(this.userBalance.toString())
                  .fontSize(24)
                  .fontWeight(600)
                  .fontColor('#FF6B35');
              }
              .layoutWeight(1)
              .alignItems(HorizontalAlign.Center)
              .padding({ top: 20, bottom: 20 })
              .backgroundColor('#FFF5F0')
              .borderRadius(8)
              .margin({ right: 8 });

              // 优惠券区域
              Column() {
                Text("优惠券 (张)")
                  .fontSize(14)
                  .fontColor('#666666')
                  .margin({ bottom: 8 });
                Text(this.userCoupons.toString())
                  .fontSize(24)
                  .fontWeight(600)
                  .fontColor('#FF6B35');
              }
              .layoutWeight(1)
              .alignItems(HorizontalAlign.Center)
              .padding({ top: 20, bottom: 20 })
              .backgroundColor('#FFF5F0')
              .borderRadius(8)
              .margin({ left: 8 });
            }
            .width('100%')
            .padding({ left: 20, right: 20, bottom: 20 });

            // 分割线
            Divider()
              .color('#EEEEEE')
              .strokeWidth(1)
              .margin({ left: 20, right: 20 });

            // 菜单列表
            List() {
              ForEach(this.menuList, (item: string, index: number) => {
                ListItem() {
                  Text(item)
                    .fontSize(16)
                    .width('100%')
                    .height(50)
                    .textAlign(TextAlign.Start)
                    .padding({ left: 20 })
                    .backgroundColor(this.selectedIndex === index ? '#F0F8FF' : 'transparent')
                    .onClick(() => {
                      this.selectedIndex = index;
                      this.showSideBar = false;
                    });
                }
              }, (item: string) => item);
            }
            .width('100%')
            .divider({ strokeWidth: 1, color: '#F0F0F0' })
            .margin({ top: 10 });
          }
        }
        .width(280) // 侧边栏宽度
        .height('100%')
        .backgroundColor('#FFFFFF')
        .align(Alignment.Start) // 内容左对齐
        // 侧边栏滑动关闭手势(向左滑动)
        .gesture(
          PanGesture()
            .onActionUpdate((event) => {
              // 使用event.offsetX获取手势坐标
              if (event.offsetX < 50) { // 滑动到左侧50px外关闭
                this.showSideBar = false;
              }
            })
        )
        // API 9过渡动画:使用基本淡入淡出+位移(避免TransitionType)
        .transition({
          opacity: 0, // 透明度从0到1
          translate: { x: -280 } // X轴从-280px(左侧外)移动到0
        });
      }
    }
    .width('100%')
    .height('100%')
    // 主内容区滑动打开手势(左侧边缘向右滑动)
    .gesture(
      PanGesture()
        .onActionStart((event) => {
          // 使用event.offsetX获取手势起始坐标
          if (event.offsetX < 30 && !this.showSideBar) { // 左侧30px内触发
            this.showSideBar = true;
          }
        })
    );
  }
}

2、定位权限开启

Step 1:声明定位权限(module.json5)

在 main/module.json5 中声明定位相关权限(决定系统弹窗显示的权限类型):

typescript 复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.LOCATION", // 基础定位权限(必选)
        "reason": "用于获取您的位置以提供出行服务", // 权限申请理由(用户可见)
        "usedScene": {
          "abilities": ["EntryAbility"], // 关联的Ability
          "when": "inuse" // 仅在应用使用时申请
        }
      },
      {
        "name": "ohos.permission.ACCESS_FINE_LOCATION", // 精确定位权限(可选,API 10+)
        "reason": "用于获取精确位置以推荐附近车辆",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      }
    ]
  }
}

Step 2:动态申请定位权限(触发系统弹窗)

通过鸿蒙的 abilityAccessCtrl 模块动态申请权限,系统会自动弹出原生弹窗(无需自定义UI):

typescript 复制代码
// utils/LocationPermission.ts
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';
import promptAction from '@ohos.promptAction';

export class LocationPermission {
  private atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();

  /**
   * 申请定位权限(触发系统原生弹窗)
   * @param context 当前Ability上下文
   * @returns 是否获取权限
   */
  async request(context: any): Promise<boolean> {
    const permissions = ['ohos.permission.LOCATION'];
    try {
      // 1. 检查权限状态
      const tokenId = context.accessTokenInfo.tokenId;
      const status = await this.atManager.checkPermissions(tokenId, permissions);

      if (status[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
        // 2. 已授权:直接返回true
        return true;
      } else {
        // 3. 未授权:动态申请权限(系统自动弹出弹窗)
        const result = await this.atManager.requestPermissionsFromUser(context, permissions);
        // 4. 返回申请结果(0=授权,-1=拒绝)
        return result.authResults[0] === 0;
      }
    } catch (err) {
      console.error('定位权限申请失败:', err);
      return false;
    }
  }
}

Step 3:获取定位数据(权限获取后)

使用鸿蒙 @ohos.geolocation 模块 获取用户实时位置(经纬度):

typescript 复制代码
// services/LocationService.ts
import geolocation from '@ohos.geolocation';
import { BusinessError } from '@ohos.base';

export class LocationService {
  /**
   * 获取当前位置(单次定位)
   * @returns 经纬度坐标 { latitude: number, longitude: number }
   */
  async getCurrentLocation(): Promise<{ latitude: number; longitude: number }> {
    return new Promise((resolve, reject) => {
      // 定位参数配置
      const options: geolocation.LocationRequest = {
        timeout: 5000, // 超时时间(ms)
        coordinateType: geolocation.CoordinateType.COORDINATE_TYPE_WGS84, // WGS84坐标系(国际标准)
        needAddress: false // 是否需要详细地址(选填)
      };

      // 调用系统定位API
      geolocation.getCurrentLocation(options, (err: BusinessError, location: geolocation.Location) => {
        if (err) {
          reject(`定位失败: ${err.message}`);
          return;
        }
        // 返回经纬度
        resolve({
          latitude: location.latitude, // 纬度
          longitude: location.longitude // 经度
        });
      });
    });
  }
}

Step 4:在页面中集成(完整流程)

在出行项目的首页(如"立即登录"所在页面)中,按以下流程调用:

typescript 复制代码
// pages/HomePage.ets
import { LocationPermission } from '../utils/LocationPermission';
import { LocationService } from '../services/LocationService';

@Entry
@Component
struct HomePage {
  private permissionManager: LocationPermission = new LocationPermission();
  private locationService: LocationService = new LocationService();
  @State userLocation: string = '获取位置中...';

  async aboutToAppear() {
    // 页面加载时触发定位流程
    await this.startLocationProcess();
  }

  /**
   * 完整定位流程:申请权限 → 获取位置 → 显示结果
   */
  private async startLocationProcess() {
    // 1. 申请定位权限(触发系统弹窗)
    const hasPermission = await this.permissionManager.request(getContext(this));
    if (!hasPermission) {
      this.userLocation = '定位权限被拒绝,请手动开启';
      return;
    }

    // 2. 获取当前位置
    try {
      const location = await this.locationService.getCurrentLocation();
      this.userLocation = `当前位置:(${location.latitude.toFixed(6)}, ${location.longitude.toFixed(6)})`;
      // 3. 将位置传递给地图组件显示(如截图中的地图界面)
      this.updateMapLocation(location);
    } catch (err) {
      this.userLocation = '获取位置失败,请重试';
    }
  }

  /**
   * 更新地图显示(需结合地图组件,如百度地图/高德地图鸿蒙SDK)
   */
  private updateMapLocation(location: { latitude: number; longitude: number }) {
    // 示例:调用地图组件API设置中心坐标
    // MapComponent.setCenterLocation(location.latitude, location.longitude);
  }

  build() {
    Column() {
      // 地图组件(此处省略,需集成第三方地图SDK或自定义地图)
      Text(this.userLocation).fontSize(16).margin(20);
      Button('立即登录').width(200).height(40).margin(10);
      // ... 其他UI元素
    }
    .width('100%')
    .height('100%');
  }
}

3、获取定位以及监听定位

3.1、使用场景

3.2、实现步骤

一、基础准备
  1. 声明权限(module.json5)
    在 main/module.json5 中声明定位权限(必选):
typescript 复制代码
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.LOCATION", // 基础定位权限(必选)
        "reason": "获取位置信息以提供服务",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      },
      {
        "name": "ohos.permission.ACCESS_FINE_LOCATION", // 精确定位(可选,API 10+)
        "reason": "获取精确位置",
        "usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
      }
    ]
  }
}
  1. 导入定位模块
typescript 复制代码
import geolocation from '@ohos.geolocation'; // 定位核心模块
import { BusinessError } from '@ohos.base';
二、实现定位工具类(封装单次/连续定位)

创建 LocationManager.ets 封装定位功能,包含权限检查、单次定位、连续监听:

typescript 复制代码
// utils/LocationManager.ets
import geolocation from '@ohos.geolocation';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import { BusinessError } from '@ohos.base';

export class LocationManager {
  private static instance: LocationManager | null = null;
  private locationListener: geolocation.LocationChangeListener | null = null; // 连续定位监听器

  // 单例模式(避免重复创建)
  static getInstance(): LocationManager {
    if (!this.instance) {
      this.instance = new LocationManager();
    }
    return this.instance;
  }

  /**
   * 检查定位权限(内部调用,无需外部关注)
   */
  private async checkPermission(context: any): Promise<boolean> {
    try {
      const atManager = abilityAccessCtrl.createAtManager();
      const tokenId = context.accessTokenInfo.tokenId;
      const status = await atManager.checkPermissions(tokenId, ['ohos.permission.LOCATION']);
      return status[0] === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
    } catch (err) {
      console.error('权限检查失败:', err);
      return false;
    }
  }

  /**
   * 1. 单次定位(获取当前位置)
   * @param context 上下文(Ability实例)
   * @returns 位置信息 { latitude: 纬度, longitude: 经度, accuracy: 精度 }
   */
  async getSingleLocation(context: any): Promise<{
    latitude: number;
    longitude: number;
    accuracy?: number;
  }> {
    // 检查权限
    if (!await this.checkPermission(context)) {
      throw new Error('定位权限未授权');
    }

    return new Promise((resolve, reject) => {
      // 定位参数配置
      const options: geolocation.LocationRequest = {
        timeout: 10000, // 超时时间(ms)
        coordinateType: geolocation.CoordinateType.COORDINATE_TYPE_WGS84, // WGS84坐标系
        needAddress: false, // 是否需要地址信息(选填)
        maxAccuracy: 100 // 期望精度(米,可选)
      };

      // 发起单次定位
      geolocation.getCurrentLocation(options, (err: BusinessError, location: geolocation.Location) => {
        if (err) {
          reject(`单次定位失败: ${err.message}`);
          return;
        }
        resolve({
          latitude: location.latitude,
          longitude: location.longitude,
          accuracy: location.accuracy // 定位精度(米)
        });
      });
    });
  }

  /**
   * 2. 连续定位(监听位置变化)
   * @param context 上下文(Ability实例)
   * @param onLocationChange 位置变化回调(实时返回新位置)
   */
  async startContinuousLocation(
    context: any,
    onLocationChange: (location: { latitude: number; longitude: number }) => void
  ): Promise<void> {
    // 检查权限
    if (!await this.checkPermission(context)) {
      throw new Error('定位权限未授权');
    }

    // 停止已有监听(避免重复监听)
    if (this.locationListener) {
      this.stopContinuousLocation();
    }

    // 创建监听器
    this.locationListener = (location: geolocation.Location) => {
      onLocationChange({
        latitude: location.latitude,
        longitude: location.longitude
      });
    };

    // 配置连续定位参数
    const options: geolocation.LocationRequest = {
      interval: 5000, // 定位间隔(ms,最小1000ms)
      fastestInterval: 3000, // 最快响应间隔(ms)
      priority: geolocation.LocationRequestPriority.HIGH_ACCURACY, // 高精度优先
      coordinateType: geolocation.CoordinateType.COORDINATE_TYPE_WGS84
    };

    // 开始连续定位监听
    geolocation.on('locationChange', this.locationListener, options);
  }

  /**
   * 3. 停止连续定位(页面关闭时调用,避免内存泄漏)
   */
  stopContinuousLocation(): void {
    if (this.locationListener) {
      geolocation.off('locationChange', this.locationListener);
      this.locationListener = null;
      console.log('连续定位已停止');
    }
  }
}
三、在页面中使用(完整示例)

在出行项目的页面(如行程页)中调用 LocationManager,实现单次定位和连续监听:

typescript 复制代码
// pages/RidePage.ets
import { LocationManager } from '../utils/LocationManager';
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct RidePage {
  private locationManager: LocationManager = LocationManager.getInstance();
  @State currentLat: number = 0; // 当前纬度
  @State currentLng: number = 0; // 当前经度
  @State isTracking: boolean = false; // 是否正在连续定位

  // 页面加载时获取单次定位
  async aboutToAppear() {
    await this.getSingleLocation();
  }

  // 页面退出时停止连续定位(必须调用,否则耗电)
  aboutToDisappear() {
    this.locationManager.stopContinuousLocation();
  }

  /**
   * 单次定位:获取初始位置
   */
  async getSingleLocation() {
    try {
      const location = await this.locationManager.getSingleLocation(getContext(this));
      this.currentLat = location.latitude;
      this.currentLng = location.longitude;
      promptAction.showToast({ message: `已获取位置: ${location.latitude}, ${location.longitude}` });
    } catch (err) {
      promptAction.showToast({ message: `定位失败: ${err.message}` });
    }
  }

  /**
   * 连续定位:开始/停止监听位置变化
   */
  async toggleContinuousLocation() {
    if (!this.isTracking) {
      // 开始连续定位
      try {
        await this.locationManager.startContinuousLocation(
          getContext(this),
          (newLocation) => {
            // 实时更新UI
            this.currentLat = newLocation.latitude;
            this.currentLng = newLocation.longitude;
            console.log(`位置更新: ${newLocation.latitude}, ${newLocation.longitude}`);
          }
        );
        this.isTracking = true;
        promptAction.showToast({ message: '开始连续定位' });
      } catch (err) {
        promptAction.showToast({ message: `连续定位失败: ${err.message}` });
      }
    } else {
      // 停止连续定位
      this.locationManager.stopContinuousLocation();
      this.isTracking = false;
      promptAction.showToast({ message: '停止连续定位' });
    }
  }

  build() {
    Column() {
      Text(`当前位置: ${this.currentLat.toFixed(6)}, ${this.currentLng.toFixed(6)}`)
        .fontSize(16)
        .margin(20);

      Button(this.isTracking ? '停止连续定位' : '开始连续定位')
        .width(200)
        .height(40)
        .margin(10)
        .onClick(() => this.toggleContinuousLocation());

      Button('重新获取单次定位')
        .width(200)
        .height(40)
        .onClick(() => this.getSingleLocation());
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }
}

4、地位数据和地图交互

4.1、效果图

4.2、关键代码

  1. 创建 WebView 页面(加载高德H5地图)
typescript 复制代码
// pages/WebMapPage.ets
import webview from '@ohos.web.webview';
import promptAction from '@ohos.promptAction';
import { LocationManager } from '../utils/LocationManager';

@Entry
@Component
struct WebMapPage {
  private webController: webview.WebviewController = new webview.WebviewController();
  private locationManager: LocationManager = LocationManager.getInstance();

  build() {
    Column() {
      // WebView组件(加载高德H5地图)
      Web({
        src: 'https://uri.amap.com/marker', // 高德H5地图URL(可替换为自定义HTML)
        controller: this.webController
      })
      .width('100%')
      .height('100%')
      .onPageEnd(() => {
        // 页面加载完成后开始定位
        this.startLocationAndUpdateMap();
      })
    }
    .width('100%')
    .height('100%')
  }

  // 开始定位并更新地图
  private async startLocationAndUpdateMap() {
    // 1. 获取定位权限
    const context = getContext(this);
    if (!await this.locationManager.checkPermission(context)) {
      promptAction.showToast({ message: '请授予定位权限' });
      return;
    }

    // 2. 获取当前位置(单次定位)
    try {
      const location = await this.locationManager.getSingleLocation(context);
      // 3. 坐标转换(WGS84→GCJ02)
      const gcj02 = coordtransform.wgs84togcj02(location.longitude, location.latitude);
      // 4. 调用WebView的JS方法更新地图
      this.updateWebMap(gcj02[0], gcj02[1]);
    } catch (err) {
      promptAction.showToast({ message: `定位失败: ${err.message}` });
    }
  }

  // 通过JS更新H5地图中心点
  private updateWebMap(longitude: number, latitude: number) {
    const jsCode = `
      // 高德H5地图API:移动中心点到新位置
      if (window.AMap) {
        const map = new AMap.Map('container', {
          center: [${longitude}, ${latitude}],
          zoom: 16
        });
        new AMap.Marker({
          position: [${longitude}, ${latitude}],
          map: map
        });
      } else {
        console.error('高德地图JS未加载');
      }
    `;
    // 执行JS代码
    this.webController.executeJs({
      script: jsCode,
      callback: (result) => {
        console.log('地图更新结果:', result);
      }
    });
  }
}

总结:

通过 WebView + 高德H5地图API 实现定位与地图交互的流程如下:

  • 获取定位:使用 @ohos.geolocation 获取WGS84坐标。

  • 坐标转换:通过 coordtransform 转换为GCJ02坐标。

  • 更新地图:调用 webController.executeJs() 执行高德地图的JS API。

  • 优化体验:处理权限、错误及性能问题。

  • 此方案适合快速集成地图功能,无需原生SDK,但需注意 网络依赖 和 坐标偏移问题。

5、终点搜索定位

5.1、效果图

5.2、实现伪代码



typescript 复制代码
// services/MapApiService.ets
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';

export class MapApiService {
  private apiKey: string = '你的高德WebService API Key'; // 替换为实际Key
  private baseUrl: string = 'https://restapi.amap.com/v3/place/text'; // 高德POI搜索接口

  /**
   * 搜索POI(兴趣点)
   * @param keyword 搜索关键词(如"广州塔")
   * @param city 城市(如"广州",可选,缩小搜索范围)
   * @returns 候选地址列表
   */
  async searchPoi(keyword: string, city?: string): Promise<Array<{
    name: string; // 地址名称
    address: string; // 详细地址
    longitude: number; // 经度
    latitude: number; // 纬度
  }>> {
    return new Promise((resolve, reject) => {
      // 1. 构建请求参数
      const params = new Map<string, string>();
      params.set('key', this.apiKey);
      params.set('keywords', keyword);
      params.set('offset', '10'); // 最多返回10条结果(如截图中的列表长度)
      params.set('page', '1');
      if (city) params.set('city', city);

      // 2. 拼接请求URL
      const queryString = Array.from(params.entries())
        .map(([k, v]) => `${k}=${encodeURIComponent(v)}`)
        .join('&');
      const requestUrl = `${this.baseUrl}?${queryString}`;

      // 3. 发起HTTP GET请求
      let httpRequest = http.createHttp();
      httpRequest.request(
        requestUrl,
        { method: http.RequestMethod.GET },
        (err: BusinessError, data: http.HttpResponse) => {
          httpRequest.destroy(); // 销毁请求实例
          if (err) {
            reject(`搜索失败: ${err.message}`);
            return;
          }

          // 4. 解析返回数据(高德POI接口返回格式)
          if (data.responseCode === 200) {
            const result = JSON.parse(data.result as string);
            if (result.status === '1' && result.pois) {
              // 提取需要的字段(名称、地址、经纬度)
              const pois = result.pois.map((poi: any) => ({
                name: poi.name,
                address: poi.address || poi.adname, // 若address为空,用区域名
                longitude: parseFloat(poi.location.split(',')[0]),
                latitude: parseFloat(poi.location.split(',')[1])
              }));
              resolve(pois);
            } else {
              reject(`搜索无结果: ${result.info}`);
            }
          } else {
            reject(`HTTP错误: ${data.responseCode}`);
          }
        }
      );
    });
  }
}

Step 3:实现搜索页面UI(候选列表+输入框)

创建 PoiSearchPage.ets,实现截图中的搜索框、候选列表、键盘交互:

typescript 复制代码
// pages/PoiSearchPage.ets
import { MapApiService } from '../services/MapApiService';
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct PoiSearchPage {
  @State keyword: string = ''; // 搜索关键词
  @State poiList: Array<{ name: string; address: string; longitude: number; latitude: number }> = [];
  private mapApi: MapApiService = new MapApiService();
  private searchDebounceTimer: number | null = null; // 防抖定时器(避免输入过快频繁调用API)

  build() {
    Column() {
      // 1. 搜索栏(输入框+清除按钮)
      Row() {
        Image($r('app.media.ic_search')) // 搜索图标(需添加本地资源)
          .width(20)
          .height(20)
          .margin(10);

        TextInput({ placeholder: '搜索地址' })
          .width('flex')
          .height(40)
          .onChange((value: string) => {
            this.keyword = value;
            this.debounceSearch(); // 防抖处理后调用搜索
          })
          .placeholderColor('#999999');

        if (this.keyword.length > 0) {
          Button({ type: ButtonType.Circle, stateEffect: true }) {
            Image($r('app.media.ic_clear')) // 清除图标(需添加本地资源)
              .width(18)
              .height(18);
          }
          .width(40)
          .height(40)
          .onClick(() => this.keyword = ''); // 清空输入
        }
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#FFFFFF')
      .borderBottom({ width: 0.5, color: '#EEEEEE' });

      // 2. 候选地址列表(如截图中的地址列表)
      List() {
        ForEach(this.poiList, (item) => {
          ListItem() {
            Column() {
              Text(item.name)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
                .textAlign(TextAlign.Start)
                .width('100%');

              Text(item.address)
                .fontSize(14)
                .color('#666666')
                .textAlign(TextAlign.Start)
                .width('100%')
                .margin({ top: 2 });
            }
            .padding(15)
            .backgroundColor('#FFFFFF');
          }
          .onClick(() => {
            // 3. 选中地址:返回上一页并传递坐标(假设通过router返回)
            router.back({
              params: {
                selectedPoi: item // 传递选中的地址信息(名称、经纬度等)
              }
            });
          });
        }, (item) => item.name);
      }
      .width('100%')
      .backgroundColor('#F5F5F5')
      .divider({ strokeWidth: 0.5, color: '#EEEEEE' }); // 列表项分隔线
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5');
  }

  /**
   * 防抖处理:用户输入停止500ms后再调用搜索接口(避免频繁请求)
   */
  private debounceSearch() {
    if (this.searchDebounceTimer) {
      clearTimeout(this.searchDebounceTimer);
    }
    this.searchDebounceTimer = setTimeout(async () => {
      if (this.keyword.trim().length < 2) {
        this.poiList = []; // 关键词过短,清空列表
        return;
      }
      // 调用POI搜索API
      try {
        const result = await this.mapApi.searchPoi(this.keyword, '广州'); // 限定城市为"广州"
        this.poiList = result;
      } catch (err) {
        promptAction.showToast({ message: `搜索失败: ${err.message}` });
      }
    }, 500); // 500ms防抖
  }
}

Step 4:地图页面接收选中地址并定位

在地图页面(如 MapPage.ets)中,接收从搜索页返回的地址信息,调用地图API定位到选中位置:

typescript 复制代码
// pages/MapPage.ets
import router from '@ohos.router';

@Entry
@Component
struct MapPage {
  @State selectedPoi: { name: string; longitude: number; latitude: number } | null = null;

  aboutToAppear() {
    // 获取从搜索页返回的选中地址
    const params = router.getParams();
    if (params?.selectedPoi) {
      this.selectedPoi = params.selectedPoi;
      this.navigateToSelectedPoi(); // 定位到选中地址
    }
  }

  // 定位到选中地址
  private navigateToSelectedPoi() {
    if (!this.selectedPoi) return;
    // 调用地图API,移动到选中地址的经纬度(以高德地图SDK为例)
    mapController.setCenter({
      longitude: this.selectedPoi.longitude,
      latitude: this.selectedPoi.latitude
    }, true); // true:带动画
    // 添加选中标记(参考前面章节的Marker代码)
  }

  build() {
    Column() {
      // 地图组件(省略,参考前面章节)
      Button('选择地址')
        .onClick(() => {
          router.pushUrl({ url: 'pages/PoiSearchPage' }); // 跳转到搜索页
        });
    }
  }
}
相关推荐
zcz16071278212 小时前
Docker Compose 搭建 LNMP 环境并部署 WordPress 论坛
android·adb·docker
yenggd5 小时前
vxlan-bgp-evnp分布式网关配置案例
网络·分布式·华为
Pika12 小时前
深入浅出 Compose 测量机制
android·android jetpack·composer
Danileaf_Guo14 小时前
华为VXLAN小实验:静态方式手工建立隧道
华为
木易 士心17 小时前
MPAndroidChart 用法解析和性能优化 - Kotlin & Java 双版本
android·java·kotlin
消失的旧时光-194317 小时前
Kotlin Flow 与“天然背压”(完整示例)
android·开发语言·kotlin
ClassOps17 小时前
Kotlin invoke 函数调用重载
android·开发语言·kotlin
爱笑的眼睛1117 小时前
深入浅出 HarmonyOS 应用开发:ArkTS 语法精要与实践
华为·harmonyos
IT葛大侠18 小时前
华为S5720配置telnet远程
网络·华为