一、应用概述

1.1 应用简介

手电筒(Flashlight)是一款模拟手电筒功能的实用工具应用,支持屏幕灯光的开关控制、亮度调节、颜色切换、闪烁模式和SOS求救信号模式。该应用以HarmonyOS NEXT的ArkTS框架为基础,深入展示了定时器模式管理、颜色控制、状态切换、交互反馈、设备功能调用和动画效果等关键技术。

1.2 核心功能

功能模块 功能描述 技术实现 设计考量
开关控制 打开/关闭手电筒 状态切换 大按钮设计
亮度调节 亮度百分比控制 滑动条+CSS透明度 0-100%无级调节
颜色切换 8种灯光颜色 颜色选择器 覆盖常见色
闪烁模式 定时闪烁效果 定时器+状态切换 可调节频率
SOS模式 摩斯电码SOS求救 定时模式序列 国际标准
定时关闭 自动关闭计时器 倒计时 5/10/15/30分钟
使用统计 使用次数/时长 数据累加 续航参考
锁屏显示 锁屏界面的控制 快捷控制 快速使用

1.3 应用架构

手电筒应用采用分层架构:

  1. UI表现层:开关按钮、亮度滑块、颜色选择器、模式选择、定时器面板。
  2. 业务逻辑层:灯光管理器、模式控制器、定时器管理器、颜色管理器。
  3. 数据持久层:使用Preferences API保存使用统计和设置偏好。

二、灯光模式控制

2.1 灯光状态机

enum LightMode {
  OFF = 'off',           // 关闭
  ON = 'on',             // 常亮
  STROBE = 'strobe',     // 闪烁
  SOS = 'sos'            // SOS求救信号
}

interface LightState {
  isOn: boolean;
  mode: LightMode;
  brightness: number;     // 0-100
  color: string;          // HEX颜色
  strobeFrequency: number; // 闪烁频率(Hz)
  timerMinutes: number;   // 定时关闭分钟数(0=不关闭)
}

class LightController {
  @State isOn: boolean = false;
  @State mode: LightMode = LightMode.OFF;
  @State brightness: number = 100;
  @State color: string = '#FFFFFF';
  @State strobeFrequency: number = 5;
  @State timerMinutes: number = 0;
  
  private strobeTimerId: number | null = null;
  private sosTimerId: number | null = null;
  private countdownTimerId: number | null = null;
  private usageStartTime: number = 0;
  private totalUsageSeconds: number = 0;
  
  private static readonly SOS_PATTERN = [
    300, 300, 300, 300, 300, 900,  // S: ···
    900, 300, 900, 300, 900, 900,  // O: ---
    300, 300, 300, 300, 300, 900   // S: ···
  ];
  
  private static readonly COLORS = [
    { name: '白色', hex: '#FFFFFF', emoji: '⚪' },
    { name: '暖白', hex: '#FFF3E0', emoji: '🌅' },
    { name: '红色', hex: '#FF1744', emoji: '🔴' },
    { name: '绿色', hex: '#00E676', emoji: '🟢' },
    { name: '蓝色', hex: '#2979FF', emoji: '🔵' },
    { name: '黄色', hex: '#FFEA00', emoji: '🟡' },
    { name: '紫色', hex: '#D500F9', emoji: '🟣' },
    { name: '橙色', hex: '#FF9100', emoji: '🟠' },
  ];
  
  // 开关切换
  toggle(): void {
    if (this.isOn) {
      this.turnOff();
    } else {
      this.turnOn();
    }
  }
  
  turnOn(): void {
    this.isOn = true;
    this.usageStartTime = Date.now();
    
    if (this.mode === LightMode.STROBE) {
      this.startStrobe();
    } else if (this.mode === LightMode.SOS) {
      this.startSOS();
    }
    
    if (this.timerMinutes > 0) {
      this.startCountdown();
    }
  }
  
  turnOff(): void {
    this.isOn = false;
    this.totalUsageSeconds += (Date.now() - this.usageStartTime) / 1000;
    this.stopAllEffects();
  }
  
  // 闪烁模式
  startStrobe(): void {
    this.stopAllEffects();
    this.mode = LightMode.STROBE;
    this.isOn = true;
    
    const interval = 1000 / this.strobeFrequency;
    this.strobeTimerId = setInterval(() => {
      this.isOn = !this.isOn;
    }, interval);
  }
  
