OpenHarmony 源碼解析之圖形子系統(tǒng)(UI)
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
1 簡(jiǎn)介
本文基于OpenHarmony 3.0為基礎(chǔ),講解Graphic子系統(tǒng)的UI。 圖形UI組件實(shí)現(xiàn)了一套系統(tǒng)級(jí)的圖形引擎,該組件為應(yīng)用開(kāi)發(fā)提供UIKit接口,包括了動(dòng)畫(huà)、布局、圖形轉(zhuǎn)換、事件處理,以及豐富的UI組件。組件內(nèi)部直接調(diào)用HAL接口,或者使用WMS(Window Manager Service)提供的客戶(hù)端與硬件交互,以完成事件響應(yīng)、圖像繪制等操作。目前只看到在L1有使用。
1.1 圖形子系統(tǒng)相關(guān)
- 《OpenHarmony 源碼解析之圖形子系統(tǒng) (一)》
- 《OpenHarmony 源碼解析之圖形子系統(tǒng)(UI)》
1.2 OpenHarmony 架構(gòu)圖

1.3 圖形子系統(tǒng)架構(gòu)圖

2 基礎(chǔ)知識(shí)
2.1 代碼目錄
- /foundation/graphic/ui
- ├── frameworks # 框架代碼
- │ ├── animator # 動(dòng)畫(huà)模塊
- │ ├── common # 公共模塊
- │ ├── components # 組件
- │ ├── core # ui主流程(渲染、任務(wù)管理等)
- │ ├── default_resource
- │ ├── dfx # 維測(cè)功能
- │ ├── dock # 驅(qū)動(dòng)適配層
- │ │ └── ohos # ohos平臺(tái)適配
- │ ├── draw # 繪制邏輯
- │ ├── engines # 繪制引擎
- │ │ ├── dfb
- │ │ ├── general
- │ │ ├── gpu_vglite
- │ │ └── software_zlite
- │ ├── events # 事件
- │ ├── font # 字體
- │ ├── imgdecode # 圖片管理
- │ ├── layout # 頁(yè)面布局
- │ ├── themes # 主題管理
- │ ├── window # 窗口管理適配層
- │ └── window_manager
- │ └── dfb
- ├── interfaces # 接口
- │ ├── innerkits # 模塊間接口
- │ │ └── xxx # 子模塊的接口
- │ └── kits # 對(duì)外接口
- │ └── xxx # 子模塊的接口
- ├── test # 測(cè)試代碼
- │ ├── framework
- │ │ ├── include # 測(cè)試框架頭文件
- │ │ └── src # 測(cè)試框架源碼
- │ ├── uitest # 顯示效果測(cè)試(可執(zhí)行程序在foundation/graphic/wms/test:sample_ui)
- │ │ └── test_xxx # 具體UI組件效果測(cè)試
- │ └── unittest # 單元測(cè)試
- │ └── xxx # 具體UI組件單元測(cè)試
- └── tools # 測(cè)試和模擬器工具(模擬器工程、資源文件)
- └── qt # QT工程
2.2 圖形組件一覽

3 實(shí)踐
3.1 UI控件效果
具體UI控件效果可以通過(guò)QT Creator運(yùn)行QT工程,效果如下:


