Build Your First App / 构建第一个 App

PixelUI 里的一个 App 是 IApplication 的派生类。ViewManager 负责构造、切换和销毁 App;App 本身负责画页面、处理没被 Widget 消耗的输入,并在生命周期回调中注册自己的 Widget 和回调。

1. 最小 App Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "core/app/IApplication.h"
#include "core/app/app_system.h"
#include "widgets/text_button/text_button.h"

class HelloApp final : public IApplication {
public:
HelloApp(PixelUI& ui, void*)
: ui_(ui), closeButton_(ui, 38, 36, 52, 16, "Back") {}

void onEnter(ExitCallback exitCallback) override {
// 这一行会保存 ViewManager 提供的退出回调。
IApplication::onEnter(exitCallback);

closeButton_.setCallback([this]() { requestExit(); });
closeButton_.onLoad();
ui_.addWidgetToFocusManager(&closeButton_);
ui_.markDirty();
}

void draw() override {
U8G2& display = ui_.getU8G2();
display.setFont(u8g2_font_6x10_tf);
display.drawStr(32, 20, "Hello PixelUI");
closeButton_.draw();
}

bool handleInput(InputEvent event) override {
if (event == InputEvent::BACK) {
requestExit();
return true;
}
return false;
}

private:
PixelUI& ui_;
TextButton closeButton_;
};

// 24 x 24 XBM 需要 24 * 3 = 72 字节。这里先用空图标占位。
static const uint8_t hello_icon_bits[72] = {};

AppItem helloApp = AppItem::make<HelloApp>("Hello", hello_icon_bits);

AppItem::make<T>() 默认要求 T(PixelUI&, void*) 这个构造函数。第二个参数是非拥有的启动参数,不需要时可以忽略它。

2. 生命周期

回调 何时发生 适合做什么
构造函数 App 在 arena 中创建 构造成员,不要把 this/成员指针发布给 manager
onEnter() App 成为顶层;如果有 fade,在 fade 完成后 设回调、调 onLoad()、注册 Widget,开始动画
onPause() 另一个 App 压到它上面 暂停 App 自己管理的工作
onResume() 上层 App 退出 恢复持续绘制等 App 级状态
onExit() 离开页面栈,销毁前 撤销 App 自己管理的外部状态
析构函数 onExit() 释放 App 内部资源

IApplication::onEnter(exitCallback) 不能漏掉,否则 requestExit() 没有可调用的退出函数。

3. 输入路由

ui.handleInput(event) 的顺序大致是:

1
PixelUI FocusManager -> active Popup -> top App

Widget 没有处理的事件才会落到 HelloApp::handleInput()。一般在这里处理 BACK 和属于整个页面的按键。

4. 注册和启动

1
2
3
4
5
6
7
8
AppManager::getInstance().registerApp(helloApp); // 注册

ViewManager& views = *ui.getViewManagerPtr();
const auto result = views.launch(helloApp, nullptr); // 启动

if (result != ViewManager::LaunchResult::Ok) {
show_launch_error(result);
}

不需要出现在 launcher 中的页面可以用 views.push<HelloApp>(ui, nullptr) 直接启动。两种启动方式和失败结果见App 生命周期与 ViewManager

5. 启动参数

1
2
3
4
5
6
7
8
9
10
11
12
13
struct HelloParameters {
const char* message;
};

class ParameterApp final : public IApplication {
public:
ParameterApp(PixelUI& ui, void* raw)
: ui_(ui), params_(static_cast<HelloParameters*>(raw)) {}

private:
PixelUI& ui_;
HelloParameters* params_; // non-owning
};

parameters 没有类型信息,也不延长对象寿命。它和 getCurrentApp() 返回的都是非拥有指针;若 App 要继续保存启动参数,目标对象必须比 App 活得更久。不要传入函数即将返回的局部变量地址。更通用的规则见回调与对象生命周期

最新文章
网站信息
文章数目 :
1
本站访客数 :
本站总浏览量 :
最后更新时间 :