  // SOS求救信号
  startSOS(): void {
    this.stopAllEffects();
    this.mode = LightMode.SOS;
    this.isOn = true;
    
    let step = 0;
    this.sosTimerId = setInterval(() => {
      this.isOn = !this.isOn;
      step = (step + 1) % LightController.SOS_PATTERN.length;
    }, LightController.SOS_PATTERN[step]);
  }
  
  // 定时关闭
  startCountdown(): void {
    if (this.countdownTimerId !== null) {
      clearTimeout(this.countdownTimerId);
    }
    
    this.countdownTimerId = setTimeout(() => {
      this.turnOff();
    }, this.timerMinutes * 60 * 1000);
  }
  
  private stopAllEffects(): void {
    if (this.strobeTimerId !== null) {
      clearInterval(this.strobeTimerId);
      this.strobeTimerId = null;
    }
    if (this.sosTimerId !== null) {
      clearInterval(this.sosTimerId);
      this.sosTimerId = null;
    }
    if (this.countdownTimerId !== null) {
      clearTimeout(this.countdownTimerId);
      this.countdownTimerId = null;
    }
    this.mode = LightMode.OFF;
  }
  
  setColor(color: string): void {
    this.color = color;
  }
  
  setBrightness(value: number): void {
    this.brightness = Math.max(0, Math.min(100, value));
  }
  
  setStrobeFrequency(freq: number): void {
    this.strobeFrequency = freq;
    if (this.mode === LightMode.STROBE && this.isOn) {
      this.startStrobe();
    }
  }
  
  getUsageTime(): string {
    const totalSeconds = this.totalUsageSeconds + 
      (this.isOn ? (Date.now() - this.usageStartTime) / 1000 : 0);
    const hours = Math.floor(totalSeconds / 3600);
    const minutes = Math.floor((totalSeconds % 3600) / 60);
    if (hours > 0) return `${hours}小时${minutes}分钟`;
    return `${minutes}分钟`;
  }
  
  static getColors(): { name: string; hex: string; emoji: string }[] {
    return [...LightController.COLORS];
  }
}

三、UI交互设计

3.1 手电筒主界面

@Component
struct FlashlightView {
  @State isOn: boolean = false;
  @State mode: LightMode = LightMode.OFF;
  @State brightness: number = 100;
  @State color: string = '#FFFFFF';
  @State timerMinutes: number = 0;
  @State remainingTime: number = 0;
  
  private controller = new LightController();
  
  build() {
    Stack() {
      // 背景(根据灯光颜色和亮度变化)
      Column()
        .width('100%')
        .height('100%')
        .backgroundColor(this.getBackgroundColor())
      
      Column() {
        // 顶部状态
        Text(this.getStatusText())
          .fontSize(16)
          .fontColor('#FFFFFF')
          .margin({ top: 48 })
        
        Spacer()
        
        // 主开关按钮
        Button()
          .width(160)
          .height(160)
          .backgroundColor(this.isOn ? '#FFFFFF' : '#333333')
          .borderRadius(80)
          .shadow({ radius: 30, color: this.isOn ? '#80FFFFFF' : '#40000000' })
          .onClick(() => {
            this.controller.toggle();
            this.isOn = this.controller.isOn;
            this.mode = this.controller.mode;
          })
        
        // 图标
        Text(this.isOn ? '🔦' : '🔘')
          .fontSize(48)
          .position({ y: -60 })
        
        Spacer()
        
        // 控制面板
        if (this.isOn) {
          this.renderControlPanel()
        }
      }
      .width('100%')
      .height('100%')
    }
    .width('100%')
    .height('100%')
  }
  
