LVGL Pro 使用指南 [15] LVGL XML 控件

概述 链接到标题

控件 (Widgets) 是 LVGL 的主要构建块之一,与组件(Components)和屏幕(Screens)并列。控件的 XML 文件以<widget>作为根元素,支持以下子标签:

  • <consts>:定义常量
  • <api>:定义 API 接口
  • <styles>:定义样式
  • <view>:定义视图
  • <previews>:定义预览

控件可以嵌套在其他控件或组件中。控件不能直接从 XML 加载,但可以通过编写和注册简单的 XML 解析器来实现。

对于一般的界面开发用户,实现控件的场景并不多。同时绝大多数情况都只会使用<api>标签描述控件的接口,让 LVGL XML 可以通过接口使用控件的功能,而控件的解释器需要通过 C 语言来实现,这超出了本文的说明范围。基于以上因素,说明控件的 XML 的必要性并不大,这也是本系列文章接近尾声时才开始介绍控件的原因。

内置控件 链接到标题

LVGL 提供了许多内置的控件,例如:lv_slider、lv_label、lv_chart 等。在 LVGL 的仓库内有专门的 XML 文件来描述这些控件的接口: https://github.com/lvgl/lvgl/tree/master/xmls

这些 XML 文件会描述控件的接口,并注释如何使用,例如其中的lv_slider.xml文件:

<!--
Example
<lv_spinner anim_duration="1500" arc_sweep="90"/>
 -->

<widget>
    <api>
        <prop name="anim_duration" type="int" help="Set the animation time of the spinner."/>
        <prop name="arc_sweep" type="int" help="Set the animation arc length of the spinner. The animation is suited to values between 180 and 360."/>
    </api>
</widget>

控件的解析器代码放在:https://github.com/lvgl/lvgl/tree/master/src/xml

API 链接到标题

控件通过<api>标签描述其接口,该标签下主要通过<prop>(属性)子标签描述接口。

属性 链接到标题

属性是描述控件接口的核心部分,例如下面的示例表示该控件有一个 text 属性可以设置,类型是字符串:

<api>
    <prop name="text" type="string" help="Text of the label."/>
</api>

参数 链接到标题

属性可以由多个参数构成,例如:

<api>
    <prop name="bind_text" help="Bind a subject's value to a label.">
        <param name="bind_text" type="subject" help="Integer or string subject"/>
        <param name="fmt" type="string" help="Format string, e.g. %d °C "/>
    </prop>
</api>

名称与属性相同的参数可以直接引用。其他参数则使用"属性-参数(property-param)“的方式引用。未设置的参数的情况:

  • 使用默认值(如果已定义)
  • 类型特定的默认值(例如 0、false、NULL)

在解析器中,每个<prop>都映射到一个设置函数。同一属性下的<param>会被传递给同一个设置函数。

<enumdef> 链接到标题

<enumdef>仅用于控件,此标签用于为参数值定义枚举。例如:

<api>
    <enumdef name="my_widget_mode" help="Possible modes">
        <enum name="normal" help="Normal mode" value="0x10"/>
        <enum name="inverted" help="Inverted mode"/>
    </enumdef>
    <prop name="mode" help="Set Widget mode">
        <param name="mode" type="enum:my_widget_mode"/>
    </prop>
</api>

<element> 链接到标题

<element>仅用于控件,此标签用于定义子控件(sub-widgets)或内部结构(例如:图表序列、下拉列表、标签视图)。也常用于创建类似"插槽(slots)“的结构(类似 tabview 的 tab),例如窗口部件中的内容区和标题区,允许直接在其中创建子对象。

<element>包含<arg><prop>子标签:

  • <arg>是必需的,用于创建或获取 element 时传入
  • <prop>是可选的,映射到 setter 函数

下面是一个示例,用于定义一个可以动态添加到控件的"indicator”。它会创建my_indicator_t *类型的元素,类似于lv_chart_add_series的方式:

