4、窗口管理:创建窗口、设置窗口属性(大小、位置、透明度)、窗口类型(应用窗口、背景窗口、覆盖窗口)
窗口管理,说白了就是告诉Screen系统:「嘿,给我一块画布,我要在上面画东西。」
这块画布怎么摆、多大、能不能让后面的东西透出来,都由你来定。我在项目中见过不少新手,上来就急着画图,结果窗口属性没设对,画面要么跑偏,要么叠得乱七八糟。嗯,咱们先把窗口管好,再谈渲染。
4.1 创建窗口:你的第一块画布
创建窗口的核心API就一个——screen_create_window()。但别小看它,参数里藏着不少门道。
screen_window_t win;
int rc = screen_create_window(&win, screen_ctx);
if (rc != 0) {
// 处理错误,我习惯直接assert或者打日志
}
第一个参数是窗口句柄,第二个是之前创建的context。创建成功后,win就是一个「空壳子」——它还没有大小、没有位置、没有类型。你得一步步给它「穿衣服」。
ENOMEM,可能是显存不够了。我曾经在低端硬件上踩过这个坑,后来加了个重试机制才搞定。
4.2 设置窗口属性:大小、位置、透明度
窗口属性通过screen_set_window_property_iv()来设置。注意那个「iv」后缀——它表示设置的是整型数组。Screen里大部分属性都用整型表示,包括坐标、尺寸、透明度。
4.2.1 大小和位置
int pos_size[4] = {100, 200, 800, 480}; // x, y, width, height
screen_set_window_property_iv(win, SCREEN_PROPERTY_POSITION, pos_size);
screen_set_window_property_iv(win, SCREEN_PROPERTY_SIZE, &pos_size[2]);
这里有个细节:SCREEN_PROPERTY_POSITION和SCREEN_PROPERTY_SIZE是分开设置的。为什么不分一个接口搞定?我猜是为了灵活性——有时候你只想改位置,不想动大小。
坐标原点在屏幕左上角,x向右为正,y向下为正。嗯,和大多数图形系统一样。
4.2.2 透明度
透明度用SCREEN_PROPERTY_ALPHA控制,取值范围0~255。0是全透明,255是不透明。
int alpha = 128; // 半透明
screen_set_window_property_iv(win, SCREEN_PROPERTY_ALPHA, &alpha);
SCREEN_FORMAT_RGBX8888,那alpha值会被忽略。必须用SCREEN_FORMAT_RGBA8888才行。我曾经在这个问题上浪费了半天,画面死活透不出来,最后发现是格式选错了。
4.3 窗口类型:三种核心角色
Screen把窗口分成几种类型,每种类型决定了窗口在合成时的行为。最常用的三种是:应用窗口、背景窗口、覆盖窗口。
| 窗口类型 | 枚举值 | 典型用途 | Z序位置 |
|---|---|---|---|
| 应用窗口 | SCREEN_APPLICATION_WINDOW |
普通App界面 | 中间层 |
| 背景窗口 | SCREEN_BACKGROUND_WINDOW |
壁纸、桌面背景 | 最底层 |
| 覆盖窗口 | SCREEN_OVERLAY_WINDOW |
状态栏、弹窗、HUD | 最顶层 |
4.3.1 应用窗口(Application Window)
这是最常用的类型。你的主界面、二级页面,都用它。
int type = SCREEN_APPLICATION_WINDOW;
screen_set_window_property_iv(win, SCREEN_PROPERTY_TYPE, &type);
应用窗口之间可以互相遮挡,谁在上面取决于创建顺序或者你手动调整的Z序。我一般用SCREEN_PROPERTY_ZORDER来控制层级,数值越大越靠上。
4.3.2 背景窗口(Background Window)
背景窗口永远在最底层。它通常用来显示壁纸或者纯色背景。
int type = SCREEN_BACKGROUND_WINDOW;
screen_set_window_property_iv(win, SCREEN_PROPERTY_TYPE, &type);
你想想看,如果所有窗口都是应用窗口,那最底下的窗口被挡住的部分就浪费了。背景窗口的存在,就是为了让合成器知道:「这个窗口不需要和其他窗口做复杂的混合计算,直接垫底就行。」
SCREEN_FORMAT_RGBX8888就够了。这样可以减少显存带宽消耗。我在做车载仪表盘时,背景窗口直接用了纯色填充,连buffer都省了。
4.3.3 覆盖窗口(Overlay Window)
覆盖窗口永远在最顶层。它适合做状态栏、通知弹窗、音量调节条这类需要「浮」在所有界面之上的东西。
int type = SCREEN_OVERLAY_WINDOW;
screen_set_window_property_iv(win, SCREEN_PROPERTY_TYPE, &type);
覆盖窗口有个特点:它不参与常规的Z序排序。不管你创建多少个覆盖窗口,它们都在应用窗口之上。多个覆盖窗口之间,可以用Z序来调整相对位置。
4.4 窗口属性的完整设置流程
把上面这些串起来,一个典型的窗口初始化流程是这样的:
// 1. 创建窗口
screen_window_t win;
screen_create_window(&win, screen_ctx);
// 2. 设置类型
int type = SCREEN_APPLICATION_WINDOW;
screen_set_window_property_iv(win, SCREEN_PROPERTY_TYPE, &type);
// 3. 设置位置和大小
int pos[2] = {100, 200};
int size[2] = {800, 480};
screen_set_window_property_iv(win, SCREEN_PROPERTY_POSITION, pos);
screen_set_window_property_iv(win, SCREEN_PROPERTY_SIZE, size);
// 4. 设置透明度(如果需要)
int alpha = 255;
screen_set_window_property_iv(win, SCREEN_PROPERTY_ALPHA, &alpha);
// 5. 设置像素格式
int format = SCREEN_FORMAT_RGBA8888;
screen_set_window_property_iv(win, SCREEN_PROPERTY_FORMAT, &format);
// 6. 创建buffer(下一章会详细讲)
screen_create_window_buffers(win, 2);
嗯,到这里,窗口的「骨架」就搭好了。接下来就可以往里面填充内容了。
4.5 小结
窗口管理是Screen编程的第一步,也是最容易出错的一步。我个人建议:
- 先确定窗口类型,再设其他属性。类型决定了行为,改起来牵一发动全身。
- 透明度别乱用,除非你真的需要半透明效果。每个带alpha的窗口都会增加合成器的计算量。
- 调试时,先检查窗口属性是否设置成功。我经常用
screen_get_window_property_iv()把属性读回来验证一遍。
下一章,我们会深入buffer管理——窗口有了,数据怎么往里塞?到时候见。