鸿蒙媒体开发【拼图】拍照和图片

拼图

介绍

该示例通过@ohos.multimedia.image和@ohos.file.photoAccessHelper接口实现获取图片,以及图片裁剪分割的功能。

效果预览

1

使用说明:

  1. 使用预置相机拍照后启动应用,应用首页会读取设备内的图片文件并展示获取到的第一个图片,没有图片时图片位置显示空白;
  2. 点击开始按钮开始后,时间开始倒计时,在规定时间内未完成拼图则游戏结束。在游戏中,玩家点击重新开始进行游戏重置;
  3. 点击开始游戏后,玩家可以根据上方的大图,点击黄格周围的图片移动,点击后图片和黄格交换位置,最终拼成完整的图片;
  4. 不在游戏中时,玩家可以点击上方大图,选择自定义图片来进行拼图游戏。

具体实现

  • 游戏中图片裁剪分割的效果实现在ImageModel中,源码参考[ImageModel]:
/** Copyright (c) 2022 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { dataSharePredicates } from '@kit.ArkData';
import { Context } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import Logger from './Logger';
import PictureItem from './PictureItem';
import { CommonConstants as Common } from '../common/CommonConstants';const TAG = '[ImageModel]';export default class ImageModel {private phAccessHelper: photoAccessHelper.PhotoAccessHelper | null = null;constructor(context: Context) {this.phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context);}async getAllImg(): Promise<photoAccessHelper.PhotoAsset[]> {Logger.info('getAllImg');let photoList: Array<photoAccessHelper.PhotoAsset> = [];if (this.phAccessHelper === null) {Logger.info('phAccessHelper fail');return photoList;}let fileKeyType = photoAccessHelper.PhotoKeys.PHOTO_TYPE;let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();Logger.info(fileKeyType);let fetchOptions: photoAccessHelper.FetchOptions = {fetchColumns: [],predicates: predicates};try {let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> =await this.phAccessHelper.getAssets(fetchOptions);if (fetchResult != undefined) {Logger.info('fetchResult success');let photoAsset: Array<photoAccessHelper.PhotoAsset> = await fetchResult.getAllObjects();if (photoAsset != undefined && photoAsset.length > 0) {for (let i = 0; i < photoAsset.length; i++) {if (photoAsset[i].photoType === 1) {photoList.push(photoAsset[i]);}}}}} catch (err) {Logger.error('getAssets failed, message = ', err);}Logger.info('photoList success');return photoList;}async splitPic(index: number): Promise<PictureItem[]> {let imagePixelMap: PictureItem[] = [];let imagesData: Array<photoAccessHelper.PhotoAsset> = await this.getAllImg();let imagePackerApi = image.createImagePacker();fileIo.open(imagesData[index].uri, fileIo.OpenMode.READ_ONLY).then(async (file: fileIo.File) => {let fd: number = file.fd;let imageSource = image.createImageSource(fd);let imageInfo = await imageSource.getImageInfo();Logger.info(TAG, `sizeImg createImageSource ${JSON.stringify(imageSource)}`);let height = imageInfo.size.height / Common.SPLIT_COUNT;for (let i = 0; i < Common.SPLIT_COUNT; i++) {for (let j = 0; j < Common.SPLIT_COUNT; j++) {let picItem: PictureItem;if (i === Common.SPLIT_COUNT - 1 && j === Common.SPLIT_COUNT - 1) {picItem = new PictureItem(Common.PICTURE_ITEM_NUMBER, {} as image.PixelMap);imagePixelMap.push(picItem);} else {Logger.info(TAG, `sizeImg x = ${imageInfo.size.width / Common.SPLIT_COUNT} y = ${height}`);let decodingOptions: image.DecodingOptions = {desiredRegion: {size: {height: height,width: imageInfo.size.width / Common.SPLIT_COUNT}, x: j * imageInfo.size.width / Common.SPLIT_COUNT, y: i * height}}imagePixelMap.push(new PictureItem(i * Common.SPLIT_COUNT + j, await imageSource.createPixelMap(decodingOptions)));}}}imagePackerApi.release();fileIo.closeSync(fd);})return imagePixelMap;}
}
  • 获取本地图片:首先使用getPhotoAccessHelper获取相册管理模块实例,然后使用getAssets方法获取文件资源,最后使用getAllObjects获取检索结果中的所有文件资产方便展示;
  • 裁剪图片准备:裁剪图片需要使用@ohos.multimedia.image接口,裁剪前需要申请图片编辑权限,使用requestPermissionsFromUser申请,源码参考[Index.ets];
/** Copyright (c) 2022-2023 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**     http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import { mediaquery } from '@kit.ArkUI';
import { Permissions, abilityAccessCtrl } from '@kit.AbilityKit';
import { emitter } from '@kit.BasicServicesKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import GameRules from '../model/GameRules';
import ImagePicker from '../common/ImagePicker';
import Logger from '../model/Logger';
import PictureItem from '../model/PictureItem';
import ImageModel from '../model/ImageModel';
import { CommonConstants as Common } from '../common/CommonConstants';const PERMISSIONS: Array<Permissions> = ['ohos.permission.READ_MEDIA','ohos.permission.WRITE_MEDIA','ohos.permission.MEDIA_LOCATION','ohos.permission.MANAGE_MISSIONS'
];@Entry
@Component
struct Index {@State numArray: PictureItem[] = [];@State imgData: Array<photoAccessHelper.PhotoAsset> = [];@State @Watch('onTimeOver') gameTime: number = Common.GAME_TIME;@State @Watch('onImageChange') index: number = Common.ZERO;@State isLand: boolean = false;@StorageLink('isGameStart') isGameStart: boolean = false;private listener = mediaquery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)');private ImageModel: ImageModel = new ImageModel(getContext(this));private game: GameRules = new GameRules();private timer: number = -1;private isRefresh: boolean = false;onLand = (mediaQueryResult: mediaquery.MediaQueryResult) => {Logger.info(`[eTSMediaQuery.Index]onLand: mediaQueryResult.matches=${mediaQueryResult.matches}`);this.isLand = mediaQueryResult.matches;};over = () => {Logger.info(Common.TAG, 'emitter on , eventID = 0');for (let i = Common.ZERO; i < Common.LOOP; i++) {this.numArray[i].index = i;}this.gameOver();};timeEnd = () => {Logger.info(Common.TAG, 'emitter on , eventID = 1');this.gameTime = Common.ZERO;};async aboutToAppear() {this.listener.on('change', this.onLand);await abilityAccessCtrl.createAtManager().requestPermissionsFromUser(getContext(this), PERMISSIONS);this.imgData = await this.ImageModel.getAllImg();Logger.info(Common.TAG, `images = ${this.imgData.length}`);this.numArray = await this.ImageModel.splitPic(this.index);// Test case. The game is simulated successfully.emitter.on({ eventId: Common.ZERO, priority: Common.ZERO }, this.over);// Test case. End of simulation time.emitter.on({ eventId: Common.ONE, priority: Common.ZERO }, this.timeEnd);}onTimeOver() {if (this.gameTime === Common.ZERO) {this.isGameStart = false;AlertDialog.show({ message: 'TimeOver' });clearInterval(this.timer);}}async onImageChange() {this.isRefresh = true;this.dialogController.close();this.numArray = [];this.numArray = await this.ImageModel.splitPic(this.index);this.init();this.isGameStart = false;this.isRefresh = false;}init() {this.gameTime = Common.GAME_TIME;clearInterval(this.timer);}gameOver() {let count = Common.ZERO;for (let i = Common.ZERO; i < Common.LOOP; i++) {if (this.numArray[i].index === i) {count++;} else {count = Common.ZERO;break;}}if (count === Common.LOOP) {this.isGameStart = false;AlertDialog.show({ message: $r('app.string.congratulations') });clearInterval(this.timer);this.gameTime = Common.GAME_TIME;}}start() {this.init();this.timer = setInterval(() => {this.gameTime--;}, Common.TIME_NUMBER)}dialogController: CustomDialogController = new CustomDialogController({builder: ImagePicker({imagesData: this.imgData,index: $index}),autoCancel: true,gridCount: Common.GRID_COUNT})@BuilderImageShow() {Image(this.imgData[this.index].uri).id(Common.IMAGE_SHOW).width(Common.IMAGE_WIDTH).height($r('app.float.imageShow_height')).objectFit(ImageFit.Fill).onClick(async () => {if (this.isRefresh) {return;}this.imgData = await this.ImageModel.getAllImg();setTimeout(() => {this.dialogController.open();}, Common.TIME);})}@BuilderImageGrid(leftMargin: number, topMargin: number) {Grid() {ForEach(this.numArray, (item: PictureItem, index) => {GridItem() {Image(item.pixelMap).width(Common.GRID_IMAGE_WIDTH).objectFit(ImageFit.Fill).height($r('app.float.grid_image_height'))}.id(`image${index}`).backgroundColor(item.pixelMap === undefined ? $r('app.color.blank_picture_background') : $r('app.color.picture_background')).onClick(() => {if (this.isRefresh) {return;}if (this.isGameStart) {this.isRefresh = true;this.numArray = this.game.gameInit(index, this.numArray);this.gameOver();this.isRefresh = false;}})}, (item: PictureItem) => JSON.stringify(item))}.id(Common.IMAGE_GRID).columnsTemplate(Common.COLUMN_TEMPLATE).columnsGap($r('app.float.gap')).rowsGap($r('app.float.gap')).width(Common.IMAGE_WIDTH).height($r('app.float.grid_height')).margin({ left: leftMargin, top: topMargin })}build() {Column() {Row() {Text(`Time:0${Math.floor(this.gameTime / Common.SECOND)}:${this.gameTime % Common.SECOND < Common.TEN? Common.STRING_ZERO + this.gameTime % Common.SECOND : this.gameTime % Common.SECOND}`).id(Common.TIME_STRING).margin({ top: Common.MARGIN, bottom: Common.MARGIN })}if (this.imgData.length > Common.ZERO) {if (this.isLand) {Row() {this.ImageShow()this.ImageGrid(Common.TEN, Common.ZERO)}.margin({ top: Common.MARGIN })} else {Column() {this.ImageShow()this.ImageGrid(Common.ZERO, Common.FIVE)}.margin({ top: Common.MARGIN })}}Button($r('app.string.start'), { type: ButtonType.Capsule, stateEffect: true }).id(Common.START_BUTTON).height($r('app.float.button_height')).width(Common.FULL_WIDTH).fontSize($r('app.float.button_font_size')).margin({ top: Common.MARGIN }).backgroundColor(this.isGameStart ? $r('app.color.forbid') : $r('app.color.allow')).enabled(!this.isGameStart).onClick(() => {this.isGameStart = true;this.start();this.numArray = this.game.gameBegin(this.numArray);})Button($r('app.string.restart'), { type: ButtonType.Capsule, stateEffect: true }).id(Common.RESTART_BUTTON).height($r('app.float.button_height')).width(Common.FULL_WIDTH).fontSize($r('app.float.button_font_size')).margin({ top: Common.MARGIN }).backgroundColor(this.isGameStart ? $r('app.color.allow') : $r('app.color.forbid')).enabled(this.isGameStart).onClick(() => {this.isGameStart = true;this.start();this.numArray = this.game.gameBegin(this.numArray);})}.width(Common.FULL_WIDTH).height(Common.FULL_HEIGHT).padding({ left: Common.PERCENT, right: Common.PERCENT })}
}
  • 图片编辑:首先使用createImagePacker创建ImagePacker实例,然后使用fs.open打开文件,调用createImageSource接口创建图片源实例方便操作图片,接下来使用getImageInfo方法获取图片大小便于分割,最后使用createPixelMap方法传入每一份的尺寸参数完成图片裁剪。

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

在这里插入图片描述

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/390571.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2024关于日本AI 领域TOP12 的大学介绍

1.东京大学 &#xff08;The University of Tokyo&#xff09; 位于&#xff1a;日本东京都文京区本郷七丁目3 番1 号 网址&#xff1a;東京大学 东京大学也被称为UTokyo 或东大&#xff0c;是日本第一所国立大学。作为领先的研究型 大学&#xff0c;东京大学提供基本所有…

8月17日|广州|Cocos开发者沙龙不见不散!

6月底举行的Cocos成都沙龙吸引了近200位开发者和10多家发行&#xff0c;得到了大家的一致好评。 Cocos广州沙龙即将到来&#xff0c;会邀请更多KOL和头部发行、渠道嘉宾分享行业经验&#xff0c;让大家实现技术干货、游戏合作、行业信息多丰收。 活动主题&#xff1a;小游戏与出…

vscode+git解决远程分支合并冲突

1&#xff09;远程分支和远程分支不复杂情况合并 例如readme的冲突 可直接在github上解决 删到只剩下 #supergenius002 合并冲突测试1/合并测试冲突1合并测试冲突2/合并测试冲突2就行 《《《/》》》也要删掉 2&#xff09;但如果是复杂的冲突&#xff0c;让我们回到vscod…

C++进阶:设计模式___适配器模式

前言 在C的基础语法的学习后,更进一步为应用场景多写代码.其中设计模式是有较大应用空间. 引入 原本在写容器中适配器类有关的帖子,发现适配模式需要先了解,于是试着先写篇和适配器模式相关的帖子 理解什么是适配器类,需要知道什么是适配器模式.适配器模式是设计模式的一种.笔…

剪画小程序:致敬奥运举重冠军:照片变成动漫风格!

在巴黎奥运会的赛场上&#xff0c;那些奥运冠军们的身影如同璀璨星辰&#xff0c;闪耀着无尽的光芒&#xff01; 看&#xff0c;举重冠军力拔山兮气盖世&#xff0c;那坚定的眼神中透露出无畏的勇气&#xff0c;爆发的力量更是震撼人心。 借助剪画&#xff0c;将这令人心潮澎湃…

LabVIEW激光主动探测系统

开发了一种基于LabVIEW的高性能激光主动探测控制与处理系统的设计与实现。该系统充分利用了LabVIEW的多线程和模块化设计优势&#xff0c;提供了一套功能完整、运行高效且稳定的解决方案&#xff0c;适用于高精度激光探测领域。 项目背景 激光主动探测技术利用激光作为主动光源…

基于SpringBoot+Vue的汽车服务管理系统(带1w+文档)

基于SpringBootVue的汽车服务管理系统(带1w文档) 基于SpringBootVue的汽车服务管理系统(带1w文档) 在开发系统过程中采用Java语言、MySQL数据库存储数据。系统以B/S为基础&#xff0c;实现管理一体化、规范化&#xff0c;为用户提供一个高效快捷的交流系统[5]。利用springboot架…

JSONP跨域

1 概述 定义 json存在的意义&#xff1a; 不同类型的语言&#xff0c;都能识别json JSONP(JSON with Padding)是JSON的一种“使用模式”&#xff0c;可用于解决主流浏览器的跨域数据访问的问题。由于同源策略&#xff0c;一般来说位于 server1.example.com 的网页无法与不是 s…

深度学习(1)--机器学习、人工智能、深度学习的关系

1956 年提出 AI 概念&#xff0c;短短3年后&#xff08;1959&#xff09; Arthur Samuel 就提出了机器学习的概念&#xff1a; Field of study that gives computers the ability to learn without being explicitly programmed. 机器学习研究和构建的是一种特殊算法&#xff0…

谷粒商城实战笔记-122~124-全文检索-ElasticSearch-分词

文章目录 一&#xff0c;122-全文检索-ElasticSearch-分词-分词&安装ik分词二&#xff0c;124-全文检索-ElasticSearch-分词-自定义扩展词库1&#xff0c;创建nginx容器1.1 创建nginx文件夹1.2 创建nginx容器获取nginx配置1.3 复制nginx容器配置文件1.4 删除临时的nginx容器…

力扣-200.岛屿数量

刷力扣热题–第二十四天:200.岛屿数量 新手第二十四天 奋战敲代码&#xff0c;持之以恒&#xff0c;见证成长 1.题目描述 2.题目解答 这道题刚开始想的确实想的绞尽脑汁的&#xff0c;看了相关解答才明白的&#xff0c;三种方法&#xff0c;这里想先用两种方法进行实现&#…

【课程总结】Day17(上):NLP自然语言处理及RNN网络

前言 在机器学习章节【课程总结】Day6&#xff08;上&#xff09;&#xff1a;机器学习项目实战–外卖点评情感分析预测中&#xff0c;我们曾借助sklearn进行了外卖点评的情感分析预测&#xff1b;接下来&#xff0c;我们将深入了解自然语言处理的基本概念、RNN模型以及借助RN…

法制史学习笔记(个人向) Part.7

法制史学习笔记(个人向) Part.7 11. 清朝法律制度 11.1 立法概述 11.1.1 立法指导思想 简单来说是&#xff1a;详译明律&#xff0c;参以国制&#xff1b; 努尔哈赤时期&#xff0c;后金政权处于由习惯法到成文法的过渡过程中&#xff1b;皇太极统治时期&#xff0c;奉行“参…

细说文件操作

你好&#xff01;感谢支持孔乙己的新作&#xff0c;本文就结构体与大家分析我的思路。 希望能大佬们多多纠正及支持 &#xff01;&#xff01;&#xff01; 个人主页&#xff1a;爱摸鱼的孔乙己-CSDN博客 目录 1.什么是文件 1.1.程序设计文件 1.1.1.程序文件 1.1.2.数据文件…

【网络】TCP协议——TCP连接相关、TCP连接状态相关、TCP数据传输与控制相关、TCP数据处理和异常、基于TCP应用层协议

文章目录 Linux网络1. TCP协议1.1 TCP连接相关1.1.1 TCP协议段格式1.1.2 确定应答(ACK)机制1.1.3 超时重传机制 1.2 TCP连接状态相关1.2.1 TIME_WAIT状态1.2.2 CLOSE_WAIT 状态 1.3 TCP数据传输与控制相关1.3.1 滑动窗口1.3.2 流量控制1.3.3 拥塞控制1.3.4 延迟应答1.3.5 捎带应…

【C语言】结构体内存布局解析——字节对齐

&#x1f984;个人主页:小米里的大麦-CSDN博客 &#x1f38f;所属专栏:https://blog.csdn.net/huangcancan666/category_12718530.html &#x1f381;代码托管:黄灿灿 (huang-cancan-xbc) - Gitee.com ⚙️操作环境:Visual Studio 2022 目录 一、引言 二、什么是字节对齐&…

使用Python绘制雷达图的简单示例

雷达图&#xff08;Radar Chart&#xff09;也被称为蜘蛛网图、星形图或极坐标图&#xff0c;是一种用于显示多变量数据的图形方法。它以一个中心点为起点&#xff0c;从中心点向外延伸出多条射线&#xff0c;每条射线代表一个特定的变量或指标。每条射线上的点或线段表示该变量…

【基础篇】MySQL数据库详解:基础知识详解

一、SQL分类 1.DDL2.DML3.DQL4.DCL二、函数 1.字符串函数2.数值函数3.日期函数4.流程函数三、约束 1.概述2.约束演示3.外键约束四、多表查询 1.多表关系2.多表查询表述3.内连接4.外连接5.自连接6.子查询五、事务 1.事务简介2.事务操作3.事务四大特性4.并发事务问题5.事务隔离级…

Git的一些简单使用

下列内容适用于git初学者&#xff0c;从创建本地git仓库到提交的一个基本过程1. 1.创建git仓库 在想创建git仓库的路径下打开git bash&#xff0c;输入以下命令行创建仓库&#xff08;一般来说&#xff0c;我觉得直接在code workspace得地方创建git仓库就可以了&#xff0c;这…

自从用了这些监控工具,我连续几天没睡好觉!

大家好&#xff0c;我是程序员鱼皮&#xff0c;今天分享一些很实用的系统监控告警工具。 为什么要用监控告警&#xff1f; 说到监控告警&#xff0c;没有企业开发经验的同学非常容易忽视它&#xff0c;甚至会有同学觉得没有必要&#xff0c;大不了出了 Bug 再修就是了。 这种…