所有UI控件在工程都可以找到效果,通過(guò)查看工程代碼可以了解到各控件的使用方式以及參數(shù)詳情。
3.2 示例
下面我們舉例UIButton解析控件的實(shí)現(xiàn):
構(gòu)造函數(shù)-參數(shù)
- UIButton::UIButton()
- : defaultImgSrc_(nullptr),
- triggeredImgSrc_(nullptr),
- currentImgSrc_(ButtonImageSrc::BTN_IMAGE_DEFAULT),
- imgX_(0),
- imgY_(0),
- contentWidth_(0),
- contentHeight_(0),
- state_(RELEASED),
- styleState_(RELEASED),
- #if DEFAULT_ANIMATION
- enableAnimation_(true),
- animator_(*this),
- #endif
- buttonStyleAllocFlag_(false)
設(shè)置Theme
- void UIButton::SetupThemeStyles()
- {
- Theme* theme = ThemeManager::GetInstance().GetCurrent();
- if (theme == nullptr) {
- buttonStyles_[RELEASED] = &(StyleDefault::GetButtonReleasedStyle());
- buttonStyles_[PRESSED] = &(StyleDefault::GetButtonPressedStyle());
- buttonStyles_[INACTIVE] = &(StyleDefault::GetButtonInactiveStyle());
- } else {
- buttonStyles_[RELEASED] = &(theme->GetButtonStyle().released);
- buttonStyles_[PRESSED] = &(theme->GetButtonStyle().pressed);
- buttonStyles_[INACTIVE] = &(theme->GetButtonStyle().inactive);
- }
- style_ = buttonStyles_[RELEASED];
- }
繪制OnDraw
- void UIButton::OnDraw(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea)
- {
- OpacityType opa = GetMixOpaScale();
- BaseGfxEngine::GetInstance()->DrawRect(gfxDstBuffer, GetOrigRect(), invalidatedArea, *buttonStyles_[state_], opa);
- DrawImg(gfxDstBuffer, invalidatedArea, opa);
- }
可以看到需要繪制2個(gè),第一個(gè)通過(guò)繪制引擎繪制點(diǎn)->DrawRect,
- void BaseGfxEngine::DrawRect(BufferInfo& dst,
- const Rect& rect,
- const Rect& dirtyRect,
- const Style& style,
- OpacityType opacity)
- {
- DrawRect::Draw(dst, rect, dirtyRect, style, opacity);
- }
第2個(gè)繪制圖片->DrawImg
- void UIButton::DrawImg(BufferInfo& gfxDstBuffer, const Rect& invalidatedArea, OpacityType opaScale)
- {
- const Image* image = GetCurImageSrc();
- if (image == nullptr) {
- return;
- }
- ImageHeader header = {0};
- image->GetHeader(header);
- Rect coords;
- Rect viewRect = GetContentRect();
- coords.SetLeft(viewRect.GetLeft() + GetImageX());
- coords.SetTop(viewRect.GetTop() + GetImageY());
- coords.SetWidth(header.width);
- coords.SetHeight(header.height);
- Rect trunc(invalidatedArea);
- if (trunc.Intersect(trunc, viewRect)) {
- image->DrawImage(gfxDstBuffer, coords, trunc, *buttonStyles_[state_], opaScale);
- }
- }
可以發(fā)現(xiàn)最終還是調(diào)用draw目錄下通過(guò)繪制點(diǎn)、線(xiàn)、圖片等等來(lái)進(jìn)行繪制:

事件處理
UIButton只重寫(xiě)了OnPressEvent,OnReleaseEvent和OnCancelEvent,增加了動(dòng)畫(huà),具體實(shí)現(xiàn)還是在基類(lèi)UIView,主要使用的函數(shù):
- void UIView::InvalidateRect(const Rect& invalidatedArea)
- {
- if (!visible_) {
- if (needRedraw_) {
- needRedraw_ = false;
- } else {
- return;
- }
- }
- Rect trunc(invalidatedArea);
- bool isIntersect = true;
- UIView* par = parent_;
- UIView* cur = this;
- while (par != nullptr) {
- if (!par->visible_) {
- return;
- }
- isIntersect = trunc.Intersect(par->GetContentRect(), trunc);
- if (!isIntersect) {
- break;
- }
- cur = par;
- par = par->parent_;
- }
- if (isIntersect && (cur->GetViewType() == UI_ROOT_VIEW)) {
- RootView* rootView = reinterpret_cast<RootView*>(cur);
- rootView->AddInvalidateRectWithLock(trunc, this);
- }
- }
在UIView里面可以發(fā)現(xiàn)還有很多事件比如:OnLongPressEvent,我們可以重寫(xiě)來(lái)自定義效果。
UIView重要函數(shù)說(shuō)明
(1) OnPreDraw——準(zhǔn)備繪制
(2) OnDraw——繪制動(dòng)作
(3) OnPostDraw——能在UI線(xiàn)程繪制
(4) Invalidate——請(qǐng)求重新繪制,有需要更新界面就可以調(diào)用此函數(shù)重新繪制
(5) Scale——縮放事件
(6) Translate——移動(dòng)事件
(7) OnPressEvent等——觸摸事件
還有很多常用的函數(shù),有興趣的同學(xué)可以自行查閱。
4 總結(jié)
到這我們對(duì)UI控件的一個(gè)使用和效果都有了解,底層實(shí)現(xiàn)流程也熟悉。不管是直接繪制還是通過(guò)繪制引擎繪制,最終還是調(diào)用draw目錄下的繪制函數(shù)。自定義控件我們可以繼承現(xiàn)有控件,擴(kuò)展實(shí)現(xiàn)效果,還可以直接繼承基類(lèi)UIView。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)