跳转至

UIKit 入门与 UIView 基础

本文档用于在 HiDiTing V100 上开发基于 UIKit 的 C++ 图形界面。文档先说明图形子系统、组件关系和真实应用页面的实现流程,再保留各类组件、资源、表盘、旋转和 DFX 的详细说明,供开发者按需查阅。

UIKit 背景知识

图形子系统与组件关系

UIKit是基于轻量OS构建的图形子系统,满足硬件资源较小的物联网设备的系统应用开发,其核心模块如图1所示。

图 1 图形子系统架构图

图形子系统架构图

  • 各模块介绍

    • Component:应用组件,包括UIView、UIViewGroup、UIButton、UILabel、UILabelButton、UIList、UISlider等。
    • Animator:动画模块,开发者可以自定义动画。
    • Layout:布局控件,包括Flexlayout、GridLayout、ListLayout等。
    • Transform:图形变换模块,包括旋转、平移、缩放等。
    • DFX:维测模块。
    • Font:字体模块,包括字库管理及多语言切换等。
    • ImageCache:图片缓存模块,包括图片加载和释放功能以及LRU自动回收机制等。
    • Event:事件模块,包括click、press、drag、long press等基础事件。
    • Render Engine:渲染绘制模块。
    • Gralloc Engine:图形内存管理模块。
    • Display Device:图形显示管理模块。
    • Input Device:输入设备管理模块。
    • Display Vau:图形硬件渲染适配层。
    • Display Gralloc:图形内存管理适配层。
    • Display Layer:图层管理适配层。
    • Input Manager:输入设备管理适配层。
  • 目录

    /src/middleware/services/gui  # UIKit 原生与增强实现
    ├── hal
    │   ├── display      # 显示相关功能的HAL层
    │   └── input        # 输入相关功能的HAL层
    └── uikit
         ├── ui       # UI模块,包括UI控件、动画、字体等功能
         ├── utils    # 图形基础库及南向适配层
         ├── proprietary  # 自研代码,包括新增控件及定制增强特性,以及graphic service服务等
         └── sample       # uikit演示用例
    

快速跑通 UIKit Demo

功能说明

本节使用 SDK 已有的 HelloWorld 页面作为最小 UIKit 场景。该页面已纳入 diting-communitynativelauncher 组件:HelloWorldPresenter.cpp 通过 REGIST_MENU 注册应用菜单,HelloWorldPage.cpp 通过 REGIST_SLICE_PAGE 注册页面;进入页面时创建容器和标签并显示 Hello World

用户界面工具包运行流程

准备工作

说明:本文示例命令以一站式 CLI 为主。实际开发可从以下三种环境中选择,推荐使用一站式 CLI。

开发环境 适用场景 使用指南
一站式 CLI(推荐) 快速完成目标选择、构建、烧录和串口监视 一站式 CLI 开发环境使用指南
HiSpark Studio for VS Code 图形化编辑、编译、烧录和调试 HiSpark Studio for VS Code 开发环境使用指南
WSL 与 Docker 在 Windows 上使用一致的 Linux 容器构建环境 WSL 与 Docker 环境使用指南

一站式 CLI 开发环境使用指南 准备环境、构建和烧录;构建前确保 fbb doctor 成功。

3322 diting-community 的目标配置已经启用 SDK_DITING_COMMUNITYSUPPORT_OHOSFWKENABLE_UIKITCONFIG_NATIVEAPP_TEST。相关定义位于 /src/build/config/target_config/3322/config.py,无需为本示例额外打开 UIKit 组件。

使用方式

构建并烧录默认 diting-community 固件后,打开设备应用菜单,选择 HelloWorld。页面应显示居中的 Hello World 文本;返回或退出页面后,页面析构函数会释放容器、标签和该页面加载的图片资源。

涉及接口

接口或类 用途 参考
UIViewGroup 页面内的容器,管理子视图 AddRemoveAll
UILabel 显示文本 SetTextSetAlignSetFont
UIView 所有组件的基类,提供位置与尺寸控制和事件属性 SetPosition(位置和尺寸)SetDraggableSetTouchable
ImageCacheManager 加载和释放多分辨率图片资源 LoadAllInMultiRes(文件)UnloadAllInMultiRes(文件)
Native 页面框架 将 UIKit 页面接入 Native 应用框架 AddViewToPageContainerREGIST_SLICE_PAGEREGIST_MENU

