介紹
隨著智能設備類型的不斷豐富,用戶可以在不同的設備上享受同樣的服務,但由于設備形態不盡相同,開發者往往需要針對具體設備修改或重構代碼,以實現功能完整性和界面美觀性的統一。OpenHarmony為開發者提供了“一次開發,多端部署”的系統能力,讓開發者可以基于一次開發,快速構建不同類型終端上的應用,降低開發成本,提高開發效率。
本篇Codelab基于“一次開發,多端部署”提供的自適應布局和響應式布局能力,實現了常見的視頻播放應用的主界面。通過三層工程結構盡可能復用了部分代碼,并根據設備尺寸的區別設計了對應的頁面以兼顧美觀和易用。應用被打開時會根據具體的設備形態顯示對應的UI界面,其中RK3568開發板的首頁效果如圖所示:
相關概念
- [一次開發,多端部署]:指一套代碼工程,一次開發上架,多端按需部署,目標是支撐開發者高效地開發支持多種終端設備形態的應用。
- [自適應布局]:當外部容器大小發生變化時,元素可以根據相對關系自動變化以適應外部容器變化的布局能力。相對關系如占比、固定寬高比、顯示優先級等。當前自適應布局能力有7種:拉伸能力、均分能力、占比能力、縮放能力、延伸能力、隱藏能力、折行能力。自適應布局能力可以實現界面顯示隨外部容器大小連續變化。
- [響應式布局]:當外部容器大小發生變化時,元素可以根據斷點、柵格或特定的特征(如屏幕方向、窗口寬高等)自動變化以適應外部容器變化的布局能力。當前響應式布局能力有3種:斷點、媒體查詢、柵格布局。
- [GridRow]:柵格容器組件,僅可以和柵格子組件(GridCol)在柵格布局場景中使用。
- [GridCol]:柵格子組件,必須作為柵格容器組件(GridRow)的子組件使用。
環境搭建
軟件要求
硬件要求
- 開發板類型:[潤和RK3568開發板]。
- OpenHarmony系統:3.2 Release。
環境搭建
完成本篇Codelab我們首先要完成開發環境的搭建,本示例以RK3568開發板為例,參照以下步驟進行:
- [獲取OpenHarmony系統版本]:標準系統解決方案(二進制)。以3.2 Release版本為例:
- 搭建燒錄環境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開發板的燒錄]
- 搭建開發環境。
- 開始前請參考[工具準備],完成DevEco Studio的安裝和開發環境配置。
- 開發環境配置完成后,請參考[使用工程向導]創建工程(模板選擇“Empty Ability”)。
- 工程創建完成后,選擇使用[真機進行調測]。
- 開發前請熟悉鴻蒙開發指導文檔:
gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
點擊或者復制轉到。
代碼結構解讀
本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在gitee中提供。
“一次開發,多端部署”推薦使用三層目錄的工程結構來管理工程,上層目錄包括common、features和product,common為公共特性目錄,存放不同形態設備公用的類和常量,features為功能模塊目錄,存放應用的各個功能模塊,product為產品層目錄,存放不同形態設備范類代碼。本Codelab不涉及功能特性,因此只存在common、product兩個分層。
├──common // 公共能力層
│ ├──src/main/ets
│ │ ├──constants
│ │ │ └──CommonConstants.ets // 公共常量類
│ │ ├──utils
│ │ │ └──BreakpointSystem.ets // 斷點工具類
│ │ └──viewmodel // 資源類接口
│ │ ├──BottomTabsItem.ets
│ │ ├──DriveTabsItem.ets
│ │ ├──FindTabsItem.ets
│ │ ├──HomeTabsItem.ets
│ │ └──MineTabsItem.ets
│ └──src/main/resources // 資源文件夾
└──product // 產品定制層
├──default/src/main/ets // 支持手機(含折疊屏)、平板
│ ├──entryability
│ │ └──EntryAbility.ts // 程序入口類
│ ├──pages
│ │ └──MainPage.ets // 主頁面
│ ├──view
│ │ ├──BottomTabsComponent.ets // 底部頁簽組件
│ │ ├──DriveTabsComponent.ets // 云盤頁組件
│ │ ├──FindTabsComponent.ets // 發現頁組件
│ │ ├──HomeTabsComponent.ets // 首頁組件
│ │ ├──LeftTabsComponent.ets // 側邊欄組件
│ │ ├──MineTabsComponent.ets // 個人頁組件
│ │ ├──RecentlyPlayedComponent.ets // “最近播放”列表
│ │ └──RecommendComponent.ets // “為你推薦”列表
│ └──viewmodel
│ ├──BottomTabsModel.ets // 底部頁簽model
│ ├──DriveTabsModel.ets // 云盤頁model
│ ├──FindTabsModel.ets // 發現頁model
│ ├──HomeTabsModel.ets // 首頁model
│ └──MineTabsModel.ets // 個人頁model
└──default/src/main/resources // 資源文件夾
`HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿`
主頁面框架設計
為了操作便捷和充分利用不同形態設備的屏幕空間,按屏幕寬度的大小將設備劃分為3類:
- sm:320vp<=width<520vp,典型設備為手機。
- md:520vp<=width<840vp,典型設備為折疊屏。
- lg:840vp<=width,典型設備為平板或PC。
根據用戶使用場景,當操作設備尺寸為sm或md時,一般為豎向使用,此時用于切換應用頁面的頁簽欄適合置于底部。當操作設備尺寸為lg時,一般為橫向使用,此時頁簽欄適合置于左側。
// MainPage.ets
@Entry
@Component
struct MainPage {
...
build() {
SideBarContainer(SideBarContainerType.Embed) {
LeftTabs({ bottomTabIndex: $bottomTabIndex }); // 側邊欄
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.End, justifyContent: FlexAlign.End }) {
Tabs({ barPosition: BarPosition.End, index: 0, controller: this.controller }) {
... // 頁面內容
}
if (this.currentBreakpoint !== Const.LG) {
BottomTabs({ bottomTabIndex: $bottomTabIndex }) // 底部欄,當屏幕尺寸不為"lg"時顯示
}
}
.width(Const.FULL_SIZE)
.backgroundColor($r('app.color.background_color'))
}
.showSideBar(this.currentBreakpoint === Const.LG) // 當屏幕尺寸為"lg"時顯示側邊欄
.showControlButton(false)
.sideBarWidth(Const.SIDEBAR_WIDTH)
.maxSideBarWidth(Const.SIDEBAR_WIDTH_MAX)
.minSideBarWidth(Const.SIDEBAR_WIDTH_MIN)
}
}
各頁面代碼實現
首頁
首頁顯示輪播圖和“最近播放”、“為你推薦”兩個列表,輪播圖根據屏幕尺寸的區別,有顯示數量的不同(sm為1,md為2,lg為3),列表使用具備自適應布局能力的List組件。
// HomeTabsComponent.ets
@Component
export struct HomeTabs {
@Link currentBreakpoint: string;
private scroller: Scroller = new Scroller();
build() {
Scroll(this.scroller) {
GridRow({
// 設置sm、md和lg的布局列數分別為4、8、12
columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
gutter: { x: $r('app.float.gutter_home') },
breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
}) {
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
... // 標題
}
.height($r('app.float.title_height'))
.margin({ bottom: $r('app.float.home_margin1') })
// 搜索欄在sm、md下占滿全部列,在lg下占8列
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_8 } }) {
... // 搜索欄
}
.height($r('app.float.home_grid_height1'))
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
Swiper() {
...
}
.height($r('app.float.home_swiper_height'))
.itemSpace(Const.ITEM_SPACE)
// 根據屏幕尺寸大小選擇不同的輪播圖數量
.displayCount(this.currentBreakpoint === Const.LG ?
Const.NUM_3 : (this.currentBreakpoint === Const.MD ? Const.NUM_2 : Const.NUM_1))
}
.height($r('app.float.home_grid_height2'))
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
... // ”最近播放”列表
}
.height($r('app.float.home_grid_height3'))
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
... // ”為你推薦”列表
}
.height($r('app.float.home_column_height'))
}
.height(Const.FULL_SIZE)
}
...
}
}
發現頁
發現頁使用柵格布局實現“一次開發,多端部署”能力,把sm設置為4列,md設置為8列,lg設置為12列。熱播榜單在不同設備尺寸上分別占據4列、6列和8列。
// FindTabsComponent.ets
@Component
export struct FindTabs {
private scroller: Scroller = new Scroller();
build() {
Scroll(this.scroller) {
GridRow({
// 設置sm、md和lg的布局列數分別為4、8、12
columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
gutter: { x: $r('app.float.gutter_find') },
breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
}) {
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
... // 標題
}
.height($r('app.float.title_height'))
LazyForEach(new FindDataSource(FindTabsList), (item: FindTabsItem) = > {
// 設置熱播榜單在sm、md和lg上分別占據4、6、8列,并且設置offset屬性保證在不同設備形態上都能保持居中
GridCol({
span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_6, lg: Const.GRID_8 },
offset: {
md: FindTabsList.indexOf(item) === Const.OFFSET_0 ? Const.OFFSET_1 : Const.OFFSET_2,
lg: FindTabsList.indexOf(item) === Const.OFFSET_0 ? Const.OFFSET_2 : Const.OFFSET_4
}
}) {
... // 榜單內容
}
}, (item: FindTabsItem) = > JSON.stringify(item))
}
}
...
}
}
RK3568開發板上發現頁的實際效果如圖所示:
云盤頁
云盤頁的柵格劃分和發現頁相同,但是每個子組件在所有屏幕尺寸上都只占據2列。
// DriveTabsComponent.ets
@Component
export struct DriveTabs {
private scroller: Scroller = new Scroller();
build() {
Scroll(this.scroller) {
GridRow({
// 設置sm、md和lg的布局列數分別為4、8、12
columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
gutter: { x : $r('app.float.gutter_drive') },
breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
}) {
GridCol({ span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 } }) {
... // 標題
}
.height($r('app.float.title_height'))
ForEach(DriveList, (item: DriveTabsItem) = > {
// 設置云盤內容在sm、md和lg上均占據2列
GridCol({ span: { xs: Const.NUM_2, sm: Const.NUM_2, md: Const.NUM_2, lg: Const.NUM_2 } }) {
... // 云盤內容
}
}, (item: DriveTabsItem) = > JSON.stringify(item))
}
}
...
}
}
RK3568開發板上云盤頁的實際效果如圖所示:
個人頁
個人頁的柵格劃分仍然和發現頁相同,但子組件在sm、md形態下占滿全部列,在lg形態下只占據8列。
// MineTabsComponent.ets
@Component
export struct MineTabs {
private scroller: Scroller = new Scroller();
build() {
Scroll(this.scroller) {
GridRow({
// 設置sm、md和lg的布局列數分別為4、8、12
columns: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_12 },
gutter: { x: $r('app.float.gutter_mine') },
breakpoints: { value: [Const.BREAKPOINTS_SM, Const.BREAKPOINTS_MD, Const.BREAKPOINTS_LG] }
}) {
// 設置個人頁在sm和md上占滿全部列,在lg上占8列,為保證居中在lg上設置offset為2列
GridCol({
span: { xs: Const.GRID_4, sm: Const.GRID_4, md: Const.GRID_8, lg: Const.GRID_8 },
offset: { lg: Const.OFFSET_2 }
}) {
... // 個人頁內容
}
}
.height(Const.FULL_SIZE)
.backgroundColor($r('app.color.mine_background_color'))
}
...
}
}
RK3568開發板上個人頁的實際效果如圖所示:
審核編輯 黃宇
-
鴻蒙
+關注
關注
57文章
2308瀏覽量
42739 -
HarmonyOS
+關注
關注
79文章
1967瀏覽量
30017 -
OpenHarmony
+關注
關注
25文章
3658瀏覽量
16149
發布評論請先 登錄
相關推薦
評論