HarmonyOS设备能力调用:地理位置、传感器与相机

设备能力调用是移动应用开发中的重要环节,HarmonyOS提供了丰富的API来访问设备硬件功能。本文将深入讲解地理位置服务、传感器数据获取和相机调用的完整实现,帮助您构建功能丰富的硬件交互应用。

一、地理位置服务

1.1 地理位置基础概念

地理位置服务是现代应用的核心功能之一,广泛应用于导航、社交、电商等场景。

核心能力

  • 精确定位:获取设备的经纬度坐标
  • 地址解析:将坐标转换为具体地址信息
  • 地理围栏:监控设备进入/离开特定区域
  • 运动状态:检测设备的移动速度和方向

1.2 权限配置与初始化

首先需要在module.json5中配置位置权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.LOCATION",
        "reason": "需要获取位置信息提供导航服务",
        "usedScene": {
          "abilities": ["MainAbility"],
          "when": "always"
        }
      }
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION", 
        "reason": "需要获取大致位置信息",
        "usedScene": {
          "abilities": ["MainAbility"],
          "when": "always"
        }
      }
    ]
  }
}

1.3 地理位置获取实战

import geolocation from '@ohos.geolocation'

@Component
struct LocationServiceExample {
  @State currentLocation: string = '正在获取位置...'
  @State locationData: LocationData = {}
  @State isTracking: boolean = false
  private locationRequest: geolocation.LocationRequest = {}

  build() {
    Column({ space: 20 }) {
      Text('地理位置服务')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text(this.currentLocation)
        .fontSize(16)
        .textAlign(TextAlign.Center)
        .backgroundColor('#F0F8FF')
        .padding(15)
        .width('100%')
      
      if (this.locationData.latitude) {
        this.buildLocationDetails()
      }
      
      Row({ space: 15 }) {
        Button('获取当前位置')
          .onClick(() => this.getCurrentLocation())
        
        Button(this.isTracking ? '停止跟踪' : '开始跟踪')
          .onClick(() => this.toggleLocationTracking())
      }
    }
    .width('100%')
    .padding(20)
    .onDetach(() => {
      this.stopLocationTracking()
    })
  }

  @Builder
  buildLocationDetails() {
    Column({ space: 10 }) {
      Text(`纬度: ${this.locationData.latitude?.toFixed(6)}`)
        .fontSize(14)
      Text(`经度: ${this.locationData.longitude?.toFixed(6)}`)  
        .fontSize(14)
      Text(`精度: ${this.locationData.accuracy}米`)
        .fontSize(14)
      Text(`海拔: ${this.locationData.altitude || '未知'}米`)
        .fontSize(14)
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#FFF8E1')
  }

  // 获取单次位置
  async getCurrentLocation() {
    try {
      // 请求位置权限
      await this.requestLocationPermission()
      
      // 配置定位请求参数
      this.locationRequest = {
        priority: geolocation.LocationRequestPriority.FIRST_FIX, // 高精度
        scenario: geolocation.LocationRequestScenario.NAVIGATION, // 导航场景
        maxAccuracy: 10, // 最高精度10米
        timeoutMs: 30000 // 30秒超时
      }

      const location = await geolocation.getCurrentLocation(this.locationRequest)
      this.updateLocationDisplay(location)
      
    } catch (error) {
      console.error('获取位置失败:', error)
      this.currentLocation = `定位失败: ${error.message}`
    }
  }

  // 持续位置跟踪
  async toggleLocationTracking() {
    if (this.isTracking) {
      this.stopLocationTracking()
    } else {
      await this.startLocationTracking()
    }
    this.isTracking = !this.isTracking
  }

  async startLocationTracking() {
    try {
      await this.requestLocationPermission()
      
      this.locationRequest = {
        priority: geolocation.LocationRequestPriority.FIRST_FIX,
        scenario: geolocation.LocationRequestScenario.NAVIGATION,
        maxAccuracy: 10,
        timeoutMs: 30000
      }

      // 注册位置变化监听
      geolocation.on('locationChange', this.locationRequest, (location) => {
        this.updateLocationDisplay(location)
        console.log('位置更新:', location)
      })

      this.currentLocation = '位置跟踪已启动'
    } catch (error) {
      console.error('启动位置跟踪失败:', error)
    }
  }

  stopLocationTracking() {
    geolocation.off('locationChange')
    this.currentLocation = '位置跟踪已停止'
  }

  // 更新位置显示
  private updateLocationDisplay(location: geolocation.Location) {
    this.locationData = {
      latitude: location.latitude,
      longitude: location.longitude, 
      accuracy: location.accuracy,
      altitude: location.altitude
    }
    
    this.currentLocation = `位置: ${location.latitude.toFixed(4)}, ${location.longitude.toFixed(4)}`
    
    // 反向地理编码获取地址
    this.reverseGeocode(location.latitude, location.longitude)
  }

  // 反向地理编码
  async reverseGeocode(latitude: number, longitude: number) {
    try {
      const geoConvert = await geolocation.getGeoConvert()
      const addressInfo = await geoConvert.getAddressFromLocation({
        latitude: latitude,
        longitude: longitude
      })
      
      if (addressInfo && addressInfo.length > 0) {
        const address = addressInfo[0]
        this.currentLocation = `${address.locale} ${address.placeName}`
      }
    } catch (error) {
      console.error('反向地理编码失败:', error)
    }
  }

  // 请求位置权限
  private async requestLocationPermission(): Promise<void> {
    try {
      const permissions: Array<Permissions> = [
        'ohos.permission.LOCATION',
        'ohos.permission.APPROXIMATELY_LOCATION'
      ]
      
      await accessControl.verifyAccess(permissions)
    } catch (error) {
      throw new Error('位置权限获取失败')
    }
  }
}

interface LocationData {
  latitude?: number
  longitude?: number  
  accuracy?: number
  altitude?: number
}

二、传感器数据获取

2.1 传感器系统概述

HarmonyOS提供了丰富的传感器API,可以访问设备的各种物理传感器数据。

常用传感器类型

