电子说
本篇Codelab基于手势处理和截屏能力,介绍了手势截屏的实现过程。样例主要包括以下功能:
完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:
qr23.cn/AKFP8k
],完成DevEco Studio的安装和开发环境配置。gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]创建工程(模板选择“Empty Ability”)。本篇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 // 资源文件目录
使用下滑手势,进行全屏截图并展示图片。效果如图所示:
主界面主要实现以下功能:
// 区域截图最底层,当主页面缩放后会露出,设置为黑色
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;
}
}
}
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !