往期推文全新看点
- 鸿蒙(HarmonyOS)北向开发知识点记录~
- 鸿蒙(OpenHarmony)南向开发保姆级知识点汇总~
- 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
- 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
- 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
- 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
- 记录一场鸿蒙开发岗位面试经历~
- 持续更新中……
随着终端设备形态日益多样化,应用设计需要考虑界面能否适配不同的屏幕尺寸、屏幕方向和设备类型。同时还需要保持多设备体验的连续性,改善多端独立的设计、尽可能降低开发者的工作量和维护成本。基于此 Harmony OS 为设计师提供了面向多设备的设计指导,让设计师在进行多端设计时有一套科学的方法,最大程度减少设计的工作量,保障多端设计在一定程度的一致性。同时 Harmony OS 也提供了对应的技术能力,帮助开发者快速地进行多端应用设计。
本指导结合用户在多端设备上的历史交互习惯、各场景下的使用诉求等,进行了一些设计方法的总结,主要包括如下几个部分:
- 基础要求:在多设备应用设计中需要遵守的基础体验要求,如果不满足基础要求,则会极大损害用户的使用体验。
- 响应式布局:在宽屏设备上,针对部分常见的界面元素提出了响应式布局设计范式,以避免简单拉伸、放大等导致的一些体验问题。
- 增值体验:在多设备应用设计中可以考虑更多体验上的变化,在合适的场景下提供增值的体验。
- 场景化设计参考:针对具体垂类场景应用给出了场景内典型页面的设计建议,方便设计师进行更有针对性的参考和选用。
基本要求
折叠屏应用开发具有导航、横竖屏、挖孔、多窗、弹出框等基础要求。
导航适配
1.底部导航&侧边导航
通常手机和折叠屏使用底部页签导航;平板及其他宽屏设备使用侧边页签或侧边栏导航。调用鸿蒙系统提供的控件可自动适配该导航规则。
2.底部导航条适配
移动端设备应用内的底部页签、底部工具栏、底部文本,或底部悬浮按钮需要自动抬高避让底部导航条。应用内的可滚动内容,可直接显示在导航条下方,仅当滚动到内容的最底部时需要向上抬高避免被底部导航条遮挡。调用鸿蒙控件,可自动实现以上导航条的避让规格。
说明
沉浸式页面,例如全屏播放视频、游戏、阅读等场景下,超过 2 秒自动隐藏底部导航条,从底部上滑或从顶部下滑可触发恢复显示底部导航条。
横竖屏与挖孔适配
手机和折叠屏折叠态的应用通常需要适配竖屏布局。仅部分特殊场景例如横屏游戏、长视频需要适配横屏布局。
折叠屏展开态、平板上,应用需要同时适配横屏和竖屏布局。
当设备尺寸比例接近 1:1 时,建议横竖屏使用相同或相近的布局。
当设备尺寸比例差异大时,横竖屏可以使用不同的布局,从而提供更好的使用体验,例如横屏后自动分栏或横屏后自动露出右侧的辅助信息等。
横竖屏适配过程中,需要考虑核心内容或重要交互不要被挖孔区遮挡,如果被遮挡则进行局部内容等避让;可滚动内容或非核心信息无需专门避让挖孔区;要避免因为避让挖孔导致左右侧留白不一致。
1.横竖屏适配
实现方案
在折叠屏展开时需要支持横竖屏旋转。使用display.on(‘foldDisplayModeChange’ API开启折叠设备屏幕显示模式变化的监听,使用 setPreferredOrientation API设置窗口的显示方向属性。
参考代码
工具类,用于设置窗口的显示方向属性
// utils/WindowsUtil.ets
import { window } from '@kit.ArkUI';
@Observed
export class WindowUtil {
private windowStage?: window.WindowStage;
private mainWindowClass?: window.Window;
// 获取实例
static getInstance(): WindowUtil | undefined {
if (!AppStorage.get<WindowUtil>('windowUtil')) {
AppStorage.setOrCreate('windowUtil', new WindowUtil());
}
return AppStorage.get<WindowUtil>('windowUtil');
}
setMainWindowPortrait(): void {
if (this.windowStage === undefined) {
return;
}
this.windowStage.getMainWindow((err, windowClass: window.Window) => {
this.mainWindowClass = windowClass;
});
}
setPhoneAutoRotation(): void {
if (this.mainWindowClass === undefined) {
return;
}
// 设置传感器自动旋转模式
this.mainWindowClass.setPreferredOrientation(window.Orientation.AUTO_ROTATION);
}
setPhoneLandscape(): void {
if (this.mainWindowClass === undefined) {
return;
}
// 设置横屏显示模式
this.mainWindowClass.setPreferredOrientation(window.Orientation.LANDSCAPE);
}
setWindowStage(windowStage: window.WindowStage): void {
this.windowStage = windowStage;
}
getMainWindow(): window.Window | undefined {
return this.mainWindowClass;
}
}
注册并获取全局windowStage对象。
// EntryAbility.ets
import { WindowUtil } from '../utils/WindowUtil';
export default class EntryAbility extends UIAbility {
// ...
onWindowStageCreate(windowStage: window.WindowStage): void {
let windowUtil = WindowUtil.getInstance();
if (windowUtil !== undefined) {
windowUtil.setWindowStage(windowStage);
windowUtil.setMainWindowPortrait();
}
// ...
}
// ...
}
实现组件,当屏幕展开时,设置屏幕自动旋转,当屏幕半折时,设置屏幕为横屏显示模式。
// FoldableScreen.ets
import { display } from '@kit.ArkUI'
import { WindowUtil } from '../utils/WindowUtil';
@Entry
@Component
export struct FoldableScreen {
@State isFoldable: Boolean = false
@State foldStatus: display.FoldStatus = 1
private windowUtil?: WindowUtil;
aboutToAppear() {
this.windowUtil = WindowUtil.getInstance();
this.windowUtil?.setPhoneAutoRotation();
// 实时监听设备折叠状态
display.on('foldStatusChange', (data: display.FoldStatus) => {
console.info('实时监听设备折叠状态: ' + JSON.stringify(data));
if (data === display.FoldStatus.FOLD_STATUS_HALF_FOLDED) {
this.windowUtil?.setPhoneLandscape()
} else if (data === display.FoldStatus.FOLD_STATUS_EXPANDED) {
this.windowUtil?.setPhoneAutoRotation();
}
});
}
build() {
Column() {
Text('demo')
}
.margin({ top: 100 })
}
}
多窗适配
手机和折叠屏折叠态、展开态,应用需要适配竖向悬浮窗、上下分屏、左右分屏、分屏比例支持自由调节。仅部分特殊场景例如横屏游戏、长视频,需要适配横向悬浮窗。
平板及更宽的设备上 ,应用需要适配自由窗口 (即可任意拖大、拖小、拖宽、拖窄) ,横屏时支持左右分屏,竖屏时支持上下分屏,且支持分屏比例的自由调节。
说明
在多端设备上,长视频、直播、会议、通话等场景,需要支持画中画。查看更多多窗的适配设计规范, 系统特性>多窗口交互 。
弹出框适配
建议调用系统的弹出框控件,避免在宽屏设备上弹出框过宽或过高的问题。
说明
若应用自定义弹出框尺寸,则建议在 8 栅格及以上的设备上,弹出框宽度为 480vp ;宽屏设备上弹出框的物理高度不超过手机上的该弹出框高度的 1.5 倍。
键鼠适配
平板及更宽的设备上的应用需要进行基础的鼠标、键盘等适配。
页面布局
折叠屏应用的页面布局包括响应式布局与Web页面布局。
1.响应式布局
应用内的页签、搜索、入口图标、广告图、列表、卡片、图片、内容模块等可以灵活地通过形变、延伸布局、重复布局、挪移布局、宫格布局、瀑布流布局等进行宽屏设备上的响应式适配,以达到较好的信息量和舒适的浏览体验。
1.1子页签&搜索
手机和折叠屏折叠态,搜索和子页签分两排显示。
折叠屏展开态、平板及更宽的设备上,子页签和搜索可以同一排显示,搜索框根据子页签数量的多少而自适应调整长度。
1.2入口图标
在多端设备上,建议 1 行显示的入口图标数量限制在一定范围内。例如在折叠屏和平板竖屏上一排图标不超过 8 个,在平板横屏上一排图标不超过 13 个,要避免一排图标过多导致信息过密。
1.3广告图
1.3.1卡片广告
在宽屏设备上建议使用延伸布局和形变进行卡片广告的响应式适配。
例如手机上一张广告卡片,在折叠屏可显示两张广告卡片,在平板显示三张广告卡片。同时基于各端的物理尺寸可进行广告卡片的自适应形变,在宽屏设备上广告卡片更长,在窄屏设备上广告卡片更高。
1.3.2沉浸广告
音视频等应用,为提供更沉浸的影音娱乐体验,可使用沉浸广告图效果。
应用使用沉浸广告时,建议沉浸广告的背景和广告内容元素分为多个图层,且为宽屏设备提供更长的背景图,或通过智能裁剪为不同宽度的设备裁剪出合适高度的背景图。保持背景图横向铺满整个设备宽度,且要避免背景图过高。广告的图片、文本等内容元素在宽屏设备上使用响应式布局。
金融理财、生活服务等类型的应用也经常使用沉浸广告图打造更好的营销氛围。需要考虑为多端提供一个适宜的沉浸广告高度,避免在宽屏设备上等比放大导致整个广告过高,导致首屏的信息量过少。
折叠屏和平板横屏时,建议沉浸广告图不超过 0.5 倍的屏幕高度,平板竖屏时建议沉浸广告图不超过 0.4 倍的屏幕高度。
1.4列表
1.4.1列表的重复布局
在宽屏设备上,为避免结构过于单调且信息量过少,可通过重复布局来改善页面的浏览舒适度和使用效率。
1.4.2列表的挪移布局
列表结构中的大视频或单张大图片,在宽屏设备上也可以通过图文挪移布局来避免视频或图片显示过大。
1.4.3列表变瀑布流或宫格
可以通过从列表到宫格或瀑布流的布局变化,带来宽屏设备上更舒适的增值阅读体验。
1.5卡片
手机上的单列的卡片,在宽屏设备上,通过分栏 + 瀑布流/宫格布局的方式进行显示适配,提升浏览效率且提供舒适布局。
1.6详情大图
在宽屏设备上,页面内的大图可以通过延伸布局或挪移布局进行适配。
1.7内容模块
同一个设备上,也可以根据多个模块的内容尺寸进行自适应的挪移布局。
2.Web页面布局
Web页面布局中讲述文字、图片、宫格元素、弹窗、广告、轮播图等的适配问题及实现方案。
2.1折叠屏文字大小适配
字体大小与折叠态一致。
两种实现方案
- 使用@media属性设置字体大小。
- 使用rem设置字体大小。
参考代码
1.使用@media属性单独设置折叠屏展开态时的字体大小。
span {
font-size: 4.6vw;
}
/* 使用@media属性设置字体大小 */
@media only screen and (min-width: 500px) {
span {
font-size: 2.3vw;
}
}
<span>xxxxxxxxxx</span>
2.字体大小跟随屏幕宽度变化的场景,可以使用rem实现,并控制字体大小的变化范围。
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
span {
font-size: 1rem;
}
</style>
<script>
/* 示例代码,根据屏幕宽度设置字体大小 */
function setHtmlFontSize() {
var baseFontSize = 16;
var baseScreenWidth = 375;
var screenWidth = window.innerWidth;
var screenHeight = window.innerHeight;
/* 示例代码:控制字体大小的变化范围 */
var fontSize = (Math.min (screenWidth, 400) / baseScreenWidth) * baseFontSize;
/* var fontSize = (screenWidth / baseScreenWidth) * baseFontSize; */
document.documentElement.style.fontSize = fontSize + "px";
}
window.addEventListener("load", setHtmlFontSize);
window.addEventListener("resize", setHtmlFontSize);
</script>
</head>
<body>
<div>
<span>xxxxxxxxxxxxxxxxxxxxxxx</span>
</div>
</body>
</html>
2.2折叠屏展开态图片适配
图片放大导致显示信息减少。
图片大小与折叠态相似、单张图片的情景。
实现方案
通过 @media 样式,设置图片元素或其容器在折叠屏展开态时的尺寸为原来的约1/2,即约为折叠态的1倍大小。
参考代码
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
div {
width: 90vw;
margin: 0 auto; /* 图片居中 */
}
/* 使用@media属性实现自适应 */
@media only screen and (min-width: 500px) {
div {
width: 50vw;
}
}
</style>
</head>
<body>
<div>
<img src=" D:\XXX.jpg" style="width: 100%" />
</div>
</body>
</html>
2.3折叠屏展开态下图标元素自适应
图标元素放大,导致页面整体显示信息减少。
图标元素尺寸不变,页面整体显示信息不变或增加。
实现方案
将重复元素的尺寸设定为绝对值、将重复元素容器的宽度设置为相对值。
参考代码
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
* {
margin: 0;
padding: 0;
}
ul {
width: 90vw;
height: auto;
margin: 0 auto;
font-size: 0;
}
li {
width: 60px;
height: 60px;
background-color: aqua;
display: inline-block;
margin: 0;
margin-bottom: 5px;
}
/* 示例:左对齐、均匀布局的一种方式 */
li:not(:nth-child(4n)) {
margin-right: calc((100% - 60px * 4) / 3);
}
/* 使用@media属性实现自适应 */
@media only screen and (min-width: 500px) {
li:not(:nth-child(8n)) {
margin-right: calc((100% - 60px * 8) / 7);
}
}
</style>
</head>
<body>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</body>
</html>
2.4折叠屏展开态下宫格元素自适应
宫格元素放大,导致显示信息减少。
宫格元素两列变三列,显示内容增多。
实现方案
通过 @media 属性,展开态下应用“元素宽高减小至原1/n”的样式,达到列数增加的效果;将容器宽度固定、高度设置为auto。
参考代码
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
#container {
width: 90vw;
height: auto;
margin: 0 auto;
font-size:0;
}
img {
width:100%;
}
.item {
margin-bottom: 10px;
display: inline-block;
}
/* 使用@media属性实现自适应 */
@media only screen and (max-width: 499px){
.item {
width:49%;
}
.item:not(:nth-child(2n)) { /* 示例:左对齐、均匀布局的一种方式 */
margin-right: 2%;
}
}
/* 使用@media属性实现自适应 */
@media only screen and (min-width: 500px){
.item {
width:32%;
}
.item:not(:nth-child(3n)) {
margin-right: 2%;
}
}
</style>
</head>
<body>
<div id="container">
<div class="item">
<img src="D:\XXX.jpg" />
</div>
<div class="item">
<img src="D:\XXX.jpg" />
</div>
<div class="item">
<img src="D:\XXX.jpg" />
</div>
<div class="item">
<img src="D:\XXX.jpg" />
</div>
<div class="item">
<img src="D:\XXX.jpg" />
</div>
<div class="item">
<img src="D:\XXX.jpg" />
</div>
<div class="item">
<img src="D:\XXX.jpg" />
</div>
</div>
</body>
</html>
2.5折叠屏展开态下弹窗元素大小自适应
弹窗元素过大。
弹窗元素尺寸与折叠态一致。
实现方案
通过 @media 样式,设置弹窗元素的容器在折叠屏展开态时的相对尺寸为原来的1/2,即约为折叠态的1倍大小。
参考代码
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script>
/* 展示Dialog */
function showDialog() {
document.getElementById("container").style.display = "block";
}
/* 隐藏Dialog */
function hideDialog() {
document.getElementById("container").style.display = "none";
}
window.onload = showDialog;
</script>
<style>
#container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
z-index: 9999;
}
#content {
position: absolute;
width: 70vw;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
font-size: 0;
}
/* 使用@media属性实现自适应 */
@media only screen and (min-width: 500px) {
#content {
width: 35vw;
}
}
img {
width: 100%;
}
button {
display: block;
margin: 0 auto;
height: 20px;
background-color: #007bff;
color: #fff;
border: none;
font-size: 12px;
}
</style>
</head>
<body>
<div id="container" style="display: none">
<div id="content">
<img src="./image/image1.png" />
<button onclick="hideDialog()">close</button>
</div>
</div>
</body>
</html>
2.6折叠屏展开态下广告图大小自适应
广告图尺寸过大,导致一页显示内容过少。
广告图适当放大后,保留核心内容、裁剪非核心内容。
实现方案
通过 @media属性和 overflow:hidden 会隐藏溢出部分的显示的特性,将广告图适当放大后,保留核心内容、裁剪非核心内容。
参考代码
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
div {
width: 90vw;
height: 200px;
margin: 0 auto;
overflow: hidden;
}
img {
width: 100%;
}
/* 使用@media属性实现自适应 */
@media only screen and (min-width: 500px) {
div {
height: 300px;
}
img {
margin-top: -50px;
}
}
</style>
</head>
<body>
<div>
<img src="D:\XXX.jpg" />
</div>
</body>
</html>
2.7折叠屏展开态下横向轮播的运营类图片大小自适应
运营类图片尺寸过大,导致一页显示内容过少。
弹窗元素尺寸与折叠态一致。
实现方案
使用@media属性将图片尺寸减小为原来的1/2、即与折叠态一致;将图片左移25%屏幕宽度的距离,使之居中显示;使用border-right等方法在两张图片之间加入间距。
建议对屏幕尺寸变化事件("resize"事件)进行监听,在开合动作后重新设置位移,以保持内容显示连续性。
参考代码
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
#par {
width: 90vw;
height: auto;
overflow: hidden;
font-size: 0;
margin: 0 auto;
}
#container {
width: 100%;
display: inline-block;
white-space: nowrap;
}
.item {
width: 100%;
}
/* 展开态 0.5+1+0.5 布局示例 */
@media only screen and (min-width: 500px) {
#container {
margin-left: 25%;
}
.item {
width: 50%;
border-right: 10px solid transparent;
}
}
</style>
</head>
<body>
<div id="par">
<div id="container" style="transform: translateX(0)">
<img class="item" src="./images.jpg" />
<img class="item" src="./images3.jpg" />
<img class="item" src="./images.jpg" />
<img class="item" src="./images3.jpg" />
<img class="item" src="./images.jpg" />
<img class="item" src="./images3.jpg" />
</div>
</div>
<script>
let par = document.getElementById("par");
let container = document.getElementById("container");
let itemWidth = document.getElementsByClassName("item")[0].offsetWidth;
let itemNum = document.getElementsByClassName("item").length;
let initialX = 0;
let distanceX = 0;
let imgFlag = 0;
par.addEventListener("touchstart", function (event) {
initialX = event.touches[0].clientX;
});
par.addEventListener("touchmove", function (event) {
event.preventDefault();
distanceX = event.touches[0].clientX - initialX;
transform = -(imgFlag * itemWidth) + distanceX;
container.style.transform = "translateX(" + transform + "px)";
});
par.addEventListener("touchend", function (event) {
if (distanceX < -30) {
imgFlag = imgFlag < itemNum - 1 ? imgFlag + 1 : 0;
} else if (distanceX > 30) {
imgFlag = imgFlag > 0 ? imgFlag - 1 : itemNum - 1;
}
container.style.transform =
"translateX(" + -(imgFlag * itemWidth) + "px)";
});
// 保持折叠展开的内容显示连续性
window.addEventListener("resize", function () {
itemWidth = document.getElementsByClassName("item")[0].offsetWidth;
container.style.transform =
"translateX(" + -(imgFlag * itemWidth) + "px)";
});
</script>
</body>
</html>
常见问题
展开态使用0.5+1+0.5三张图布局时,首图和尾图两侧有留白。
实现方案
在图片列表前、后各补充一张图片,对留白部分进行填充;通过控制图片序号的循环范围,使填充图不显示在画面中央。
参考代码
修改上述代码中的JS部分:
<script>
let par = document.getElementById("par");
let container = document.getElementById("container");
let itemWidth = document.getElementsByClassName("item")[0].offsetWidth;
let itemNum = document.getElementsByClassName("item").length;
let initialX = 0;
let distanceX = 0;
let imgFlag = 1; // 记录当前显示的图片序号,因填充图不显示而从1开始记录
FillImg(); // 首尾填充两张图,使图片两边不出现留白
container.style.transform =
"translateX(" + -(imgFlag * itemWidth) + "px)"; // 从非填充的图片开始显示
/*
* 下述为滑动切换图片的一种示例;
* 需注意第一张图(序号 0 )和最后一张图(序号 itemNum-1 )是复制出来的填充图,不显示在画面中央
*/
par.addEventListener("touchstart", function (event) {
initialX = event.touches[0].clientX;
});
par.addEventListener("touchmove", function (event) {
event.preventDefault();
distanceX = event.touches[0].clientX - initialX;
transform = -(imgFlag * itemWidth) + distanceX;
container.style.transform = "translateX(" + transform + "px)";
});
par.addEventListener("touchend", function (event) {
if (distanceX < -30) {
imgFlag = imgFlag < itemNum - 2 ? imgFlag + 1 : 1;
} else if (distanceX > 30) {
imgFlag = imgFlag > 1 ? imgFlag - 1 : itemNum - 2;
}
container.style.transform =
"translateX(" + -(imgFlag * itemWidth) + "px)";
});
function FillImg() {
let newImg = document
.getElementsByClassName("item")
[itemNum - 1].cloneNode();
container.insertBefore(
newImg,
document.getElementsByClassName("item")[0]
);
newImg = document.getElementsByClassName("item")[1].cloneNode();
container.appendChild(newImg);
itemNum += 2; // 图片总数+2;
}
// 保持折叠展开的内容显示连续性
window.addEventListener("resize", function () {
itemWidth = document.getElementsByClassName("item")[0].offsetWidth;
container.style.transform =
"translateX(" + -(imgFlag * itemWidth) + "px)";
});
</script>
最后
总是有很多小伙伴反馈说:鸿蒙开发不知道学习哪些技术?不知道需要重点掌握哪些鸿蒙开发知识点? 为了解决大家这些学习烦恼。在这准备了一份很实用的鸿蒙全栈开发学习路线与学习文档给大家用来跟着学习。
针对一些列因素,整理了一套纯血版鸿蒙(HarmonyOS Next)全栈开发技术的学习路线,包含了鸿蒙开发必掌握的核心知识要点,内容有(OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、OpenHarmony驱动开发、系统定制移植……等)技术知识点。
《鸿蒙 (Harmony OS)开发学习手册》(共计892页):https://docs.qq.com/doc/DSEd0U29uT3hHbFZK
如何快速入门?
1.基本概念
2.构建第一个ArkTS应用
3.……
鸿蒙开发面试真题(含参考答案):
《OpenHarmony源码解析》:
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
- 系统架构分析
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……