  • 加速度计:检测设备加速度和倾斜
  • 陀螺仪:检测设备旋转角度
  • 磁力计:检测磁场方向(电子罗盘)
  • 光线传感器:检测环境光照强度
  • 距离传感器:检测物体接近程度

2.2 传感器数据监控实战

import sensor from '@ohos.sensor'

@Component
struct SensorMonitorExample {
  @State accelerometerData: SensorData = {}
  @State gyroscopeData: SensorData = {}
  @State lightLevel: number = 0
  @State isMonitoring: boolean = false

  build() {
    Column({ space: 20 }) {
      Text('传感器监控')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      this.buildSensorDisplay()
      
      Button(this.isMonitoring ? '停止监控' : '开始监控')
        .onClick(() => this.toggleSensorMonitoring())
        .width(200)
    }
    .width('100%')
    .padding(20)
    .onDetach(() => {
      this.stopAllSensors()
    })
  }

  @Builder
  buildSensorDisplay() {
    Column({ space: 15 }) {
      if (this.accelerometerData.x !== undefined) {
        Text('加速度传感器:')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        Text(`X: ${this.accelerometerData.x.toFixed(2)}`)
        Text(`Y: ${this.accelerometerData.y.toFixed(2)}`)  
        Text(`Z: ${this.accelerometerData.z.toFixed(2)}`)
      }
      
      if (this.gyroscopeData.x !== undefined) {
        Text('陀螺仪传感器:')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        Text(`X: ${this.gyroscopeData.x.toFixed(2)} rad/s`)
        Text(`Y: ${this.gyroscopeData.y.toFixed(2)} rad/s`)
        Text(`Z: ${this.gyroscopeData.z.toFixed(2)} rad/s`)
      }
      
      if (this.lightLevel > 0) {
        Text('环境光线:')
          .fontSize(16)
          .fontWeight(FontWeight.Medium)
        Text(`${this.lightLevel} lux`)
        this.buildLightIndicator()
      }
    }
    .width('100%')
  }

  @Builder
  buildLightIndicator() {
    const intensity = Math.min(this.lightLevel / 1000, 1) // 标准化到0-1
    const color = `rgb(${255 * intensity}, ${255 * intensity}, 100)`
    
    Row({ space: 10 }) {
      Text('●')
        .fontSize(24)
        .fontColor(color)
      Progress({ value: this.lightLevel, total: 1000 })
        .width(150)
    }
  }

  // 切换传感器监控状态
  async toggleSensorMonitoring() {
    if (this.isMonitoring) {
      this.stopAllSensors()
    } else {
      await this.startAllSensors()
    }
    this.isMonitoring = !this.isMonitoring
  }

