【说在前面的话】
做过嵌入式UI的都知道,对一些素材(图片、按钮等)进行适当的排列布局后,会使得界面看起来整齐美观。今天讲的UI布局也是如此,比如让一个圆环显示在屏幕中央,如下图
我们需要设置固定的圆环坐标(x,y)的值来使圆环居于屏幕中央,这样带来的问题就是当屏幕为240*240时需要设置一个(x,y)坐标,当需求改变或者换了一块200*200像素的屏幕时,我们就需要手动更改(x,y)坐标来使圆环居于屏幕中央。
基于此种需求,Arm-2D为我们提供了一些布局的小工具,使得我们在更换不同大小的屏幕时,这些小工具会自动计算(x,y)坐标,让我们实现屏幕尺寸的自动适应,而不需要自己再手动计算坐标了(* ̄︶ ̄)
例如让圆环居于屏幕中央,就可以这样:
1、先获取到屏幕的大小,在Arm-2D中用canvas就可以
2、然后用居中的小工具(即arm_2d_align_centre)就可以获取到屏幕中央的区域坐标了
程序如下:
arm_2d_canvas(ptTile, _canvas) {
arm_2d_align_centre(_canvas, 100,100 ) {
arm_2d_rgb565_tile_copy_with_colour_keying_and_opacity(
&c_tilelogo1RGB565,
ptTile,
&__centre_region,
100,
(arm_2d_color_rgb565_t){GLCD_COLOR_BLACK});
}
}
在arm_2d_align_centre中,第一个参数就是获取到的屏幕大小,第2和第3个参数就是圆环素材的宽和高(屏幕大小改变而素材的宽高是不变的),根据这3个参数就可以计算出把圆环放到屏幕中央的坐标了(即__centre_region)
然后调用tile_copy函数在此区域进行绘制就可以了
这些布局小工具在Arm-2D集合 中的文章《还在手算坐标?试试Layout Assistant吧》已经介绍过了。
不过,今天要讲的状态栏的设计使用了一些新的布局小工具,涉及到的知识点也会重新简单介绍的(* ̄︶ ̄),大家如果没看过上面的文章,也可以接着往下看哦。
【状态栏】
那今天讲的状态栏是什么样的呢?
顾名思义,它就是用来显示软硬件(蓝牙、wifi等)中的一些状态,比如wifi连接成功显示白色图标,连接失败显示灰色图标。并且它要显示在屏幕的上面,如下图
那怎么能让它显示到屏幕的上方呢?
【布局小工具dock】
这就需要Arm-2D提供的布局小工具dock了。
获取屏幕上方的区域就用arm_2d_dock_top,它的使用方法如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
__top_region
}
}
__top_region就是描述了屏幕顶部 高度为 40 的一个区域(宽度就是屏幕的宽度),如下图
注意:当我们使用arm_2d_dock_top生成一个区域时,此时生成的区域名称就叫__top_region(也就是说它的名字是固定的)
同样的,Arm-2D还提供了获取屏幕左边、右边和下方区域的小工具,如下表所示
工具名称 | 对应区域名称 |
arm_2d_dock_bottom(__region, __height) | __bottom_region |
arm_2d_dock_left(__region, __width) | __left_region |
arm_2d_dock_right(__region, __width) | __right_region |
那我想获取屏幕中间的区域呢,比如高度为30,宽度为屏幕宽度的区域(如下图所示)该怎么办呢?
哈哈,这个也简单,贴心的官方也给我们提供了工具,如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_vertical( _canvas, 30 ) {
__vertical_region
}
}
__vertical_region就是我们想要的区域了
同样的,获取屏幕中间竖条区域的小工具也有,如下
arm_2d_dock_horizontal(__region, __width)
它对应的区域名称为__horizontal_region
好了,到这里,dock小工具就介绍完了,大家有没有发现dock工具的第一个参数都是一个区域,而这个区域不一定非得传屏幕大小的区域,也可以在屏幕中任意取一块区域传进去。因此dock的本质就是根据传人的区域计算出一块新区域,只不过传人屏幕大小的区域就可以自适应屏幕而已(* ̄︶ ̄)
由此,我们就可以实现dock的套娃模式了(因为传入的是区域,生成的也是区域),比如可以这样使用,如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
arm_2d_dock_vertical(__top_region,30) {
//__vertical_region
}
}
}
此时你知道最里面的__vertical_region是屏幕中的哪一片区域吗?
如果你和我一样有点晕,也不要急,我们可以调用fill_colour函数给这片区域填充一个颜色把它显示出来(这样就方便我们查看区域设置的正不正确),程序如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
arm_2d_dock_vertical(__top_region,30) {
arm_2d_fill_colour_with_opacity(
ptTile,
&__vertical_region,
(__arm_2d_color_t){GLCD_COLOR_WHITE},
100);
}
}
}
这个套娃的区域如下图
怎么样,和你想得一样吗(* ̄︶ ̄)
【绘制状态栏】
我们今天绘制的状态栏就是上面的套娃区域,然后把一个个小图标(30*30像素)绘制在此区域即可。
那么问题又来了,怎么在此区域中绘制图标呢?
此时,就需要使用另一个小工具线性流式布局(Line Stream Layout)了。它就可以按顺序一个接一个地将图标放置在指定区域内。
它的使用方法如下
arm_2d_layout(__vertical_region) {
__item_line_dock_horizontal( <width> ) {
//__item_region
}
__item_line_dock_horizontal( <width> ) {
//...
}
//...
}
首先把图标绘制的区域传给arm_2d_layout
然后用__item_line_dock_horizontal就可以获取到每个图标的区域(__item_region),它需要传入的参数为图标的宽度
要绘制几个图标就用几个__item_line_dock_horizontal,他会帮我们依次计算出绘制小图标的区域
这样,绘制状态栏的图标就简单了,程序如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
arm_2d_dock_vertical(__top_region,30) {
arm_2d_layout(__vertical_region) {
//绘制wifi图标
__item_line_dock_horizontal(30) {
arm_2d_fill_colour_with_mask_and_opacity(
ptTile,
&__item_region,
&c_tileWiFiMask,
(__arm_2d_color_t){GLCD_COLOR_WHITE},
250);
}
//绘制蓝牙图标
__item_line_dock_horizontal(30) {
arm_2d_fill_colour_with_mask_and_opacity(
ptTile,
&__item_region,
&c_tileBlueToothMask,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
250);
}
//...
}
}
}
}
是不是很简单。
此时,如果你想实现下面的需求,即蓝牙连接设备成功,就绘制蓝牙图标,未连接到设备就不绘制蓝牙图标,如下图所示
大家是不是发现未画蓝牙图标时,中间两个图标的间隔变大而显得很不美观,这个也就是固定坐标带来的弊端,不过好在我们使用了线性流式布局,使得这种问题再也不会出现了。代码修改也很简单,只需要添加一个控制显示的变量就可以,需要绘制就赋值为true,否则为false。
修改后的代码如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
arm_2d_dock_vertical(__top_region,30) {
arm_2d_layout(__vertical_region) {
//绘制wifi图标
__item_line_dock_horizontal(30) {
arm_2d_fill_colour_with_mask_and_opacity(
ptTile,
&__item_region,
&c_tileWiFiMask,
(__arm_2d_color_t){GLCD_COLOR_WHITE},
250);
}
//绘制蓝牙图标
if(blue_tooth_flag){
__item_line_dock_horizontal(30) {
arm_2d_fill_colour_with_mask_and_opacity(
ptTile,
&__item_region,
&c_tileBlueToothMask,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
250);
}
}
//...
}
}
}
}
只在15行添加了一个if判断语句就可以了
最后运行的效果如下所示
怎么样,相信大家已经get到布局小工具的妙用了。
【线性流式布局二】
上面的例子我们只是简单使用了下线性流式布局,其实它还有别的用法。
那就是通过__item_line_dock_horizontal宏我们还可以指定当前元素与上下左右之间的间隔,其语法如下:
arm_2d_layout(<the target region: arm_2d_region_t>) {
/* Syntax 1 */
__item_line_dock_horizontal(<width>,
[, <left>, <right>, <top>, <bottom>]) {
//...
}
/* more of the __item_line_dock_horizontal segments */
//...
}
举例如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
arm_2d_dock_vertical(__top_region,30) {
arm_2d_layout(__vertical_region) {
__item_line_dock_horizontal(30,10,0,0,0) {
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
__item_line_dock_horizontal(20,10,10,5,5) {
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
__item_line_dock_horizontal(30) {
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
//...
}
}
}
}
为了方便讲解,我们只是在区域中填充了颜色。这个程序绘制的区域如下图所示
第1个区域我们设置width=30,相邻间距只设置了,left=10,其他都为0
第2个区域我们设置width=20,相邻间距left和right为10,up和bottom为5。这样就把一个边长为20的区域放到了中间。
第3个区域只设置了width=30
注意:当设置上下左右相邻之间的间隔时,四个位置必须同时指定,且顺序不能改变。(即第1个区域虽然只设置了left,但是其他3个0必须写而不能省略)
同样的,我们有横向的线性流式布局,那有没有纵向的呢?
答案是肯定的。
纵向的线性流式布局(Vertical Line Stream Layout)与横向的线性流式布局(Horizontal Line Stream Layout)类似,其语法如下:
arm_2d_layout(<the target region: arm_2d_region_t>) {
__item_line_dock_vertical( <height>
[, <left>, <right>, <top>, <bottom>]) {
// ...
}
/* more of the __item_line_vertical segments */
//...
}
举例如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_left( _canvas, 50 ) {
arm_2d_layout(__left_region) {
__item_line_dock_vertical(30,10,10,50,10){
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
__item_line_dock_vertical(30){
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
}
}
}
第1个区域我们设置height=30,相邻间距left和right为10,up=50和bottom=10。
第2个区域直接设置height=30
这两个布局区域如下图所示
第2个区域由于我们只设置了height=30,所以它的宽度还是__left_region的宽度50
那我们想让它变成宽度为30的区域该怎么办呢?
聪明的你很快就想到了,设置间距left和right为10不就可以了,没错,是这样的,程序如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_left( _canvas, 50 ) {
arm_2d_layout(__left_region) {
__item_line_dock_vertical(30,10,10,50,10){
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
__item_line_dock_vertical(30,10,10,0,0){
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_BLUE},
100);
}
}
}
}
这个程序的布局区域如下图所示
到这里,你以为线性流式布局的用法就讲完了吗?
no!no!no!
其实它还有一种用法哦!
那就是__item_line_dock_horizontal() 以及 __item_line_dock_vertical() 括号里的参数是可以省略的,表示占用剩下所有面积区域。
那这种用法有什么用呢?
这个非常适合类似MDK Project View占用左边的纵向空间,右边的就是用剩下的所有空间作为编辑区。
好,那我们就简单把屏幕分成左右两个区域,程序如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_layout(_canvas) {
__item_line_dock_horizontal(50){
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_RED},
100);
}
__item_line_dock_horizontal(){
arm_2d_fill_colour_with_opacity(
ptTile,
&__item_region,
(__arm_2d_color_t){GLCD_COLOR_GREEN},
100);
}
}
}
第1个区域我们设置width=50
第2个dock_horizontal()函数没有传参数
这个代码划分的区域如下图所示
怎么样,是不是很简单。
好了,到这里,dock的线性流式布局就介绍完了,大家赶快动手试试把~~~///(^v^)\\\~~~
【套娃模式】
有了布局小工具dock,相信大家在为界面布局时也能得心应手了。因为它很容易就可以把屏幕分成几块,然后分别在分好的区域中绘制图案就可以了。如下图,把屏幕分成3个区域
第1个区域用arm_2d_dock_top就可以获取到
第2个区域先使用arm_2d_dock_bottom获取下面的区域,然后对获取的__bottom_region使用arm_2d_dock_left就可以了
同样的,第3个区域先用dock_bottom获取,再对此区域使用dock_right即可
由于dock工具区域支持套娃模式,我们可以对2、3区域再进行划分,如下图
这样我们很容易就把屏幕划分成若干个区域了。
不过,说起套娃模式,还有一个注意点需要和大家说一下,
那就是使用相同的dock工具进行套娃时,如下所示
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 40 ) {
arm_2d_dock_top( __top_region, 30 ) {
//。。。
}
}
}
这样挨着同时使用两个dock_top,建议最好不要这样用(当然这样用也是没问题的)
这个也很简单,因为两个dock_top嵌套,其实就相当于使用里面的dock_top获取的区域一样,根本没必要使用两个哦!!
上面使用两个top想获取的区域其实就用一个即可,程序如下
arm_2d_canvas(ptTile, _canvas) {
arm_2d_dock_top( _canvas, 30 ) {
//。。。
}
}
哈哈,你现在应该明白了吧~~~///(^v^)\\\~~~
【dock布局综合例子】
最后,在简单制作一个例子来结束今天的文章。
我们就用arm-2d官方demo中的arm_2d_scene_fan来举例。
它的界面如下所示
就是一个旋转的小风扇,并显示温度。
那现在就开始分析一下它的布局。
首先,小风扇和温度值所需要的区域为140*200,我们把这个区域放到屏幕中间,如下图所示
程序也很简单,如下
arm_2d_canvas(ptTile, __top_canvas) {
arm_2d_align_centre(__top_canvas, 140, 200) {
//。。。
}
}
然后用线性流式布局把这个居中的区域分成两块,如下图
上面140*140的区域用来风扇旋转,而剩下的区域则用来显示温度,程序也很简单,如下
arm_2d_canvas(ptTile, __top_canvas) {
arm_2d_align_centre(__top_canvas, 140, 200) {
arm_2d_layout(__centre_region) {
__item_line_dock_vertical(140) {
//绘制旋转的风扇
}
__item_line_dock_vertical() {
//绘制温度值
}
}
}
}
怎么样,是不是很简单。
不过,虽然简单,但是用了布局小工具之后,我们就很容易适配不同大小的屏幕了。比如上面的视频中用的屏幕像素为240*320的,但是如果你换成240*240的屏幕也可以运行,不需要再修改代码哦。
有小伙伴又要说了,那适配240*135的屏幕呢?
哈哈哈。。。
这个屏幕当然也可以,但是一点代码都不修改是做不到的。
那接下来就一起适配这个屏幕,看看到底需要修改多少代码。
首先,由于我们的屏幕高度只有135,所以竖着显示显然是不行,那我们就改成横着的,把之前的140*200的区域改为200*135
修改后的代码如下
arm_2d_align_centre(__top_canvas, 200, 135) {
arm_2d_layout(__centre_region) {
//。。。
}
}
}
既然要横着显示了,那肯定__item_line_dock_vertical要改为__item_line_dock_horizontal了。
修改后的布局代码如下
arm_2d_canvas(ptTile, __top_canvas) {
arm_2d_align_centre(__top_canvas, 200, 135) {
arm_2d_layout(__centre_region) {
__item_line_dock_horizontal(135) {
//绘制旋转的风扇
}
__item_line_dock_horizontal() {
//绘制温度值
}
}
}
}
这样,我们的布局就修改好了。唯一的问题就是把风扇旋转的区域140*140改成了135*135,这样把区域缩小后会导致风扇显示不全。
不过,不要急,先看一下风扇旋转的代码,如下
使用的是transform函数进行旋转的,而transform函数本身就支持缩放。
哈哈哈,你是不是已经明白了,
只要把缩放倍数1.001f改为0.8f就好了,修改后的代码如下
到这里就把240*135的屏幕适配好了,是不是很简单。其实这个也相当于把一个竖屏显示的布局改成了横屏布局,其修改的内容也不多~~~///(^v^)\\\~~~
好了,那我们就看看修改后的代码运行效果,如下
哈哈,各位看官,看到这里还不赶快试试。
这个demo的添加也很简单,如下
然后在弹出的对话框中选择Fan即可,如下
添加成功就会在工程中出现fan.c文件了,如下
它的使用也很简单,程序如下
int main(void)
{
system_init();
disp_adapter0_init();
__arm_2d_scene_fan_init( &DISP0_ADAPTER,NULL);
//。。。
}
首先添加头文件arm_2d_scene_fan.h
然后调用初始化函数即可。是不是太简单了!
好了,到这里今天的内容就讲完了。欢迎大家和我一起玩转Arm-2D,我们的口号是:一起玩,一起玩才更好玩。下期精彩继续(基于高斯模糊的特效设计)。。。
原创不易,如果你喜欢我的公众号、觉得我的文章对你有所启发,
请务必“点赞、收藏、转发”,这对我很重要,谢谢!
欢迎订阅 嵌入式小书虫