细解鸿蒙之元服务UX上架标准-挖孔区适配
是否必须遵守:必须
标准项描述:在各类应用程序的设计与开发过程中,界面布局需要充分考虑摄像头挖孔区域,以实现全面且有效的适配。这一适配过程涉及多个方面,以下详细阐述:
适配目标
确保应用界面在摄像头挖孔区域周围的显示和交互功能正常,优化用户体验。当用户使用应用时,挖孔区域不会干扰到界面内容的展示,且应用能够在挖孔区域提供良好的交互操作。
适配内容
1.显示布局
内容调整:应用需要对挖孔区域周围的内容进行重新布局。例如,在主屏幕界面上,当摄像头挖孔位于屏幕顶部时,附近的图标、文字等元素需要进行合理调整,避免被挖孔遮挡。这可能涉及到将原本在挖孔位置的信息移到其他位置,或者调整字体大小、图标尺寸等,以保证内容的完整性和可读性。
背景设计:背景图像或图案需要与挖孔区域相协调。例如,在壁纸设计中,壁纸图案可能需要避开挖孔区域,或者在挖孔周围设置特殊的过渡效果,使挖孔与整体背景融合得更加自然。这样可以避免挖孔区域在视觉上显得突兀,提升整体美观度。
显示比例:应用要根据挖孔区域的位置和尺寸,调整显示比例。例如,在一些应用中,挖孔区域可能会占用一定的屏幕空间,导致屏幕显示比例发生变化。应用需要根据这种变化,调整内容的显示方式,确保在挖孔区域周围的内容能够正常显示,不会出现拉伸或变形等情况。
- 交互功能
触摸操作:应用需要对挖孔区域周围的触摸操作进行优化。由于挖孔区域的存在,触摸操作可能会受到影响。例如,在挖孔附近进行触摸操作时,可能会出现误操作的情况。应用需要通过设置触摸区域的敏感度、手势识别等方式,确保用户能够准确地进行触摸操作。例如,当用户在挖孔附近进行滑动操作时,应用能够准确识别手势,并做出相应的响应。
功能按钮:在挖孔区域周围设置合适的功能按钮。例如,在拍照应用中,挖孔附近可能会设置一些拍照、录像等功能按钮。这些按钮需要根据挖孔区域的位置和用户操作习惯进行合理布局,方便用户操作。同时,按钮的大小、位置和形状也需要考虑到挖孔区域的影响,确保用户能够方便地点击按钮。
通知提示:应用在挖孔区域周围的通知提示也需要进行适配。例如,当有新消息或通知时,通知内容可能会在挖孔附近显示。应用需要确保通知内容不会被挖孔遮挡,并且能够清晰地显示。这可能涉及到调整通知的位置、字体大小等,以保证用户能够及时获取通知信息。
适配方法
- 技术手段
检测与识别:利用设备的传感器和系统接口,检测挖孔区域的位置和尺寸。通过代码编写,获取挖孔区域的相关信息,以便应用进行相应的适配处理。例如,在应用开发过程中,使用设备的摄像头传感器来检测挖孔区域的位置和大小。
布局调整:根据挖孔区域的位置和尺寸,通过代码调整应用的布局。例如,在应用的布局文件中,使用特定的布局参数来调整挖孔区域周围的元素位置和大小。同时,利用响应式设计技术,使应用能够根据不同屏幕尺寸和挖孔位置进行自动调整。
图形处理:对挖孔区域进行图形处理,优化显示效果。例如,通过图像处理技术对挖孔区域周围的图像进行裁剪、拉伸等操作,使挖孔区域与整体界面融合得更加自然。同时,应用还可以利用动画效果来增强挖孔区域的视觉效果。
2.测试与优化
模拟测试:在开发过程中,通过模拟不同设备的挖孔区域进行测试。利用模拟器或测试设备,模拟挖孔区域的位置和尺寸,测试应用在不同挖孔区域的显示和交互效果。例如,在模拟器上模拟挖孔区域的位置和大小,测试应用在该区域的显示和操作情况。
用户反馈:收集用户反馈,了解用户在挖孔区域的使用体验。通过用户反馈,发现应用在挖孔区域存在的问题,并及时进行优化。例如,用户可能会反馈挖孔区域周围的显示不清晰、操作不方便等问题,开发团队根据这些反馈进行针对性的改进。
优化调整:根据测试结果和用户反馈,对应用进行优化调整。例如,对挖孔区域周围的显示和交互功能进行调整,优化应用的性能和用户体验。同时,不断改进应用的适配策略,提高应用在挖孔区域的适配效果。
总之,界面布局适配摄像头挖孔区域是一个复杂而细致的过程,需要综合考虑多种因素,通过技术手段和测试优化,确保应用在挖孔区域能够实现良好的显示和交互功能,提升用户体验。 --javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
// 当窗口阶段创建时执行的函数
onWindowStageCreate(windowStage: window.WindowStage): void {
// 将 windowStage 存储到 AppStorage 中,使用键 'context'
AppStorage.setOrCreate('context', windowStage);
// 获取主窗口
windowStage.getMainWindow((err: BusinessError, data) => {
// 存储错误代码
let errCode: number = err.code;
// 设置窗口为全屏显示状态
data.setWindowLayoutFullScreen(true);
// 要隐藏的系统栏名称数组,这里包含 'status' 和 'navigation'
let names: Array<'status' | 'navigation'> = [];
// 设置窗口系统栏的可见性,这里是隐藏顶部状态栏
data.setWindowSystemBarEnable(names, (err: BusinessError) => {
if (err.code) {
// 若设置系统栏可见性时出错,打印错误信息
console.error('Failed to set the system bar to be visible. Cause:' + JSON.stringify(err));
return;
}
// 若设置系统栏可见性成功,打印成功信息
console.info('Succeeded in setting the system bar to be visible.');
});
})
//...
}
import { display, promptAction, window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';
import { batteryInfo } from '@kit.BasicServicesKit';
// 定义一个类来存储文本的左右边距信息
class TextMargin {
left: number = 0; // 状态栏左偏移量
right: number = 0; // 状态栏右偏移量
}
// 标记该组件为入口组件
@Entry
// 声明该类为组件
@Component
struct Index {
// 存储当前日期
@State date: Date = new Date();
// 存储顶部状态栏显示的时间
@State currentTime: string = '';
// 存储不可用区域数据
@State boundingRect: display.Rect[] = [];
// 存储屏幕宽度
@State screenWidth: number = 0;
// 存储显示对象
@State displayClass: display.Display | null = null;
// 存储顶部状态栏偏移量
@State topTextMargin: TextMargin = { left: 0, right: 0 };
// 从存储中获取 UIAbilityContext,使用键 'context'
@StorageLink('context') context: common.UIAbilityContext | undefined =
AppStorage.get('context');
// 组件即将显示时执行的函数
aboutToAppear(): void {
// 获取默认的显示对象
this.displayClass = display.getDefaultDisplaySync();
// 获取屏幕切口信息
display.getDefaultDisplaySync().getCutoutInfo((err, data) => {
if (err.code!== 0) {
// 若获取切口信息失败,打印错误信息
console.log('getCutoutInfo failed. error is:', JSON.stringify(err));
return;
}
// 存储切口信息的边界矩形数组
this.boundingRect = data.boundingRects;
// 计算并存储顶部状态栏的偏移量
this.topTextMargin = this.getBoundingRectPosition();
})
// 获取当前日期的小时数
let hours = this.date.getHours();
// 获取当前日期的分钟数
let minutes = this.date.getMinutes();
// 将小时和分钟组合成时间字符串,分钟小于 10 时在前面加 0
this.currentTime = hours.toString() + ':' + (minutes < 10? '0' + minutes : minutes.toString());
}
// 组件即将消失时执行的函数
aboutToDisappear() {
if (this.context!== undefined) {
// 获取最后一个窗口
window.getLastWindow(this.context, async (err, data) => {
if (err.code!== 0) {
// 若获取最后一个窗口失败,打印错误信息
console.log('getLastWindow failed. error is:', JSON.stringify(err));
// 将系统栏设置为可见
data.setWindowSystemBarEnable(['status', 'navigation']);
// 将窗口布局设置为非全屏
data.setWindowLayoutFullScreen(false);
}
})
}
}
// 计算边界矩形位置的函数
getBoundingRectPosition(): TextMargin {
if (this.boundingRect!== null && this.displayClass!== null && this.boundingRect[0]!== undefined) {
// 计算不可用区域右侧到屏幕右边界的距离
let boundingRectRight: number =
this.displayClass.width - (this.boundingRect[0].left + this.boundingRect[0].width);
// 获取不可用区域左侧到屏幕左边界的距离
let boundingRectLeft: number = this.boundingRect[0].left;
// 部分设备不可用区域在中间时存在左右距离会有 10 像素以内的差距,获取到的左右距离差值绝对值小于 10 都按照不可用区域位于中间处理
if (Math.abs(boundingRectLeft - boundingRectRight) <= 10) {
return { left: 0, right: 0 };
}
if (boundingRectLeft > boundingRectRight) {
// 不可用区域在右边时的偏移量设置
return { left: 0, right: this.displayClass.width - boundingRectLeft };
} else if (boundingRectLeft < boundingRectRight) {
// 不可用区域在左边时的偏移量设置
return { left: this.boundingRect[0].left + this.boundingRect[0].width, right: 0 };
}
}
return { left: 0, right: 0 };
}
build() {
// 使用 Stack 组件进行布局
Stack() {
// 显示一张图片,使用资源 $r('app.media.digging_hole_screen_2048game')
Image($r('app.media.digging_hole_screen_2048game'))
.objectFit(ImageFit.Fill)
.width('100%')
.height('100%')
.onClick(() => {
// 点击图片时显示一个暂未开发的提示
promptAction.showToast({
message: '该功能暂未开发',
duration: 2000
})
})
// 使用 Column 组件进行布局
Column() {
// 使用 Flex 组件进行布局,方向为行,元素之间的空间分布为两端对齐
Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {
// 显示时间文本
Text(this.currentTime)
.fontSize(16)
.fontColor(Color.Black)
.fontWeight(FontWeight.Regular)
.padding({ left: 12 })
.margin({
left: px2vp(this.topTextMargin.left),
top: 14
})
// 显示电池电量文本
Text(batteryInfo.batterySOC.toString() + '%')
.fontSize(16)
.fontColor(Color.Black)
.fontWeight(FontWeight.Regular)
.padding({ right: 16 })
.margin({
right: px2vp(this.topTextMargin.right),
top: 14
})
}
.width('100%')
}
.width('100%')
}
.width('100%')
.height('100%')
.alignContent(Alignment.TopStart)
}
}
PS:实际项目中如有出入,请告知博主,博主会第一时间修改得哇~