  // 启动所有传感器监控
  async startAllSensors() {
    try {
      // 加速度计
      sensor.on(sensor.SensorId.ACCELEROMETER, (data) => {
        this.accelerometerData = {
          x: data.x,
          y: data.y, 
          z: data.z
        }
      })
      
      // 陀螺仪  
      sensor.on(sensor.SensorId.GYROSCOPE, (data) => {
        this.gyroscopeData = {
          x: data.x,
          y: data.y,
          z: data.z
        }
      })
      
      // 光线传感器
      sensor.on(sensor.SensorId.AMBIENT_LIGHT, (data) => {
        this.lightLevel = data.intensity
      })
      
      console.log('所有传感器监控已启动')
    } catch (error) {
      console.error('启动传感器监控失败:', error)
    }
  }

  // 停止所有传感器监控
  stopAllSensors() {
    sensor.off(sensor.SensorId.ACCELEROMETER)
    sensor.off(sensor.SensorId.GYROSCOPE) 
    sensor.off(sensor.SensorId.AMBIENT_LIGHT)
    console.log('所有传感器监控已停止')
  }
}

interface SensorData {
  x?: number
  y?: number
  z?: number
}

2.3 传感器应用案例:计步器

@Component
struct PedometerExample {
  @State stepCount: number = 0
  @State isCounting: boolean = false
  private lastAcceleration: number = 0
  private stepThreshold: number = 2.0 // 步数检测阈值

  build() {
    Column({ space: 20 }) {
      Text('计步器')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text(`步数: ${this.stepCount}`)
        .fontSize(32)
        .fontColor('#007DFF')
      
      Button(this.isCounting ? '停止计步' : '开始计步')
        .onClick(() => this.toggleStepCounting())
        .width(200)
      
      Text('手持设备正常行走即可计数')
        .fontSize(12)
        .fontColor('#666')
    }
    .width('100%')
    .padding(20)
  }

  toggleStepCounting() {
    if (this.isCounting) {
      this.stopStepCounting()
    } else {
      this.startStepCounting()
    }
    this.isCounting = !this.isCounting
  }

  startStepCounting() {
    sensor.on(sensor.SensorId.ACCELEROMETER, (data) => {
      this.detectStep(data)
    })
    this.stepCount = 0
  }

  stopStepCounting() {
    sensor.off(sensor.SensorId.ACCELEROMETER)
  }

  // 简单的步数检测算法
  private detectStep(accelData: sensor.AccelerometerResponse) {
    // 计算加速度矢量幅度
    const acceleration = Math.sqrt(
      accelData.x * accelData.x + 
      accelData.y * accelData.y + 
      accelData.z * accelData.z
    )
    
    // 检测峰值(简单的步数检测逻辑)
    if (this.lastAcceleration > 0) {
      const delta = acceleration - this.lastAcceleration
      
      if (delta > this.stepThreshold) {
        this.stepCount++
        console.log(`检测到步伐,总步数: ${this.stepCount}`)
      }
    }
    
    this.lastAcceleration = acceleration
  }
}

三、相机调用与图像处理

3.1 相机权限配置

module.json5中配置相机权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.CAMERA",
        "reason": "需要调用相机进行拍照和录像",
        "usedScene": {
          "abilities": ["MainAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.MICROPHONE", 
        "reason": "需要录制视频时采集音频",
        "usedScene": {
          "abilities": ["MainAbility"],
          "when": "always"
        }
      },
      {
        "name": "ohos.permission.WRITE_MEDIA",
        "reason": "需要保存照片和视频到相册",
        "usedScene": {
          "abilities": ["MainAbility"], 
          "when": "always"
        }
      }
    ]
  }
}

3.2 相机调用实战

import camera from '@ohos.multimedia.camera'
import image from '@ohos.multimedia.image'

@Component
struct CameraExample {
  @State previewUrl: string = ''
  @State capturedImage: string = ''
  @State cameraStatus: string = '准备中...'
  @State flashMode: string = 'off'
  
  private cameraManager: camera.CameraManager | null = null
  private cameraInput: camera.CameraInput | null = null
  private previewOutput: camera.PreviewOutput | null = null
  private photoOutput: camera.PhotoOutput | null = null

  build() {
    Column({ space: 15 }) {
      Text('相机应用')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Text(this.cameraStatus)
        .fontSize(14)
        .fontColor('#666')
      
      if (this.previewUrl) {
        Image(this.previewUrl)
          .width('100%')
          .height(300)
          .objectFit(ImageFit.Cover)
      } else {
        Rectangle()
          .width('100%')
          .height(300)
          .backgroundColor('#000')
      }
      
      Row({ space: 10 }) {
        Button('拍照')
          .onClick(() => this.takePhoto())
        
        Button(`闪光灯: ${this.flashMode}`)
          .onClick(() => this.toggleFlash())
        
        Button('切换相机')
          .onClick(() => this.switchCamera())
      }
      
      if (this.capturedImage) {
        Image(this.capturedImage)
          .width(150)
          .height(150)
          .margin({ top: 10 })
      }
    }
    .width('100%')
    .padding(20)
    .onAppear(() => {
      this.initCamera()
    })
    .onDetach(() => {
      this.releaseCamera()
    })
  }

