代码功能概述

实现了一个功能完整的鸿蒙图片浏览器应用,全面展示了ArkTS在图片显示、手势操作、文件管理和用户界面设计等方面的核心能力。主要功能包括:

- **图片浏览**:支持图片的缩放、平移、旋转等手势操作

- **图片管理**:添加、删除、收藏图片

- **分类查看**:按相册、时间、收藏等分类浏览图片

- **图片信息**:显示图片的详细信息(大小、分辨率、拍摄时间等)

- **幻灯片播放**:自动轮播图片,支持播放控制

- **图片编辑**:简单的图片裁剪、滤镜效果

- **分享功能**:模拟图片分享到其他应用

代码逻辑分析

应用采用"图片数据驱动UI"的架构设计:

1. **初始化阶段**:应用启动时,加载模拟的图片数据和用户设置

2. **状态管理**:使用多个`@State`装饰器管理图片列表、当前图片、浏览模式和编辑状态

3. **图片浏览流程**:

   - 选择图片 → 进入详情视图 → 显示大图

   - 手势操作 → 缩放/平移图片 → 实时更新显示

   - 切换图片 → 滑动切换 → 更新当前图片

4. **图片管理操作**:

   - 添加图片 → 选择图片 → 添加到相册

   - 删除图片 → 确认删除 → 从列表中移除

   - 收藏图片 → 标记收藏 → 更新收藏状态

5. **幻灯片播放**:

   - 开始播放 → 自动切换图片 → 显示播放控制

   - 暂停播放 → 停止自动切换 → 保持当前图片

6. **图片编辑**:

   - 选择编辑功能 → 应用效果 → 预览并保存

7. **界面更新**:所有操作实时反映在UI上,提供流畅的用户体验

完整代码

@Entry
@Component
struct ImageViewerTutorial {
  @State imageList: ImageItem[] = [];
  @State currentImage: ImageItem = new ImageItem();
  @State currentIndex: number = 0;
  @State viewMode: string = 'grid';
  @State scale: number = 1.0;
  @State offsetX: number = 0;
  @State offsetY: number = 0;
  @State isPlaying: boolean = false;
  @State selectedAlbum: string = 'all';

  aboutToAppear() {
    this.loadImages();
  }