  @Builder
  private renderControlPanel() {
    Column() {
      // 亮度控制
      Row() {
        Text('☀️')
          .fontSize(16)
        Slider({ value: this.brightness, min: 0, max: 100, step: 1 })
          .width('70%')
          .onChange((v) => {
            this.brightness = v;
            this.controller.setBrightness(v);
          })
        Text('☀️')
          .fontSize(24)
      }
      .width('90%')
      .padding(16)
      .backgroundColor('#80000000')
      .borderRadius(16)
      
      // 颜色选择
      Row({ space: 8 }) {
        ForEach(LightController.getColors(), (c: { name: string; hex: string; emoji: string }) => {
          Circle()
            .width(36)
            .height(36)
            .fill(c.hex)
            .stroke(this.color === c.hex ? '#FFFFFF' : 'transparent')
            .strokeWidth(3)
            .onClick(() => {
              this.color = c.hex;
              this.controller.setColor(c.hex);
            })
        })
      }
      .padding(12)
      .backgroundColor('#80000000')
      .borderRadius(16)
      .margin({ top: 8 })
      
      // 模式按钮
      Row({ space: 16 }) {
        this.createModeButton('常亮', LightMode.ON)
        this.createModeButton('闪烁', LightMode.STROBE)
        this.createModeButton('SOS', LightMode.SOS)
      }
      .padding(12)
      .backgroundColor('#80000000')
      .borderRadius(16)
      .margin({ top: 8 })
      
      // 定时关闭
      Row({ space: 8 }) {
        ForEach([{ label: '5分', value: 5 }, { label: '10分', value: 10 }, { label: '15分', value: 15 }, { label: '30分', value: 30 }, { label: '关闭', value: 0 }], 
          (item: { label: string; value: number }) => {
          Button(item.label)
            .fontSize(12)
            .padding({ left: 12, right: 12 })
            .backgroundColor(this.timerMinutes === item.value ? '#4CAF50' : '#666666')
            .borderRadius(12)
            .onClick(() => {
              this.timerMinutes = item.value;
              this.controller.timerMinutes = item.value;
            })
        })
      }
      .padding(12)
      .backgroundColor('#80000000')
      .borderRadius(16)
      .margin({ top: 8, bottom: 32 })
    }
    .width('100%')
  }
  
  @Builder
  private createModeButton(label: string, mode: LightMode) {
    Button(label)
      .fontSize(14)
      .padding({ left: 20, right: 20 })
      .backgroundColor(this.mode === mode ? '#4CAF50' : '#666666')
      .borderRadius(20)
      .onClick(() => {
        this.mode = mode;
        if (mode === LightMode.STROBE) {
          this.controller.startStrobe();
        } else if (mode === LightMode.SOS) {
          this.controller.startSOS();
        } else {
          this.controller.turnOn();
        }
      })
  }
  
  private getBackgroundColor(): string {
    if (!this.isOn) return '#1a1a1a';
    // 根据颜色和亮度计算背景色
    const hex = this.color;
    const r = parseInt(hex.slice(1, 3), 16);
    const g = parseInt(hex.slice(3, 5), 16);
    const b = parseInt(hex.slice(5, 7), 16);
    const factor = this.brightness / 100;
    return `rgb(${Math.round(r * factor)}, ${Math.round(g * factor)}, ${Math.round(b * factor)})`;
  }
  
  private getStatusText(): string {
    if (!this.isOn) return '点击开关打开手电筒';
    if (this.mode === LightMode.STROBE) return '闪烁模式';
    if (this.mode === LightMode.SOS) return 'SOS求救信号';
    if (this.timerMinutes > 0) return `${this.timerMinutes}分钟后自动关闭`;
    return '手电筒已开启';
  }
}

四、总结

4.1 核心技术要点

  1. 灯光状态机:OFF/ON/STROBE/SOS四种模式完整转换,支持模式切换。
  2. 闪烁模式控制:基于setInterval的可调节频率闪烁,支持1-10Hz调节。
  3. SOS求救信号:国际标准SOS摩斯电码序列(···—···)的精确时序控制。
  4. 颜色管理系统:8种预设颜色,支持HEX颜色切换。
  5. 亮度控制:0-100%无级亮度调节,通过CSS背景色计算实现。
  6. 定时关闭:可设置5/10/15/30分钟自动关闭,省电设计。

4.2 扩展方向

  1. 相机闪光灯调用:调用设备相机闪光灯,获取更亮的照明效果。
  2. 屏幕闪烁模式:自定义闪烁模式和频率。
  3. 语音控制:通过语音命令控制手电筒开关。
  4. 手势控制:甩动手机开关手电筒。
  5. 快捷小组件:桌面小组件一键开关。

4.3 核心代码量统计

模块 核心代码行数 接口数 组件数
灯光控制器 160 8 -
模式控制器 80 5 -
UI组件 300 4 5
总计 540 17 5

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