Components¶
Overview¶
Components are one of the main building blocks for creating new UI elements.
<component>
s support the following child XML tags:
Although they can't contain C code, they are very powerful:
They can extend another Component or Widget (the base can be defined)
Components can be built from Widgets and other Components
A custom API can be defined
Local styles can be defined, and the global styles can be used
Local constants can be defined, and the global constants can be used
Function calls, subject changes, or screen load/create events can be added. See XML Events
Previews can be defined to preview the components in various settings
Unlike Widgets (which are always compiled into the application), Components can either:
be loaded at runtime from XML, or
be exported to C code and compiled with the application.
Usage from XML¶
In XML Files¶
Using Components in XMLs is very intuitive. The name of the components can be used as XML tag
in the <view>
of other Components, Screens, and Widgets.
The Component properties are just XML attributes.
To load Components from file, it's assumed that the XML files are saved to the device either as data (byte array) or as file. Once the data is saved, each component can be registered, and instances can be created after that.
<!-- my_button.xml -->
<component>
<view extends="lv_button" flex_flow="row">
<lv_image src="logo"/>
<my_h3 text="Title"/>
</view>
</component>
Styles, Constants, and custom API can also be described in the XML files.
Registration¶
Once a Component is created (e.g., my_button
), it can be registered by calling either:
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml")
lv_xml_component_register_from_data("my_button", xml_data_of_my_button)
These registration functions process the XML data and store relevant information internally. This is required to make LVGL recognize the Component by name.
Note that the "A:" in the above path is a file system "driver identifier letter" from File System (lv_fs_drv) and used accordingly. See that documentation for details.
When loaded from a file, the file name is used as the Component name.
During registration, the <view>
of the Component is saved in RAM.
Instantiation¶
After registration, a new instance of any registered Component can be created with:
lv_obj_t * obj = lv_xml_create(lv_screen_active(), "my_button", NULL);
The created Widget is a normal LVGL Widget that can be used like any other manually-created Widget.
The last parameter can be NULL
or an attribute list, like this:
/* Can be local */
char * my_button_attrs[] = {
"x", "10",
"y", "-10",
"align", "bottom_left",
NULL, NULL,
};
lv_obj_t * btn1 = lv_xml_create(lv_screen_active(), "my_button", my_button_attrs);
Usage from Exported Code¶
From each Component XML file, a C and H file is exported with a single function inside:
lv_obj_t * component_name_create(lv_obj_t * parent, ...api properties...);
where 'component_name' (in the function above) is replaced by the Component's XML file name.
When a Component is used in another Component's XML code and the code is exported,
this create
function will be called. This means that Components do not have a
detailed set/get API but can be created with a fixed set of parameters.
If the user needs to access or modify values dynamically, it is recommended to use a Data bindings via Subject.
The user can also call these ..._create()
functions at any time from application code
to create new components on demand.
Extending¶
Additionally, when a Component is created, it can use the extended Widget's attributes
(see <view extends="...">
in the code examples below).
This means that Components inherit the API of the extended Widget as well.
Custom Properties¶
The properties of child elements can be adjusted, such as:
<my_button x="10" width="200"/>
However, it's also possible to define custom properties in the <api>
tag.
The properties then can be passed to any properties of the children by
referencing them by $
. For example:
<!-- my_button.xml -->
<component>
<api>
<prop name="btn_text" type="string"/>
</api>
<view extends="lv_button">
<lv_label text="$btn_text"/>
</view>
</component>
And it can be used like
<!-- my_list.xml -->
<component>
<view>
<my_button btn_text="First"/>
<my_button btn_text="Second"/>
<my_button btn_text="Third"/>
</view>
</component>
In this setup, the btn_text
property is mandatory, however it can be made optional
by setting a default value:
<prop name="btn_text" type="string" default="Title"/>
See <api> for more details and XML Syntax for all the supported types.
Examples¶
The following example demonstrates parameter passing and the use of the
text
label property on a Component. Styles and Constants are also shown.
<!-- h3.xml -->
<component>
<view extends="lv_label" style_text_color="0xffff00"/>
</component>
<!-- red_button.xml -->
<component>
<api>
<prop type="string" name="btn_text" default="None"/>
</api>
<consts>
<int name="thin" value="2"/>
</consts>
<styles>
<style name="pressed_style" border_width="#thin" border_color="0xff0000"/>
</styles>
<view extends="lv_button" style_radius="0" style_bg_color="0xff0000">
<style name="pressed_style" selector="pressed"/>
<h3 text="Some text"/>
<h3 text="$btn_text" y="40"/>
</view>
</component>
lv_xml_component_register_from_file("A:path/to/h3.xml");
lv_xml_component_register_from_file("A:path/to/red_button.xml");
/* Creates a button with "None" text */
lv_xml_create(lv_screen_active(), "red_button", NULL);
/* Use attributes to set the button text */
const char * attrs[] = {
"btn_text", "Click here",
NULL, NULL,
};
lv_xml_create(lv_screen_active(), "red_button", attrs);
Live Example¶
Load components at runtime¶
C code
View on GitHub#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_XML
void lv_example_xml_1(void)
{
/*A red button created from builti-in LVGL widgets
*It has an API parameter too to change its text.*/
const char * red_button_xml =
"<component>"
" <api>"
" <prop name=\"button_text\" type=\"string\" default=\"None\"/>"
" </api>"
" <view extends=\"lv_button\" radius=\"0\" style_bg_color=\"0xa91500\">"
" <lv_label text=\"$button_text\" align=\"center\"/>"
" </view>"
"</component>";
/*The card is just an lv_obj where a label and two red buttons are used.
* Its API allow setting a title (label test) and the action (the text of a button)*/
const char * card_xml =
"<component>"
" <api>"
" <prop name=\"title\" type=\"string\" default=\"Hello world\"/>"
" <prop name=\"action\" type=\"string\"/>"
" </api>"
" <view width=\"200\" height=\"content\">"
" <lv_label text=\"$title\" align=\"top_mid\"/>"
" <red_button y=\"20\" align=\"top_left\" button_text=\"Cancel\"/>"
" <red_button y=\"20\" align=\"top_right\" button_text=\"$action\"/>"
" </view>"
"</component>";
/* Motor card is a special case of a card where the title and action are already set*/
const char * motor_card_xml =
"<component>"
" <view extends=\"card\" title=\"Motor start?\" action=\"Start\">"
" </view>"
"</component>";
/*Register all the custom components*/
lv_xml_component_register_from_data("red_button", red_button_xml);
lv_xml_component_register_from_data("card", card_xml);
lv_xml_component_register_from_data("motor_card", motor_card_xml);
lv_obj_t * card;
/*Create a card with the default values*/
card = (lv_obj_t *) lv_xml_create(lv_screen_active(), "card", NULL);
/*Create a motor card too. The returned value can be adjusted freely*/
card = (lv_obj_t *) lv_xml_create(lv_screen_active(), "motor_card", NULL);
lv_obj_set_y(card, 90);
/*Pass properties to a card*/
const char * attrs[] = {
"y", "180",
"action", "Apply",
"title", "New title",
NULL, NULL,
};
card = (lv_obj_t *) lv_xml_create(lv_screen_active(), "card", attrs);
}
#endif
C code
View on GitHub#include "../../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_XML && LV_USE_TRANSLATION
void lv_example_xml_2(void)
{
lv_result_t res;
res = lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_h3.xml");
if(res != LV_RESULT_OK) {
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "Couldn't open the XML files.");
lv_obj_center(label);
return;
}
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_card.xml");
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml");
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/view.xml");
lv_xml_translation_register_from_file("A:lvgl/examples/others/xml/translations.xml");
lv_xml_register_font(NULL, "lv_montserrat_18", &lv_font_montserrat_18);
lv_translation_set_language("de");
lv_obj_t * obj = (lv_obj_t *) lv_xml_create(lv_screen_active(), "view", NULL);
lv_obj_set_pos(obj, 10, 10);
lv_xml_component_unregister("my_button");
const char * slider_attrs[] = {
"x", "200",
"y", "-15",
"align", "bottom_left",
"value", "30",
NULL, NULL,
};
lv_obj_t * slider = (lv_obj_t *) lv_xml_create(lv_screen_active(), "lv_slider", slider_attrs);
lv_obj_set_width(slider, 100);
}
#endif