预期结果

  • 应用菜单中出现 HelloWorld
  • 进入后显示 Hello World 标签,文本使用默认矢量字体并水平、垂直居中。
  • 页面退出后没有重复加载资源、悬空视图或重复注册页面的日志。

文件结构与代码走读

文件 作用
/src/application/wearable/nativeapp/nativeui/helloworldpage/HelloWorldPresenter.cpp REGIST_MENU 向应用菜单注册 HelloWorld
/src/application/wearable/nativeapp/nativeui/helloworldpage/HelloWorldPage.cpp 注册页面、创建组件、加载资源和释放资源
/src/application/wearable/nativeapp/nativeui/include/helloworldpage/HelloWorldPage.h 页面类声明和组件成员所有权
/src/application/wearable/nativeapp/nativelauncher/include/AppViewIDs.h 应用视图 ID 枚举,包含 VIEW_HELLOWORLD
/src/application/wearable/nativeapp/CMakeLists.txt 递归收集 nativeui/*.cpp 并构建 nativelauncher

HelloWorldPresenter.cpp 的核心是:

REGIST_MENU(VIEW_HELLOWORLD, HelloWorldView, HelloWorldPresenter,
    EMPTY_ICON, EMPTY_ICON, "HelloWorld");

该注册使启动框架能够在应用菜单中找到对应 View、Presenter 和标题,宏定义见 REGIST_MENUHelloWorldPage.cpp 再将视图 ID、页 ID 和页面类关联:

REGIST_SLICE_PAGE(VIEW_HELLOWORLD, HELLOWORLD_PAGE, HelloWorldPage, true);

HelloWorldPage::OnStart() 是实际绘制流程:先使用 ImageCacheManager::LoadAllInMultiRes() 加载页面资源;随后创建 UIViewGroupUILabel,调用 SetPosition()SetDraggable()SetTouchable()SetAlign()SetFont()SetText() 设置标签,最后执行 UIViewGroup::Add()AddViewToPageContainer()。析构函数按相反方向 RemoveAll()、删除对象、UnloadAllInMultiRes(),避免页面切换后资源泄漏。REGIST_SLICE_PAGE 的定义见 /src/application/wearable/nativeabilityfwk/include/SlicePageFactory.h

基于 UIKit Demo 开发自己的应用

新建页面

  1. /src/application/wearable/nativeapp/nativeui 下新建页面源文件目录,并在 nativeui/include 下新建对应头文件目录。
  2. /src/application/wearable/nativeapp/nativelauncher/include/AppViewIDs.h 中增加唯一的 VIEW_MY_APP 枚举值。
  3. 仿照 HelloWorld 创建 MyAppViewMyAppPresenter 和继承 SlicePage<MyAppPresenter>MyAppPage;在 Presenter 中注册菜单,在页面源文件中注册首页面。
  4. OnStart() 中创建组件树;在析构函数或页面退出回调中按创建的反向顺序移除视图、释放资源。
REGIST_MENU(VIEW_MY_APP, MyAppView, MyAppPresenter,
    EMPTY_ICON, EMPTY_ICON, "MyApp");
REGIST_SLICE_PAGE(VIEW_MY_APP, 1, MyAppPage, true);

关键代码片段

void MyAppPage::OnStart(void *data)
{
    auto *container = new UIViewGroup();
    container->SetPosition(0, 0, HORIZONTAL_RESOLUTION, VERTICAL_RESOLUTION);

    auto *label = new UILabel();
    label->SetPosition(20, 120, 360, 48);
    label->SetAlign(TEXT_ALIGNMENT_CENTER, TEXT_ALIGNMENT_CENTER);
    label->SetText("My UIKit App");

    container->Add(label);
    AddViewToPageContainer(container);
}

示例仅展示组件树构建顺序。实际页面必须保存 containerlabel 的成员指针,并在析构时先从容器移除子视图,再释放对象;涉及图片时,同时配对调用资源加载和卸载接口。

构建与验证

nativelauncher 的 CMake 已递归收集 nativeui/*.cpp,因此新增页面源文件无需单独添加 CMake 源文件列表;但新增头文件路径应遵循现有 nativeui/include 组织。重新配置并构建固件后,在应用菜单确认 MyApp 出现,进入后检查组件显示、按键/触摸响应和退出后的资源释放。

UIKit 组件与功能详细说明

API Reference 入口

下文保留各组件的功能、参数和效果说明。需要查阅完整声明、继承关系和单个接口约束时,可按组件类别进入 API Reference;组件表中的函数名应以这些接口声明和对应 SDK 头文件为准。

组件类别 主要用途 API Reference
视图树与事件 UIViewUIViewGroupRootView,用于布局、容器管理和输入事件分发 UIViewUIViewGroupRootView
布局与滚动容器 Grid/Flex/List 布局,滚动、滑动和列表场景 布局UIListUIScrollView
文本、按钮和图片 标签、按钮、图片显示和交互 UILabelUIButtonUIImageView
绘制与资源 Canvas 绘制、字体和图片缓存 UICanvas字体管理ImageCacheManager
动画、DFX 与服务 动画、帧率/截图/控件树和 Graphic Service AnimatorDFX 命令Graphic Service

组件继承关系

所有组件均继承于UIView,组件分为基础组件和容器组件。

  • 基础组件仅实现组件本身单一功能,例如UIButton、UILabel、UIImageView等。
  • 容器类组件可以添加其他组件作为自身的子组件,例如RootView、UIList、UIPicker等。

本章主要介绍各个组件的使用,并以sample的形式呈现,指导用户开发。

图 1 组件继承关系图

**图 1** 组件继承关系图

UIView和UIViewGroup

UIView为所有组件的父类,所有组件都继承自UIView,实现自定义扩展。例如UIViewGroup继承于UIView,并扩展其容器特性(允许添加其他组件)。

下面分别介绍了UIView和UIViewGroup的常用函数,同时也是所有派生组件的公共函数。

UIView函数介绍

UIView常用函数如表1所示,用户可以结合实际情况使用。

表 1 UIView常用函数说明

函数 介绍
virtual bool OnPreDraw(Rect& invalidatedArea) const 在绘制视图之前调用。该函数用于检查失效区域是否可以被该视图完全覆盖,以优化绘制过程。自定义的派生类按需重写该函数,实现定制化逻辑。
virtual void OnDraw(const Rect& invalidatedArea) 通过调用当前OnDraw函数来绘制。自定义的派生类按需重写该函数,实现定制化逻辑。
virtual void OnPostDraw(const Rect& invalidatedArea) 调用OnDraw函数后的调用该函数。自定义的派生类按需重写该函数,实现定制化逻辑。
virtual void ReMeasure() 重新测量视图大小。
void Invalidate() 刷新视图的区域。
void InvalidateRect(const Rect& invalidatedArea) 刷新视图的指定区域。
void SetParent(UIView* parent) 设置视图的父视图。
UIView* GetParent() const 获取视图的父视图。
void SetNextSibling(UIView* sibling) 设置视图的下一个同级视图。
UIView* GetNextSibling() const 获取视图的下一个同级视图。
virtual void SetVisible(bool visible) 设置视图是否可见。
bool IsVisible() const 获取视图是否可见。
void SetTouchable(bool touch) 设置视图是否可触摸。
bool IsTouchable() const 获取视图是否可触摸。
void SetDraggable(bool draggable) 设置视图是否可拖动。
注意:可拖动的前提为可触摸,仅这两个属性均被设置为true时,才能支持拖动交互。
bool IsDraggable() const 获取视图是否可拖动。
void SetDragParentInstead(bool dragParentInstead) 设置拖动视图时是否将拖动事件传递给父视图进行处理。
注意:在嵌套场景中,如果父子控件都需要响应拖动事件,子控件该属性应当确保已经被设置为false,否则子控件的拖拽事件会被传递到父控件,导致子控件的拖拽事件不响应。
bool IsDragParentInstead() const 获取是否拖动事件会传递给父视图进行处理。
Rect GetRect() const 获取视图的绝对矩形区域,如经仿射变换,则为变换后的有效区域。
Rect GetVisibleRect() const 获取可见的绝对矩形区域。
Rect GetMaskedRect() const 获取有效的绝对矩形区域。
Rect GetOrigRect() const 获取控件设置的绝对矩形区域。
virtual Rect GetContentRect() 获取控件内容的绝对矩形区域,不包含padding和border。
Rect GetRelativeRect() const 获取相对于父视图的相对矩形区域。
void ResizeVisibleArea(int16_t x, int16_t y, int16_t width, int16_t height) 调整可见区域的大小。此操作可能会影响最终显示大小。
virtual void SetWidth(int16_t width) 设置视图的宽度。
virtual void SetHeight(int16_t height) 设置视图的高度。
virtual void SetWidthPercent(float widthPercent) 按照相对于父视图的百分比来设置宽度。
virtual void SetHeightPercent(float heightPercent) 按照相对于父视图的百分比来设置高度。
virtual void Resize(int16_t width, int16_t height) 调整当前视图大小。
virtual void SetPosition(int16_t x, int16_t y) 设置当前视图的起始点坐标。
virtual void SetPosition(int16_t x, int16_t y, int16_t width, int16_t height) 设置视图的X、Y坐标以及宽高。
virtual void SetPositionPercent(float xPercent, float yPercent, float widthPercent, float heightPercent) 按照父视图的百分比来设置X、Y坐标以及宽高。
virtual void ResizePercent(float widthPercent, float heightPercent) 按照父视图的百分比来设置宽高。
virtual void SetX(int16_t x) 设置视图左上点的X坐标。
virtual void SetXPercent(float xPercent) 按照相对于父视图的百分比来设置X坐标。
int16_t GetX() const 获取视图的X坐标。
virtual void SetY(int16_t y) 设置视图左上点的Y坐标。
virtual void SetYPercent(float yPercent) 按照相对于父视图的百分比来设置Y坐标。
int16_t GetY() const 获取视图的Y坐标。
bool IsViewGroup() const 当前视图是否包含其他视图。
void SetIntercept(bool isIntercept) 设置是否拦截事件。仅在未注册对应事件监听器时生效:

• 如果值为true,则不会将事件传递到父视图。
• 如果值为false,则会将事件传递到父视图。 已注册对应事件监听器时,则根据回调函数返回值判断(同上)是否已经消费事件而不传递给父视图。
bool IsIntercept() 获取是否拦截事件。
void SetTransformMap(const TransformMap& transMap) 设置视图的仿射变换。
TransformMap& GetTransformMap() 获取视图的仿射变换。
void SetViewId(const char* id) 给视图设置ID。
const char* GetViewId() const 获取视图的ID。
void SetViewIndex(int16_t index) 设置视图下标。
int16_t GetViewIndex() const 获取视图下标。
virtual UIViewType GetViewType() const 获取视图类型。
void SetOpaScale(uint8_t opaScale) 设置视图的不透明度,合法值为[0,255]。
uint8_t GetOpaScale() const 获取视图的不透明度。
ViewExtraMsg* GetExtraMsg() 获取视图的额外信息。
void SetExtraMsg(ViewExtraMsg* extraMsg) 设置视图的额外信息。
void Rotate(int16_t angle, const Vector2& pivot) 旋转该视图。

• 参数angle:旋转角度。
• 参数pivot:旋转中心点。
void Rotate(int16_t angle, const Vector3& pivotStart, const Vector3& pivotEnd) 旋转该视图。

• 参数angle:旋转角度。
• 参数pivotStart:旋转起点。
• 参数pivotEnd:旋转终点。
void Scale(const Vector2& scale, const Vector2& pivot) 缩放该视图。

• 参数scale:缩放系数(x, y)。
• 参数pivot:缩放中心点。
void Scale(const Vector3& scale, const Vector3& pivot) 缩放该视图。

• 参数scale:缩放系数(x, y, z)。
• 参数pivot:缩放中心点。
void Translate(const Vector2& trans) 平移该视图。

• 参数trans:平移系数(x, y)。
void Translate(const Vector3& trans) 平移该视图。

• 参数trans:平移系数(x, y, z)。
void SetCameraDistance(int16_t distance) 设置视点距离(三维空间透视投影z轴方向)。

• 参数distance:视点距离。
void SetCameraPosition(const Vector2& position) 设置视点位置(三维空间透视投影x、y平面位置)

• 参数position:视点位置。
void Shear(const Vector2& shearX, const Vector2& shearY, const Vector2& shearZ) 切变该视图。

• 参数shearX:x轴切变参数。
• 参数shearY:y轴切变参数。
• 参数shearZ:z轴切变参数。
void SetOnDragListener(OnDragListener* onDragListener) 设置拖动事件监听。
void SetOnClickListener(OnClickListener* onClickListener) 设置点击事件监听。
void SetOnLongPressListener(OnLongPressListener* onLongPressListener) 设置长按事件监听。
void SetOnRotateListener(OnRotateListener* onRotateListener) 设置旋转表冠事件监听。仅通过RequestFocus()函数成功获焦的控件,才能触发该回调监听函数。
void SetOnTouchListener(OnTouchListener* onTouchListener) 设置触摸事件监听。
virtual bool OnLongPressEvent(const LongPressEvent& event) 回调长按事件监听,通过返回值判断是否继续上报。
virtual bool OnDragStartEvent(const DragEvent& event) 回调拖动开始事件监听,通过返回值判断是否继续上报。
virtual bool OnDragEvent(const DragEvent& event) 回调拖动中事件监听,通过返回值判断是否继续上报。
virtual bool OnDragEndEvent(const DragEvent& event) 回调拖动结束事件监听,通过返回值判断是否继续上报。
virtual bool OnClickEvent(const ClickEvent& event) 回调点击事件监听,通过返回值判断是否继续上报。
virtual bool OnPressEvent(const PressEvent& event) 回调触摸事件监听,通过返回值判断是否继续上报。
virtual bool OnCancelEvent(const CancelEvent& event) 回调触摸取消事件监听,通过返回值判断是否取消上报。
virtual bool OnReleaseEvent(const ReleaseEvent& event) 回调触摸释放事件监听,通过返回值判断是否继续上报。
virtual bool OnRotateStartEvent(const RotateEvent& event) 回调旋转表冠开始事件监听,仅获焦视图可响应此事件。
virtual bool OnRotateEvent(const RotateEvent& event) 回调旋转表冠事件监听,仅获焦视图可响应此事件。
virtual bool OnRotateEndEvent(const RotateEvent& event) 回调旋转表冠结束事件监听,仅获焦视图可响应此事件。
void SetBackgroundBlur(float radius) 对最上层可见的静态控件设置背景高斯模糊。
参数radius:模糊半径。内部调参值,并非指半径内的区域才会模糊,此值决定不同程度的高斯模糊效果,合法值为[0,32]。
void ClearBackgroundBlur() 清除控件设置的背景高斯模糊效果。
void SetBlurPath(uint32_t cmdNum, const uint8_t cmd, uint32_t dataNum, const float data) 通过设置矢量路径进行高斯模糊。
void ClearBlurPath() 清除高斯模糊所用的矢量路径。
void SetFullScreenBlur(bool isFull) 设置高斯模糊为全屏高斯模糊(默认为局部高斯模糊)。
bool IsFullScreenBlur() const 获取高斯模糊状态。
void SetSpeLightStyle(const LightSpeStyle& speLightStyle) 设置反射光照参数,目前反射光效果仅支持圆形或扇形区。
LightSpeStyle GetSpeLightStyle() 调用获取反射光照参数。
void SetAmbLightStyle(float ambAmp) 设置环境基础光照值,可调节控件所在的矩形区域的亮度。
float GetAmbLightStyle() 获取环境基础光照值。
void EnableLight(bool enable) 使能控件的光照设置。
bool IsEnableLight() 获取光照设置是否使能。
virtual void RequestFocus() 使控件获焦。
virtual void ClearFocus() 使控件失焦。
virtual void SetFocusable(bool focusable) 设置控件能否获焦。
virtual bool IsFocusable() const 获取控件能否获焦。
void AlignToReference(AlignmentType alignType, UIView* referView, int16_t ofsX = 0, int16_t ofsY = 0) 将控件与referView进行对齐,alignType是对齐方式,具体请查看表2,ofsX和ofsY是相对于referView的对齐后的偏移设置。

表 2 AlignmentType说明

枚举值

介绍

ALIGN_DEFAULT

默认值,不对齐

ALIGN_TOP_LEFT

控件左上角对齐到父容器内容区左上角

ALIGN_TOP_MID

控件顶部中点对齐到父容器内容区顶部中点

ALIGN_TOP_RIGHT

控件右上角对齐到父容器内容区右上角

ALIGN_MID_LEFT

控件右下角对齐到父容器内容区右下角

ALIGN_MID_MID

控件右边缘中点对齐到父容器内容区右边缘中点

ALIGN_MID_RIGHT

控件中心对齐到父容器内容区中心

ALIGN_BOTTOM_LEFT

控件左下角对齐到父容器内容区左下角

ALIGN_BOTTOM_MID

控件底部中点对齐到父容器内容区底部中点

ALIGN_BOTTOM_RIGHT

控件右下角对齐到父容器内容区右下角

ALIGN_OUT_TOP_LEFT

控件左下角对齐到参考对象左上角

ALIGN_OUT_TOP_MID

控件底部中点对齐到参考对象顶部中点

ALIGN_OUT_TOP_RIGHT

控件右下角对齐到参考对象右上角

ALIGN_OUT_BOTTOM_LEFT

控件左上角对齐到参考对象左下角

ALIGN_OUT_BOTTOM_MID

控件顶部中点对齐到参考对象底部中点

ALIGN_OUT_BOTTOM_RIGHT

控件右上角对齐到参考对象右下角

ALIGN_OUT_LEFT_TOP

控件右上角对齐到参考对象左上角

ALIGN_OUT_LEFT_MID

控件右侧中点对齐到参考对象左侧中点

ALIGN_OUT_LEFT_BOTTOM

控件右下角对齐到参考对象左下角

ALIGN_OUT_RIGHT_TOP

控件左上角对齐到参考对象右上角

ALIGN_OUT_RIGHT_MID

控件左侧中点对齐到参考对象右侧中点

ALIGN_OUT_RIGHT_BOTTOM

控件左下角对齐到参考对象右下角

说明: 全屏高斯模糊只支持1个控件持有,非全屏高斯模糊支持多个控件持有。如果高斯模糊在特定的模糊半径下的显示效果有方块感,建议添加蒙板来解决。

UIView的监听器介绍

UIView中定义了多种监听器,使用方式基本相同。用户需实例化监听器或者继承监听器,实现抽象函数,在执行具体操作时,会回调监听器中对应的回调函数。用户可以在回调函数中添加需要执行的逻辑。

下面分别介绍了UIView中定义的监听器。

UIViewGroup介绍

UIViewGroup继承于UIView,常用函数如表1所示。

表 1 UIViewGroup核心函数

函数 介绍
virtual void Add(UIView* view) 添加一个子视图。如果入参视图已经有父视图,则无法加入。
virtual void Insert(UIView prevView, UIView insertView) 在prevView视图后插入一个子视图。
virtual void Remove(UIView* view) 从当前视图树中移除指定子视图,非释放子视图。
virtual void RemoveAll() 从当前视图树中移除所有子视图,非释放子视图。
virtual void GetTargetView(const Point& point, UIView** last) override 获取可见的、最上层的、可以响应指定坐标点的触摸事件的目标子视图。
virtual void GetTargetView(const Point& point, UIView current, UIView target) override 获取一个可见的、最上层的、可以响应指定坐标点的触摸事件的目标子视图(current)。获取一个最上层的视图包含指定的坐标点(target)。
virtual void MoveChildByOffset(int16_t x, int16_t y) 移动所有子视图。
UIView* GetChildrenHead() const 获取此视图组中的最前一个子视图。
UIView* GetChildrenTail() const 获取此视图组中的最后一个子视图。
UIView GetChildById(const char id) const 通过校验id,获取对应子视图。
void SetDisallowIntercept(bool flag) 设置是否拦截触摸事件至子视图。
virtual UIView GetChildById(const char id) const override 根据指定的id获取子控件。
void SetAutoSize(bool state) 设置该视图是否适应子控件的尺寸:自调整为能够包含所有子视图的尺寸。