  // 初始化相机
  async initCamera() {
    try {
      this.cameraStatus = '初始化相机...'
      
      // 获取相机管理器
      this.cameraManager = camera.getCameraManager(this.context)
      
      // 获取相机列表
      const cameras = this.cameraManager.getSupportedCameras()
      if (cameras.length === 0) {
        throw new Error('未找到可用相机')
      }
      
      // 使用后置相机
      const cameraDevice = cameras.find(cam => cam.position === camera.CameraPosition.CAMERA_POSITION_BACK) || cameras[0]
      
      // 创建相机输入
      this.cameraInput = this.cameraManager.createCameraInput(cameraDevice)
      await this.cameraInput.open()
      
      // 创建预览输出
      this.previewOutput = this.cameraManager.createPreviewOutput()
      this.previewUrl = this.previewOutput.getSurfaceId()
      
      // 创建照片输出
      this.photoOutput = this.cameraManager.createPhotoOutput()
      
      // 创建会话并启动预览
      const session = this.cameraManager.createCaptureSession()
      await session.beginConfig()
      await session.addInput(this.cameraInput)
      await session.addOutput(this.previewOutput)
      await session.addOutput(this.photoOutput)
      await session.commitConfig()
      await session.start()
      
      this.cameraStatus = '相机就绪'
      
    } catch (error) {
      console.error('相机初始化失败:', error)
      this.cameraStatus = `相机初始化失败: ${error.message}`
    }
  }

  // 拍照
  async takePhoto() {
    if (!this.photoOutput) {
      console.error('照片输出未就绪')
      return
    }
    
    try {
      this.cameraStatus = '拍照中...'
      
      const photoSettings: camera.PhotoSettings = {
        quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
        rotation: camera.ImageRotation.ROTATION_0
      }
      
      await this.photoOutput.capture(photoSettings)
      
      // 监听照片捕获完成
      this.photoOutput.on('photoAvailable', (err, photo) => {
        if (err) {
          console.error('拍照失败:', err)
          return
        }
        
        this.processCapturedPhoto(photo)
      })
      
    } catch (error) {
      console.error('拍照异常:', error)
      this.cameraStatus = `拍照失败: ${error.message}`
    }
  }

  // 处理捕获的照片
  private async processCapturedPhoto(photo: camera.Photo) {
    try {
      // 将照片保存到应用沙箱
      const filePath = this.context.filesDir + `/photo_${Date.now()}.jpg`
      await photo.save(filePath)
      
      this.capturedImage = filePath
      this.cameraStatus = '拍照完成'
      
      console.log('照片已保存:', filePath)
      
    } catch (error) {
      console.error('照片处理失败:', error)
    }
  }

  // 切换闪光灯模式
  async toggleFlash() {
    if (!this.cameraInput) return
    
    const modes = ['off', 'auto', 'on']
    const currentIndex = modes.indexOf(this.flashMode)
    this.flashMode = modes[(currentIndex + 1) % modes.length]
    
    try {
      await this.cameraInput.setFlashMode(this.flashMode as camera.FlashMode)
    } catch (error) {
      console.error('设置闪光灯失败:', error)
    }
  }

  // 切换前后相机
  async switchCamera() {
    await this.releaseCamera()
    await this.initCamera()
  }

  // 释放相机资源
  async releaseCamera() {
    try {
      if (this.cameraInput) {
        await this.cameraInput.close()
      }
      this.cameraStatus = '相机已释放'
    } catch (error) {
      console.error('释放相机资源失败:', error)
    }
  }
}

3.3 图像处理与滤镜应用

@Component
struct ImageProcessorExample {
  @State originalImage: string = ''
  @State processedImage: string = ''
  @State currentFilter: string = 'none'