<api>
    <element name="indicator" type="my_indicator_t" help="The indicator of my_widget" access="add">
        <!-- args are passed when the element is created -->
        <arg name="color" type="color"/>
        <arg name="max_value" type="int"/>

        <!-- props can be set by setters at any time -->
        <prop name="value" type="int"/>
    </element>
</api>

Element 的 access 类型:

  • add:动态创建多个元素,例如 tabview 的 tabs
  • get:访问隐式创建的元素,例如 dropdown 的列表部分
  • set:访问按索引存在的部分,例如 table 的 cells
  • custom:映射自定义 C 函数到 XML,例如 bind_state_is_eq

add 与 get 类型的 element 都会返回一个对象,因此需要声明type。该type可以是任何自定义类型,例如type="my_data"。在导出的 C 代码中,返回值会保存在my_data_t *类型的变量中。如果type="lv_obj",该 element 允许拥有子控件或组件。

Element 在<view>中以<widget-element>的形式引用。名称部分使用-分隔。

注意:Element 只能在 XML 中定义其 API;实现必须写在 C 代码中。

access="add" 链接到标题

通过 add 创建的名为indicator的 Element:

<api>
    <element name="indicator" type="obj" help="The indicator of my_widget" access="add">
        <arg name="color" type="color"/>
        <arg name="max_value" type="int"/>
        <prop name="value">
            <param name="value" type="int"/>
        </prop>
    </element>
</api>

使用方法如下:

<my_widget width="100px">
    <my_widget-indicator name="indic1" color="0xff0000" max_value="120" value="30"/>
</my_widget>

通过 LVGL Pro Editor 会生成以下 add 原型,需要另外写 C 代码实现:

lv_obj_t * my_widget_add_indicator(lv_obj_t * parent, lv_color_t color, int32_t max_value);
void my_widget_set_indicator_value(lv_obj_t * obj, int32_t value);

access="get" 链接到标题

通过 get 创建的名为control_button的 Element:

<api>
    <element name="control_button" type="obj" help="A control button of my_widget" access="get">
        <arg name="index" type="int"/>
        <prop name="title" type="string"/>
    </element>
</api>

使用方法如下:

<my_widget width="100px">
    <my_widget-control_button name="btn1" index="3" title="Hello"/>
</my_widget>

通过 LVGL Pro Editor 会生成以下 get 原型,需要另外写 C 代码实现:

lv_obj_t * my_widget_get_control_button(lv_obj_t * parent, int32_t index);
void my_widget_set_control_button_title(lv_obj_t * obj, const char * text);

access="set" 链接到标题

通过 set 创建的名为item的 Element:

<api>
    <element name="item" type="obj" access="set">
        <arg name="index" type="int"/>
        <prop name="icon" type="img_src"/>
        <prop name="color" type="color"/>
    </element>
</api>

使用方法如下:

<my_widget width="100px">
    <my_widget-item index="3" icon_src="image1" color="0xff0000"/>
</my_widget>

通过 LVGL Pro Editor 会生成以下 set 原型,需要另外写 C 代码实现:

void my_widget_set_item_icon(lv_obj_t * parent, int32_t index, const void * icon_src);
void my_widget_set_item_color(lv_obj_t * parent, int32_t index, lv_color_t color);

access="custom" 链接到标题

通过 custom 创建的名为bind_color的 Element:

<element name="bind_color" access="custom">
    <arg name="subject" type="subject"/>
    <arg name="new_color" type="color"/>
    <arg name="ref_value" type="int"/>
</element>

使用方法如下:

<my_widget width="100px">
    <my_widget-bind_color subject="subject_1" color="0xff0000" ref_value="15"/>
</my_widget>

通过 LVGL Pro Editor 会生成以下自定义原型,需要另外写 C 代码实现:

void my_widget_bind_color(lv_obj_t * parent, lv_subject_t * subject, lv_color_t color, int32_t ref_value);

参考 链接到标题

https://docs.lvgl.io/master/xml/ui_elements/widgets.html https://docs.lvgl.io/master/xml/ui_elements/api.html