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了方法后就开始存在,直到程序结束咯?
***/

 

posted @ 2023-10-20 20:03  caesura_k  阅读(281)  评论(0编辑  收藏  举报