【鸿蒙开发实战】HarmonyOS简易图片浏览器
2. **状态管理**:使用多个`@State`装饰器管理图片列表、当前图片、浏览模式和编辑状态。- **图片信息**:显示图片的详细信息(大小、分辨率、拍摄时间等)1. **初始化阶段**:应用启动时,加载模拟的图片数据和用户设置。- **图片浏览**:支持图片的缩放、平移、旋转等手势操作。- **幻灯片播放**:自动轮播图片,支持播放控制。- 切换图片 → 滑动切换 → 更新当前图片。- **
代码功能概述
实现了一个功能完整的鸿蒙图片浏览器应用,全面展示了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 面向对象核心的类和对象、继承多态,到吃透鸿蒙开发关键技能,还能冲刺鸿蒙基础 +高级开发者证书,更惊喜的是考证成功还送好礼!快加入我的鸿蒙班,一起从入门到精通,班级链接:点击免费进入
更多推荐



所有评论(0)