赞
踩
本篇Codelab基于手势处理和截屏能力,介绍了手势截屏的实现过程。样例主要包括以下功能:
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
[获取OpenHarmony系统版本]:标准系统解决方案(二进制)。以3.2 Release版本为例:
搭建烧录环境。
搭建开发环境。
本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在gitee中提供。
- ├──entry/src/main/ets // 代码区
- │ ├──common
- │ │ └──utils
- │ │ ├──CommonConstants.ets // 公共常量类
- │ │ ├──DrawUtil.ets // 画布相关工具类
- │ │ └──Logger.ets // 日志打印类
- │ ├──entryability
- │ │ └──EntryAbility.ets // 程序入口类
- │ ├──model
- │ │ └──OffsetModel.ets // 区域截图坐标相关工具类
- │ ├──pages
- │ │ └──GestureScreenshot.ets // 主界面
- │ └──view
- │ ├──AreaScreenshot.ets // 自定义区域截屏组件类
- │ └──ScreenshotDialog.ets // 自定义截屏显示弹窗组件类
- └──entry/src/main/resources // 资源文件目录
使用下滑手势,进行全屏截图并展示图片。效果如图所示:
主界面主要实现以下功能:
- // GestureScreenshot.ets
- // 区域截图最底层,当主页面缩放后会露出,设置为黑色
- Stack() {
- // 主页面布局
- Column() {
- ...
- })
- // 添加滑动手势事件
- .gesture(
- // fingers:触发手指数 direction:触发方向 distance:触发滑动距离
- PanGesture({
- fingers: 1,
- direction: PanDirection.Down,
- distance: CommonConstants.MINIMUM_FINGER_DISTANCE
- })// 触发开始回调
- .onActionStart(() => {
- let screenshotOptions: screenshot.ScreenshotOptions = {
- rotation: 0
- };
- screenshot.save(screenshotOptions, (err: Error, data: image.PixelMap) => {
- if (err) {
- Logger.error(`Failed to save the screenshot. Error:${ JSON.stringify(err) }`);
- }
- if (this.pixelMap !== undefined) {
- this.pixelMap.release();
- }
- this.pixelMap = data;
- this.dialogController.open();
- });
- })
- )
- .scale(this.scaleNum)
-
- // 区域截图相关组件
- AreaScreenshot({ showScreen: this.showScreen, pixelMap: this.pixelMap, scaleNum: this.scaleNum })
- }
- .backgroundColor($r('app.color.black_area'))
- // 添加双击手势事件
- .gesture(
- TapGesture({ count: 2 })
- .onAction(() => {
- this.showScreen = true;
- this.scaleNum = {
- x: CommonConstants.X_SCALE_DOWN,
- y: CommonConstants.Y_SCALE_DOWN
- }
- })
- )
本章节将完成区域选择框的绘制并完成区域截图,效果如图所示:
在绘制区域选择框之前,首先需要在AreaScreenshot.ets的aboutToAppear方法中获取屏幕的宽和高,并初始化offsetModel和drawUtil对象(初始化参数为屏幕的宽高)。offsetModel对输入的坐标进行计算和更改,drawUtil使用offsetModel的坐标在屏幕上绘制区域选择框。
- // AreaScreenshot.ets
- aboutToAppear() {
- window.getLastWindow(getContext(this))
- .then((window) => {
- let property = window.getWindowProperties();
- this.systemBarHeight = property.windowRect.top;
-
- drawUtil.initDrawUtil(
- this.canvasRenderingContext,
- px2vp(property.windowRect.width),
- px2vp(property.windowRect.height)
- );
-
- offsetModel.initOffsetModel(
- px2vp(property.windowRect.width),
- px2vp(property.windowRect.height)
- );
-
- // 在展示截图的时候,用于计算图片大小
- this.screenAspectRatio = px2vp(property.windowRect.height) / px2vp(property.windowRect.width);
- })
- .catch((err: Error) => {
- Logger.error(`window loading has error: ${ JSON.stringify(err) }`);
- })
- }
在AreaScreenshot.ets布局页面中添加Canvas组件,通过showScreen变量控制局部截屏页面的显示,并控制主页面的缩放。步骤如下:
- // AreaScreenshot.ets
- // 关闭区域截屏相关组件,并还原主页面
- private resetParameter() {
- this.showScreen = false;
- this.scaleNum = {
- x: CommonConstants.NO_SCALE_DOWN,
- y: CommonConstants.NO_SCALE_DOWN
- };
- offsetModel.resetDefaultOffSet();
- }
-
- // 使用if渲染,控制区域截图相关组件的显隐
- if (this.showScreen) {
- Stack() {
- Canvas(this.canvasRenderingContext)
- ...
- .onReady(() => {
- // 通过draw方法绘制选择框和非高亮区域
- drawUtil.draw();
- })
- // 截图的工具栏
- Row() {
- ...
- // 区域截图并展示图像
- Image($r('app.media.ic_save'))
- .onClick(() => {
- let screenshotOptions: screenshot.ScreenshotOptions = {
- // 截屏区域Rect参数
- screenRect: {
- left: vp2px(offsetModel.getXLeft()),
- top: vp2px(offsetModel.getYTop()) + this.systemBarHeight,
- width: vp2px(offsetModel.getWidth()),
- height: vp2px(offsetModel.getHeight())
- } as screenshot.Rect,
- // 截图的大小
- imageSize: {
- width: vp2px(offsetModel.getWidth()),
- height: vp2px(offsetModel.getHeight())
- } as screenshot.Size,
- rotation: 0,
- displayId: 0
- };
- screenshot.save(screenshotOptions, (err: Error, data: image.PixelMap) => {
- if (err) {
- Logger.error(`Failed to save the screenshot. Error:${JSON.stringify(err)}`);
- }
- if (this.pixelMap !== undefined) {
- this.pixelMap.release();
- }
- this.pixelMap = data;
- // 使用弹窗组件展示截完的图片
- this.dialogController.open();
- });
- this.resetParameter();
- })
- }
- ...
- // 根据手指位置调整选择框大小和位置
- .onTouch((event: TouchEvent) => {
- switch(event.type) {
- case TouchType.Down:
- // 根据手指位置,判断移动哪个坐标
- offsetModel.setXLocationType(event.touches[0].screenX);
- offsetModel.setYLocationType(event.touches[0].screenY);
- break;
- case TouchType.Move:
- // 更新坐标信息,并保证坐标值合法
- offsetModel.resetOffsetXY(event.touches[0].screenX, event.touches[0].screenY);
- drawUtil.draw();
- break;
- default:
- break;
- }
- })
- }
在构建区域截图组件中介绍了OffsetModel和DrawUtil两个工具类,本章节介绍一下具体的实现步骤。
使用OffsetModel校验坐标的范围,并保存坐标相关信息。
- // OffsetModel.ets
- public initOffsetModel(width: number, height: number) {
- ...
- this.blackAreaWidth = this.screenWidth * (1 - CommonConstant.X_SCALE_DOWN);
- this.blackAreaWidth = this.blackAreaWidth / CommonConstant.BLACK_AREA_NUM;
- this.blackAreaHeight = this.screenHeight * (1 - CommonConstant.Y_SCALE_DOWN);
- this.blackAreaHeight = this.blackAreaHeight / CommonConstant.BLACK_AREA_NUM;
- }
-
- // 判断x坐标位置
- public setXLocationType(offsetX: number) {
- if (offsetX > this.offsetXRight - CommonConstant.OFFSET_RANGE &&
- offsetX < this.offsetXRight + CommonConstant.OFFSET_RANGE) {
- this.xLocationType = XLocationEnum.XRight;
- } else if (offsetX > this.offsetXLeft - CommonConstant.OFFSET_RANGE &&
- offsetX < this.offsetXLeft + CommonConstant.OFFSET_RANGE) {
- this.xLocationType = XLocationEnum.XLeft;
- } else {
- this.xLocationType = XLocationEnum.noChange;
- }
- }
-
- // 判断y坐标位置
- public setYLocationType(offsetY: number) {
- ...
- }
-
- // 根据参数改变坐标值
- public resetOffsetXY(offsetX: number, offsetY: number) {
- if (this.xLocationType === XLocationEnum.XLeft) {
- this.offsetXLeft = this.offsetXRight - offsetX < CommonConstant.OFFSET_RANGE * 2 ?
- this.offsetXLeft : offsetX;
- }
- ...
-
- this.checkOffsetXY();
- }
-
- // 再次校验坐标值,是否超出可截屏区域
- private checkOffsetXY() {
- this.offsetXLeft = this.offsetXLeft < this.blackAreaWidth ? this.blackAreaWidth : this.offsetXLeft;
- this.offsetXRight = this.offsetXRight > this.screenWidth - this.blackAreaWidth ?
- this.screenWidth - this.blackAreaWidth : this.offsetXRight;
- this.offsetYTop = this.offsetYTop < this.blackAreaHeight ? this.blackAreaHeight : this.offsetYTop;
- this.offsetYBottom = this.offsetYBottom > this.screenHeight - this.blackAreaHeight ?
- this.screenHeight - this.blackAreaHeight : this.offsetYBottom;
- }
DrawUtil主要提供绘制方法,用于绘制区域选择框。
- // DrawUtil.ets
- // 绘制整个区域选择框
- public draw() {
- this.offsetXLeft = offsetModel.getXLeft();
- this.offsetXRight = offsetModel.getXRight();
- this.offsetYTop = offsetModel.getYTop();
- this.offsetYBottom = offsetModel.getYBottom();
-
- // 填充非高亮区域
- this.drawScreenSelection();
- // 绘制框选线
- this.drawLines();
- }
-
- // 填充非高亮区域,设置回形区域并填充颜色
- private drawScreenSelection() {
- this.canvasContext.clearRect(0, 0, this.screenWidth, this.screenHeight)
- this.canvasContext.beginPath();
-
- this.canvasContext.moveTo(0, 0);
- this.canvasContext.lineTo(this.screenWidth, 0);
- this.canvasContext.lineTo(this.screenWidth, this.screenHeight);
- this.canvasContext.lineTo(0, this.screenHeight);
- this.canvasContext.closePath();
-
- this.canvasContext.moveTo(this.offsetXRight, this.offsetYTop);
- this.canvasContext.lineTo(this.offsetXLeft, this.offsetYTop);
- this.canvasContext.lineTo(this.offsetXLeft, this.offsetYBottom);
- this.canvasContext.lineTo(this.offsetXRight, this.offsetYBottom);
-
- this.canvasContext.globalAlpha = Constants.UNSELECT_AREA_ALPHA;
- this.canvasContext.fillStyle = Constants.UNSELECT_AREA_COLOR;
- this.canvasContext.closePath();
- this.canvasContext.fill();
- }
-
- // 绘制框选线
- private drawLines() {
- this.canvasContext.beginPath();
- ...
- this.canvasContext.moveTo(
- (this.offsetXLeft + Constants.LINES_MAX_LENGTH),
- (this.offsetYTop - Constants.GAP_WIDTH)
- );
- this.canvasContext.lineTo(
- (this.offsetXLeft - Constants.GAP_WIDTH),
- (this.offsetYTop - Constants.GAP_WIDTH)
- );
- this.canvasContext.lineTo(
- (this.offsetXLeft - Constants.GAP_WIDTH),
- (this.offsetYTop + Constants.LINES_MAX_LENGTH)
- );
- ...
- this.canvasContext.stroke();
- }
采用弹窗组件展示截屏,需要在aboutToAppear方法中计算对应的宽度:
- // ScreenshotDialog.ets
- aboutToAppear() {
- this.getDialogWidth();
- }
- ...
- private async getDialogWidth() {
- if (this.pixelMap !== undefined) {
- let info = await this.pixelMap.getImageInfo();
- let pixelMapAspectRatio = info.size.height / info.size.width;
-
- if ((this.screenAspectRatio !== -1) && (pixelMapAspectRatio > this.screenAspectRatio)) {
- let width = CommonConstants.HEIGHT_FIRST / pixelMapAspectRatio * this.screenAspectRatio;
- this.dialogWidth = width + '%';
- } else {
- this.dialogWidth = CommonConstants.WIDTH_FIRST;
- }
- }
- }
目前还有很多小伙伴不知道要学习哪些鸿蒙技术?不知道重点掌握哪些?为了避免学习时频繁踩坑,最终浪费大量时间的。
自己学习时必须要有一份实用的鸿蒙(Harmony NEXT)资料非常有必要。 这里我推荐,根据鸿蒙开发官网梳理与华为内部人员的分享总结出的开发文档。内容包含了:【ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战】等技术知识点。
废话就不多说了,接下来好好看下这份资料。
如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。鸿蒙OpenHarmony知识←前往。下面是鸿蒙开发的学习路线图。
针对鸿蒙成长路线打造的鸿蒙学习文档。鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。
《鸿蒙开发基础》鸿蒙OpenHarmony知识←前往
《鸿蒙开发进阶》鸿蒙OpenHarmony知识←前往
《鸿蒙开发实战》鸿蒙OpenHarmony知识←前往
鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。