  build() {
    Column({ space: 20 }) {
      Text('图像处理器')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      if (this.originalImage) {
        Image(this.originalImage)
          .width(200)
          .height(200)
          .margin({ bottom: 10 })
      }
      
      if (this.processedImage && this.currentFilter !== 'none') {
        Image(this.processedImage)
          .width(200)
          .height(200)
        Text(`应用滤镜: ${this.currentFilter}`)
          .fontSize(14)
          .fontColor('#666')
      }
      
      Row({ space: 10 }) {
        Button('选择图片')
          .onClick(() => this.pickImage())
        
        Button('灰度滤镜')
          .onClick(() => this.applyGrayFilter())
        
        Button('复古滤镜')
          .onClick(() => this.applyVintageFilter())
      }
    }
    .width('100%')
    .padding(20)
  }

  // 选择图片(简化实现)
  async pickImage() {
    // 实际应用中应该使用系统相册选择器
    // 这里使用模拟图片路径
    this.originalImage = '/resources/base/media/sample_image.jpg'
    this.processedImage = ''
    this.currentFilter = 'none'
  }

  // 应用灰度滤镜
  async applyGrayFilter() {
    if (!this.originalImage) return
    
    try {
      this.currentFilter = 'grayscale'
      // 实际图像处理应该使用图像处理API
      // 这里使用模拟处理结果
      this.processedImage = this.originalImage + '?filter=grayscale'
    } catch (error) {
      console.error('应用灰度滤镜失败:', error)
    }
  }

  // 应用复古滤镜
  async applyVintageFilter() {
    if (!this.originalImage) return
    
    try {
      this.currentFilter = 'vintage'
      // 模拟复古滤镜处理
      this.processedImage = this.originalImage + '?filter=vintage'
    } catch (error) {
      console.error('应用复古滤镜失败:', error)
    }
  }
}

四、设备能力综合应用案例

4.1 智能拍照应用

结合地理位置和传感器数据,创建智能拍照应用:

@Component
struct SmartCameraApp {
  @State photoInfo: PhotoMetadata = {}
  @State currentLocation: string = ''
  @State cameraOrientation: string = ''

  build() {
    Column({ space: 15 }) {
      Text('智能相机')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      CameraExample()
      
      if (this.photoInfo.location) {
        Text(`拍摄地点: ${this.photoInfo.location}`)
          .fontSize(12)
      }
      
      if (this.photoInfo.orientation) {
        Text(`拍摄方向: ${this.photoInfo.orientation}`)
          .fontSize(12)
      }
      
      if (this.photoInfo.timestamp) {
        Text(`拍摄时间: ${this.photoInfo.timestamp}`)
          .fontSize(12)
      }
    }
  }

  // 增强的拍照方法
  async takeSmartPhoto() {
    // 获取地理位置
    const location = await this.getCurrentLocation()
    
    // 获取设备方向
    const orientation = await this.getDeviceOrientation()
    
    // 设置照片元数据
    this.photoInfo = {
      location: location,
      orientation: orientation,
      timestamp: new Date().toLocaleString()
    }
    
    // 执行拍照
    await this.takePhoto()
  }

  private async getCurrentLocation(): Promise<string> {
    try {
      const location = await geolocation.getCurrentLocation({
        priority: geolocation.LocationRequestPriority.FIRST_FIX
      })
      
      return `${location.latitude.toFixed(4)}, ${location.longitude.toFixed(4)}`
    } catch (error) {
      return '位置未知'
    }
  }

  private async getDeviceOrientation(): Promise<string> {
    return new Promise((resolve) => {
      sensor.once(sensor.SensorId.ACCELEROMETER, (data) => {
        const orientation = this.calculateOrientation(data)
        resolve(orientation)
      })
    })
  }

  private calculateOrientation(accelData: sensor.AccelerometerResponse): string {
    const { x, y, z } = accelData
    
    if (Math.abs(z) > Math.abs(x) && Math.abs(z) > Math.abs(y)) {
      return z > 0 ? '正面朝上' : '正面朝下'
    } else if (Math.abs(x) > Math.abs(y)) {
      return x > 0 ? '左侧朝上' : '右侧朝上'
    } else {
      return y > 0 ? '底部朝上' : '顶部朝上'
    }
  }
}

interface PhotoMetadata {
  location?: string
  orientation?: string
  timestamp?: string
}

总结

通过本文的深入学习,您应该掌握了HarmonyOS设备能力调用的核心技能:

  1. 地理位置服务:精确定位、地址解析和地理围栏
  2. 传感器数据:加速度计、陀螺仪、光线传感器等硬件数据获取
  3. 相机调用:拍照、录像、闪光灯控制和图像处理
  4. 综合应用:结合多种设备能力创建智能应用

需要参加鸿蒙认证的请点击 鸿蒙认证链接

posted @ 2025-10-30 10:57  ifeng918  阅读(13)  评论(0)    收藏  举报