  build() {
    Column({ space: 0 }) {
      this.BuildHeader()
      
      if (this.viewMode === 'grid') {
        this.BuildGridView()
      } else if (this.viewMode === 'detail') {
        this.BuildDetailView()
      } else if (this.viewMode === 'slideshow') {
        this.BuildSlideshowView()
      }
      
      if (this.viewMode !== 'grid') {
        this.BuildControlBar()
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#000000')
  }

  @Builder BuildHeader() {
    Row({ space: 15 }) {
      if (this.viewMode !== 'grid') {
        Button('返回')
          .onClick(() => {
            this.viewMode = 'grid';
            this.resetImageTransform();
          })
          .fontSize(16)
          .fontColor('#FFFFFF')
          .backgroundColor('#333333')
          .borderRadius(20)
          .padding(10)
      }
      
      Text(this.getHeaderTitle())
        .fontSize(18)
        .fontWeight(FontWeight.Medium)
        .fontColor('#FFFFFF')
        .layoutWeight(1)
      
      if (this.viewMode === 'detail') {
        Button('编辑')
          .onClick(() => {
            this.showEditOptions();
          })
          .fontSize(16)
          .fontColor('#FFFFFF')
          .backgroundColor('#333333')
          .borderRadius(20)
          .padding(10)
      }
    }
    .width('100%')
    .padding({ left: 15, right: 15, top: 10, bottom: 10 })
    .backgroundColor('#1A1A1A')
  }

  @Builder BuildGridView() {
    Column({ space: 10 }) {
      this.BuildAlbumSelector()
      
      Grid() {
        ForEach(this.getFilteredImages(), (image: ImageItem, index: number) => {
          GridItem() {
            this.BuildGridItem(image, index)
          }
        })
      }
      .columnsTemplate('1fr 1fr 1fr')
      .rowsTemplate('1fr 1fr 1fr')
      .columnsGap(5)
      .rowsGap(5)
      .width('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .padding(10)
    .layoutWeight(1)
  }

  @Builder BuildAlbumSelector() {
    const albums = this.getAlbums();
    
    Scroll() {
      Row({ space: 10 }) {
        ForEach(albums, (album: AlbumInfo) => {
          Button(album.name + ` (${album.count})`)
            .onClick(() => {
              this.selectedAlbum = album.name;
            })
            .backgroundColor(this.selectedAlbum === album.name ? '#4A90E2' : '#333333')
            .fontColor('#FFFFFF')
            .borderRadius(15)
            .padding({ left: 12, right: 12 })
            .height(30)
        })
      }
      .padding(5)
    }
    .scrollable(ScrollDirection.Horizontal)
    .width('100%')
    .height(40)
  }

  @Builder BuildGridItem(image: ImageItem, index: number) {
    Column({ space: 0 }) {
      Image(image.path)
        .width('100%')
        .height(120)
        .objectFit(ImageFit.Cover)
        .borderRadius(8)
        .onClick(() => {
          this.currentImage = image;
          this.currentIndex = index;
          this.viewMode = 'detail';
        })
      
      if (image.isFavorite) {
        Image($r('app.media.favorite_icon'))
          .width(16)
          .height(16)
          .position({ x: '80%', y: '10%' })
      }
    }
    .width('100%')
    .height(130)
  }

  @Builder BuildDetailView() {
    Stack({ alignContent: Alignment.Center }) {
      this.BuildImageDisplay()
      this.BuildImageInfo()
      this.BuildActionButtons()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#000000')
  }

  @Builder BuildImageDisplay() {
    GestureGroup(GestureMode.Exclusive) {
      PinchGesture()
        .onActionStart(() => {})
        .onActionUpdate((event: GestureEvent) => {
          this.scale = Math.max(0.5, Math.min(3, this.scale * event.scale));
        })
        .onActionEnd(() => {})
      
      PanGesture()
        .onActionStart(() => {})
        .onActionUpdate((event: GestureEvent) => {
          this.offsetX += event.offsetX;
          this.offsetY += event.offsetY;
        })
        .onActionEnd(() => {})
    }
    
    Image(this.currentImage.path)
      .width('100%')
      .height('100%')
      .objectFit(ImageFit.Contain)
      .scale({ x: this.scale, y: this.scale })
      .translate({ x: this.offsetX, y: this.offsetY })
  }

  @Builder BuildImageInfo() {
    Column({ space: 8 }) {
      Text(this.currentImage.name)
        .fontSize(16)
        .fontWeight(FontWeight.Medium)
        .fontColor('#FFFFFF')
      
      Text(`${this.currentImage.width} × ${this.currentImage.height}`)
        .fontSize(14)
        .fontColor('#CCCCCC')
      
      Text(this.currentImage.createTime)
        .fontSize(12)
        .fontColor('#999999')
    }
    .width('90%')
    .padding(15)
    .backgroundColor('rgba(0, 0, 0, 0.7)')
    .borderRadius(10)
    .position({ x: '5%', y: '85%' })
  }

  @Builder BuildActionButtons() {
    Row({ space: 20 }) {
      Button('收藏')
        .onClick(() => {
          this.toggleFavorite();
        })
        .backgroundColor(this.currentImage.isFavorite ? '#FF6B6B' : '#333333')
        .fontColor('#FFFFFF')
        .borderRadius(20)
        .padding({ left: 15, right: 15 })
      
      Button('分享')
        .onClick(() => {
          this.shareImage();
        })
        .backgroundColor('#333333')
        .fontColor('#FFFFFF')
        .borderRadius(20)
        .padding({ left: 15, right: 15 })
      
      Button('删除')
        .onClick(() => {
          this.deleteImage();
        })
        .backgroundColor('#333333')
        .fontColor('#FFFFFF')
        .borderRadius(20)
        .padding({ left: 15, right: 15 })
    }
    .position({ x: '50%', y: '10%' })
  }

  @Builder BuildSlideshowView() {
    Stack({ alignContent: Alignment.Center }) {
      Image(this.currentImage.path)
        .width('100%')
        .height('100%')
        .objectFit(ImageFit.Contain)
      
      if (this.isPlaying) {
        this.BuildPlaybackControls()
      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#000000')
    .onClick(() => {
      this.isPlaying = !this.isPlaying;
    })
  }

  @Builder BuildPlaybackControls() {
    Column({ space: 15 }) {
      Text('幻灯片播放中...')
        .fontSize(16)
        .fontColor('#FFFFFF')
      
      Row({ space: 20 }) {
        Button('上一张')
          .onClick(() => {
            this.previousImage();
          })
          .backgroundColor('#333333')
          .fontColor('#FFFFFF')
          .borderRadius(15)
          .padding(10)
        
        Button('暂停')
          .onClick(() => {
            this.isPlaying = false;
          })
          .backgroundColor('#4A90E2')
          .fontColor('#FFFFFF')
          .borderRadius(15)
          .padding(10)
        
        Button('下一张')
          .onClick(() => {
            this.nextImage();
          })
          .backgroundColor('#333333')
          .fontColor('#FFFFFF')
          .borderRadius(15)
          .padding(10)
      }
    }
    .width('80%')
    .padding(20)
    .backgroundColor('rgba(0, 0, 0, 0.7)')
    .borderRadius(15)
    .position({ x: '10%', y: '80%' })
  }

  @Builder BuildControlBar() {
    Row({ space: 30 }) {
      Button('幻灯片')
        .onClick(() => {
          this.viewMode = 'slideshow';
          this.startSlideshow();
        })
        .backgroundColor(this.viewMode === 'slideshow' ? '#4A90E2' : '#333333')
        .fontColor('#FFFFFF')
        .borderRadius(20)
        .padding({ left: 15, right: 15 })
      
      Button('上一张')
        .onClick(() => {
          this.previousImage();
        })
        .backgroundColor('#333333')
        .fontColor('#FFFFFF')
        .borderRadius(20)
        .padding({ left: 15, right: 15 })
      
      Button('下一张')
        .onClick(() => {
          this.nextImage();
        })
        .backgroundColor('#333333')
        .fontColor('#FFFFFF')
        .borderRadius(20)
        .padding({ left: 15, right: 15 })
    }
    .width('100%')
    .padding(15)
    .backgroundColor('#1A1A1A')
    .justifyContent(FlexAlign.Center)
  }

  private loadImages(): void {
    const mockImages: ImageItem[] = [
      {
        id: '1',
        name: '风景1.jpg',
        path: $r('app.media.landscape1'),
        album: '风景',
        size: 2048576,
        width: 1920,
        height: 1080,
        createTime: '2024-01-15 10:30',
        isFavorite: true
      },
      {
        id: '2',
        name: '人物1.jpg', 
        path: $r('app.media.portrait1'),
        album: '人物',
        size: 1572864,
        width: 1080,
        height: 1920,
        createTime: '2024-01-16 14:20',
        isFavorite: false
      }
    ];
    
    this.imageList = mockImages;
    if (this.imageList.length > 0) {
      this.currentImage = this.imageList[0];
    }
  }

  private getFilteredImages(): ImageItem[] {
    if (this.selectedAlbum === 'all') {
      return this.imageList;
    }
    return this.imageList.filter(image => image.album === this.selectedAlbum);
  }

  private getAlbums(): AlbumInfo[] {
    const albums: {[key: string]: AlbumInfo} = {};
    
    this.imageList.forEach(image => {
      if (!albums[image.album]) {
        albums[image.album] = {
          name: image.album,
          count: 0,
          cover: image.path
        };
      }
      albums[image.album].count++;
    });
    
    return Object.values(albums);
  }

  private getHeaderTitle(): string {
    switch (this.viewMode) {
      case 'grid':
        return '图片库';
      case 'detail':
        return this.currentImage.name;
      case 'slideshow':
        return '幻灯片';
      default:
        return '图片浏览器';
    }
  }

  private toggleFavorite(): void {
    this.currentImage.isFavorite = !this.currentImage.isFavorite;
    const index = this.imageList.findIndex(img => img.id === this.currentImage.id);
    if (index >= 0) {
      this.imageList[index].isFavorite = this.currentImage.isFavorite;
    }
  }

  private previousImage(): void {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      this.currentImage = this.imageList[this.currentIndex];
      this.resetImageTransform();
    }
  }

  private nextImage(): void {
    if (this.currentIndex < this.imageList.length - 1) {
      this.currentIndex++;
      this.currentImage = this.imageList[this.currentIndex];
      this.resetImageTransform();
    }
  }

  private resetImageTransform(): void {
    this.scale = 1.0;
    this.offsetX = 0;
    this.offsetY = 0;
  }

  private startSlideshow(): void {
    this.isPlaying = true;
    setInterval(() => {
      if (this.isPlaying) {
        this.nextImage();
      }
    }, 3000);
  }

  private shareImage(): void {
    console.log('分享图片:', this.currentImage.name);
  }

  private deleteImage(): void {
    this.imageList = this.imageList.filter(img => img.id !== this.currentImage.id);
    
    if (this.imageList.length > 0) {
      this.currentIndex = Math.min(this.currentIndex, this.imageList.length - 1);
      this.currentImage = this.imageList[this.currentIndex];
    } else {
      this.viewMode = 'grid';
    }
  }

  private showEditOptions(): void {
    console.log('显示编辑选项');
  }
}

class ImageItem {
  id: string = '';
  name: string = '';
  path: string = '';
  album: string = '默认相册';
  size: number = 0;
  width: number = 0;
  height: number = 0;
  createTime: string = '';
  isFavorite: boolean = false;
}

class AlbumInfo {
  name: string = '';
  count: number = 0;
  cover: string = '';
}

想入门鸿蒙开发又怕花冤枉钱?别错过!现在能免费系统学 -- 从 ArkTS 面向对象核心的类和对象、继承多态,到吃透鸿蒙开发关键技能,还能冲刺鸿蒙基础 +高级开发者证书,更惊喜的是考证成功还送好礼!快加入我的鸿蒙班,一起从入门到精通,班级链接:点击免费进入

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