Harmony:NativeUI界面添加
1 NativeAbilityFwk框架
对于hi3321芯片的brandy sdk而言,ui的开发是基于NativeAbilityFwk应用框架;
NativeAbilityFwk是基于穿戴产品的特点从鸿蒙原生Native中裁剪定制的轻量级UI;
JSAbility由JavaScript编写;通过ACE注册到AMS中管理,先放着;
NativeAbility由C++编写;通过LauncherAbility注册到AMS中管理;NativeAbility只有一个,可以管理多个Slice的生命周期;
2 添加sliceID
添加slice的时候,首先为待添加页面定义一个id,在NativeAbility中通过ID来管理和切换页面和视图;
//need_add //AppViewIDs.h typedef enum : uint16_t { VIEW_INVALID = 0, VIEW_MAIN = 1, //... VIEW_MAX_INTER_APP, } AppViewId;
3 添加slice信息
添加slice的时候,需要将slice信息添加到NativeAbility中管理;
3.1 NativeAbilityFwk的入口函数
这部分是程序ui入口执行的逻辑流,不用修改,只是为了结构清晰;
//don't need add; //NativeAbility.cpp void NativeAbility::InstallAbility(void){ InstallNativeAbility(nullptr, this); NativeLauncher::GetInstance().InitNativeLauncher(); return; } //NativeLauncher.cpp void NativeLauncher::InitNativeLauncher() { GraphicStartUp::InitFontEngine(MEM_POOL_UI_FONT, MEM_POOL_UI_FONT_SIZE,\ OHOS::VECTOR_FONT_DIR, DEFAULT_VECTOR_FONT_FILENAME); InitBtHeadsetEvent(); //cae:把每个initSlices函数都调用了一遍,把创建的实例和ID对应上 InitAllNativeSlices(); CallerLogModel::GetInstance()->InitPhoneService(); PresenterTask::GetInstance()->Init(); PlayersModel::GetInstance()->AudioInit(); ActivityModel::GetInstance()->Init(); ScreenModels::GetInstance()->InitCardConfig(); VocassistModel::GetInstance()->Init(); HeartModel::GetInstance()->Init(); BluetoothModel::GetInstance()->Init(); ScreenModels::GetInstance()->PreLoadAppimages(); NativeAbility::GetInstance().SetDefaultSliceId(VIEW_MAIN_SAMPLE); } void NativeLauncher::InitAllNativeSlices(void){ uint16_t size = sizeof(SLICE_MAPPER) / sizeof(SLICE_MAPPER[0]); for (int index = 0; index < size; index++) { if ((SLICE_MAPPER[index].id >= VIEW_MAX_INTER_APP)) continue; //cae:将SLICE_MAPPER[]中的每个地址以函数指针的形式调用; ( this->*(SLICE_MAPPER[index].func) )(); } return; }
3.2 添加slice字符串ID
添加slice的时候,为页面添加对应的字符串ID;
//need add //NativeLauncher.cpp 感觉作用不大,先放着; struct ViewIdToString{ AppViewId id; char idForUikit[256]; }; static const ViewIdToString VIEW_MAPPER[] = { {VIEW_MAIN, "main"}, {VIEW_SETTING, "setting"}, //... //attention: static const data; };
3.3 添加slice的实例化地址
添加slice的时候,为页面添加对应的实例化函数地址;
至此slice在程序启动后执行到此,将slice实例添加进NativeAbility中管理;添加slice结束,后面是编写slice模块;
//need add //NativeLauncher.cpp struct AppViewMapper{ AppViewId id; void (NativeLauncher::*func)(void); }; static const AppViewMapper SLICE_MAPPER[] = { {VIEW_MAIN, &NativeLauncher::InitMainSlice}, {VIEW_SETTING, &NativeLauncher::InitSettingSlice}, //... //attention: static const data; }; //new一个模块实例,将实例同ID对应上,添加到NativeAbility; void NativeLauncher::InitMainSlice(void){ SliceProxy *mainSlice = new AbilitySliceProxy<MainView, MainPresenter>; if (mainSlice != nullptr) { NativeAbility::GetInstance().AddSliceProxy(VIEW_MAIN, mainSlice); } } //需要将上面实例化函数用到的VIEW_MAIN、MainView、MainPresenter所在的xx.h添加到当前文件中; //编写VIEW_MAIN slice,就是编写 MainView.h、 MainPresenter.h 以及 MainView.cpp、MainPresenter.cpp;
4 slice页面构建
slice页面构建使用mvp (model view presenter)框架来构建;
框架原理详见Sparta OpenHarmony Native Slice开发用户指南;
这些mvp模块,更应该将其理解为是一种属性和结构;只有创建了他们的存储实例化后,才是具体的对象;(重要)
4.1 model模块
用于获取数据;目前只是创建页面,暂时不用先放着;
4.2 presenter模块
用于处理页面监听,数据交互;需要用户实现如下4个生命周期接口;
#ifndef SLEEPDETAIL_PRESENTER_H #define SLEEPDETAIL_PRESENTER_H #include "Presenter.h" #include "SleepDetailView.h" namespace OHOS { class SleepDetailView; class SleepDetailPresenter : public Presenter<SleepDetailView>, public UIView::OnClickListener, public UIView::OnDragListener { public: SleepDetailPresenter(); virtual ~SleepDetailPresenter(); void OnStart() override; void OnPause() override; void OnResume() override; void OnStop() override; static SleepDetailPresenter *GetInstance(); private: //因为继承了OnClickListener,所以有OnClick属性,至于参数,原函数也这样直接继承; bool OnClick(UIView &view, const ClickEvent &event) override; bool OnDrag(UIView &view, const DragEvent &event) override; }; } //namespace OHOS #endif //SLEEPDETAIL_PRESENTER_H
#include "AppViewIDs.h" #include "NativeAbility.h" #include "SleepDetailPresenter.h" namespace OHOS{ //当前Presenter属性实例化的内存指针 static SleepDetailPresenter *g_pSleepDetailPresenter = nullptr; SleepDetailPresenter::SleepDetailPresenter(){ g_pSleepDetailPresenter = this; } SleepDetailPresenter::~SleepDetailPresenter(){ g_pSleepDetailPresenter = nullptr; } SleepDetailPresenter *SleepDetailPresenter::GetInstance(){ return g_pSleepDetailPresenter; } void SleepDetailPresenter::OnStart(){ Presenter<SleepDetailView>::OnStart(); } void SleepDetailPresenter::OnPause(void){ Presenter<SleepDetailView>::OnPause(); } void SleepDetailPresenter::OnResume(void){ Presenter<SleepDetailView>::OnResume(); } void SleepDetailPresenter::OnStop(void){ Presenter<SleepDetailView>::OnStop(); } bool SleepDetailPresenter::OnDrag(UIView &view, const DragEvent &event){ if (event.GetDragDirection() == DragEvent::DIRECTION_LEFT_TO_RIGHT) { NativeAbility::GetInstance().ChangeSlice(VIEW_APPLIST); } return true; } bool SleepDetailPresenter::OnClick(UIView &view, const ClickEvent &event){ // 根据控件id查找是哪个控件触发了点击回调 // if (strcmp(view.GetViewId(), 控件ID) == 0) return true; } }
4.3 view模块
用于视图构建和显示;需要用户实现如下四个生命周期接口;
#ifndef SLEEPDETAIL_VIEW_H #define SLEEPDETAIL_VIEW_H #include "BaseView.h" #include "View.h" #include "components/ui_view_group.h" #include "components/ui_label.h" #include "components/ui_image_view.h" namespace OHOS { class SleepDetailPresenter; class SleepDetailView : public View<SleepDetailPresenter>{ public: SleepDetailView(); virtual ~SleepDetailView(); static SleepDetailView *GetInstance(void); void OnStart() override; void OnSetUpView() override; void OnTearDownView() override; void OnStop() override; private: UIView::OnDragListener *draglistener{nullptr}; void InitImage(void); void InitLable(void); //以下控件属性通过new实例化后,add到对应root属性上; UIViewGroup *container_ = nullptr; //container属性指针 UILabel *titleLabel{nullptr}; //label属性指针 UIImageView *icon{nullptr}; //image属性指针 }; } //namespace OHOS #endif //SLEEPDETAIL_PRESENTER_H
#include "SleepDetailView.h" #include "SleepDetailPresenter.h" #include "common/image_cache_manager.h" #include "UiConfig.h" #include "ui_resource_image.h" namespace OHOS{ static SleepDetailView *g_pSleepDetailView{nullptr}; SleepDetailView::SleepDetailView(){ g_pSleepDetailView = this; } SleepDetailView::~SleepDetailView(){ container_->RemoveAll(); delete container_; container_ = nullptr; if (titleLabel != nullptr){ } if (icon != nullptr){ delete icon; icon = nullptr; } g_pSleepDetailView = nullptr; } SleepDetailView *SleepDetailView::GetInstance(){ return g_pSleepDetailView; } void SleepDetailView::InitLable(){ //label属性 new titleLabel = new UILabel(); //label属性 扩展 titleLabel->SetPosition(62, 288, 369, 200); titleLabel->SetFont(DEFAULT_VECTOR_FONT_FILENAME, 30U); titleLabel->SetText("No record.wear watch to monitor sleep quality."); titleLabel->SetStyle(STYLE_TEXT_COLOR, Color::Gray().full); //label属性 add container_->Add(titleLabel); } void SleepDetailView::InitImage(){ //image属性 new icon = new UIImageView(); //image属性 扩展 ImageInfo *imageInfo = ImageCacheManager::GetInstance().LoadOneInMultiRes(SSLEEP_SCREMINDSLEEP_396X292, SSLEEP); if (imageInfo == nullptr){ return; } icon->SetPosition(35, 17, 396, 292); icon->SetSrc(imageInfo); //image属性 add container_->Add(icon); } void SleepDetailView::OnStart(){ //container_容器属性 new container_ = new UIViewGroup(); if (container_ == nullptr){ return; } //container_容器属性 扩展 container_->SetPosition(0, 0); container_->SetWidth(Screen::GetInstance().GetWidth()); container_->SetHeight(Screen::GetInstance().GetHeight()); draglistener = static_cast<UIView::OnDragListener *>(SleepDetailPresenter::GetInstance()); if (draglistener == nullptr){ return; } container_->SetOnDragListener(draglistener); container_->SetTouchable(true); container_->SetDraggable(true); InitImage(); InitLable(); //container_容器属性 add RootView::GetInstance()->Add(container_); //label、image >> container_ ; //container_ >> 当前RootView ; } void SleepDetailView::OnSetUpView(){ View<SleepDetailPresenter>::OnSetUpView(); Draw(); } void SleepDetailView::OnTearDownView(){ View<SleepDetailPresenter>::OnTearDownView(); return; } void SleepDetailView::OnStop(){ View<SleepDetailPresenter>::OnStop(); RootView::GetInstance()->RemoveAll(); return; } }
5 CmakeList增加
在对应目录下的cmakelist中添加xx.cpp路径,以及头文件路径;
如果编译有报错,改一下报错;页面添加流程至此完成;
如果是因为内存不够想要注释掉界面,那么只要在该界面的InitxxSlice函数中,注释掉new Slice即可;
如果是因为makefile list太长,想要注释掉界面可以反推如何注释呀;
在makefile中把xx.c和include目录注释掉,那么哪里用到了xx.c和xx.h呢?可不就是InitxxSlice函数new的时候需要头文件和源文件么。
那么哪里调用了InitxxSlice函数呢?倒推全部注释掉即可;
6 先放着
/*** 一开始的问题: slice的内存是在new该slice方法的时候分配的;new的时候顺便调用构造函数; 那么问题来了: slice的内存在delete的时候释放的么? delete的时候顺便调用析构函数? 我没有看到delete slice的;在onStop切换时,会调用析构函数,然后delete自身内存么? 大约因为slice由NativeAbility管理,所以slice的方法都继承自AbilitySlice.h中的方法; Slice1在切换的时候,会自动调用slice1的onStop()方法; 如果没有重写slice1的onStop()的话,那么就会继承AbilitySlice.h中的onStop()如下: //AbilitySlice.h void OnStop() override { if (view_) { view_->OnStop(); delete view_; //这里调用view_的析构函数,然后delete view_ 的内存? view_ = nullptr; } if (presenter_) { presenter_->OnStop(); delete presenter_; presenter_ = nullptr; } } 合理的解释是每次onstop的时候都把slice的view视图给清空释放了,但是slice的内存还是在的; 其实这些slice从一开始Initxx new的时候,new了内存一直没释放; onstop没有删除自身,它会调用继承父类的onstop函数,清除slice上的控件内存,但是slice还是存在的; 整个ability框架中的slice内存从Initxx函数中new了方法后就开始存在,直到程序结束咯? ***/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具