续接上一篇文章,分析LVGL的demo源码。官方的widget例程代码lv_demo_widgets()函数体如下,下面逐步分析。虽然之前也简单看过一遍LVGL官方教程,但对于这些复杂的结构,还是结合代码看清晰一点。这里的策略是,对于复杂的结构和源码可以不求甚解,先大概浏览一遍,有个印象。然后再慢慢迭代。而且最终的目标也并不是再写一套LVGL出来,所以主要目的还是会用即可。
lv_demo_widgets()函数总接口:
void lv_demo_widgets(void)
{
if(LV_HOR_RES <= 320) disp_size = DISP_SMALL;
else if(LV_HOR_RES < 720) disp_size = DISP_MEDIUM;
else disp_size = DISP_LARGE; //是根据显示屏的参数确定显示器大小分类
font_large = LV_FONT_DEFAULT; //指定字体
font_normal = LV_FONT_DEFAULT;
lv_coord_t tab_h; //tabwiew控件的标签区高度,根据显示屏大小,确定此参数
if(disp_size == DISP_LARGE) {
tab_h = 70;
#if LV_FONT_MONTSERRAT_24
font_large = &lv_font_montserrat_24;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_24 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_16
font_normal = &lv_font_montserrat_16;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_16 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
} else if(disp_size == DISP_MEDIUM) {
tab_h = 45;
#if LV_FONT_MONTSERRAT_20
font_large = &lv_font_montserrat_20;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_20 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_14
font_normal = &lv_font_montserrat_14;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_14 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
} else { /* disp_size == DISP_SMALL */
tab_h = 45;
#if LV_FONT_MONTSERRAT_18
font_large = &lv_font_montserrat_18;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_18 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
#if LV_FONT_MONTSERRAT_12
font_normal = &lv_font_montserrat_12;
#else
LV_LOG_WARN("LV_FONT_MONTSERRAT_12 is not enabled for the widgets demo. Using LV_FONT_DEFAULT instead.");
#endif
}
#if LV_USE_THEME_DEFAULT
lv_theme_default_init(NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), LV_THEME_DEFAULT_DARK, font_normal); //初始化默认主题
#endif
lv_style_init(&style_text_muted);
lv_style_set_text_opa(&style_text_muted, LV_OPA_50); //设定50%的不透明率
lv_style_init(&style_title); lv_style_set_text_font(&style_title, font_large); //设定字体
lv_style_init(&style_icon);
lv_style_set_text_color(&style_icon, lv_theme_get_color_primary(NULL)); //设置颜色
lv_style_set_text_font(&style_icon, font_large); //设置字体
lv_style_init(&style_bullet);
lv_style_set_border_width(&style_bullet, 0); //设置边宽
lv_style_set_radius(&style_bullet, LV_RADIUS_CIRCLE); //设置半径,传入'LV_RADIUS_CIRCLE'智能判断绘制为圆形
tv = lv_tabview_create(lv_scr_act(), LV_DIR_TOP, tab_h); //新创建一个tabview控件
lv_obj_set_style_text_font(lv_scr_act(), font_normal, 0); //设置最底层字体,上层控件不特定设置则用此字体
if(disp_size == DISP_LARGE) {
lv_obj_t * tab_btns = lv_tabview_get_tab_btns(tv); //获取tabview的标签按钮
lv_obj_set_style_pad_left(tab_btns, LV_HOR_RES / 2, 0); //设置标签按钮位置,从左侧看一半的位置开始
lv_obj_t * logo = lv_img_create(tab_btns); //在标签按钮控件内创建一个image
LV_IMG_DECLARE(img_lvgl_logo); //宏展开为:extern lv_img_dsc_t img_lvgl_logo; 声明外部图片数组
lv_img_set_src(logo, &img_lvgl_logo); //设置图片数据到image控件。“例程的图片和字体都是取模加入源码的,后面自己可以尝试从SD卡中导入图片和中文字体等”
lv_obj_align(logo, LV_ALIGN_LEFT_MID, -LV_HOR_RES / 2 + 25, 0); //设定logo位置
lv_obj_t * label = lv_label_create(tab_btns); //在标签按钮上创建一个label
lv_obj_add_style(label, &style_title, 0); //选定显示风格
lv_label_set_text(label, "LVGL v8 hello"); //设定text
lv_obj_align_to(label, logo, LV_ALIGN_OUT_RIGHT_TOP, 10, 0); //设定位置
label = lv_label_create(tab_btns);
lv_label_set_text(label, "Widgets demo");
lv_obj_add_style(label, &style_text_muted, 0);
lv_obj_align_to(label, logo, LV_ALIGN_OUT_RIGHT_BOTTOM, 10, 0);
}
lv_obj_t * t1 = lv_tabview_add_tab(tv, "Profile"); //在tabview控件内创建3个tab页
lv_obj_t * t2 = lv_tabview_add_tab(tv, "Analytics");
lv_obj_t * t3 = lv_tabview_add_tab(tv, "Shop");
profile_create(t1); //在tab页内创建内容
analytics_create(t2);
shop_create(t3);
color_changer_create(tv); //设定换页时的颜色切换
}
上面代码的第3~5行,是根据显示屏的参数确定显示器大小分类的。可以根据显示器大小运行不同的布局代码。当然,此例程中只实现了大尺寸的显示屏布局。
1,其中‘LV_HOR_RES’宏定义到如下的函数,用于获取显示屏的长边分辨率。
lv_coord_t lv_disp_get_hor_res(lv_disp_t * disp)
{
if(disp == NULL) disp = lv_disp_get_default();
if(disp == NULL) {
return 0;
}
else {
switch(disp->driver->rotated) {
case LV_DISP_ROT_90:
case LV_DISP_ROT_270:
return disp->driver->ver_res;
default:
return disp->driver->hor_res;
}
}
}
1.1 其中涉及到一个‘lv_disp_t’结构。此结构,上篇文章也接触过,是LVGL层处理显示相关的结构类型。如下:
typedef struct _lv_disp_t {
/**< Driver to the display*/
struct _lv_disp_drv_t * driver;
/**< A timer which periodically checks the dirty areas and refreshes them*/
lv_timer_t * refr_timer;
/**< The theme assigned to the screen*/
struct _lv_theme_t * theme;
/** Screens of the display*/
struct _lv_obj_t ** screens; /**< Array of screen objects.*/
struct _lv_obj_t * act_scr; /**< Currently active screen on this display*/
struct _lv_obj_t * prev_scr; /**< Previous screen. Used during screen animations*/
struct _lv_obj_t * scr_to_load; /**< The screen prepared to load in lv_scr_load_anim*/
struct _lv_obj_t * top_layer; /**< @SEE lv_disp_get_layer_top*/ struct _lv_obj_t * sys_layer; /**< @see lv_disp_get_layer_sys*/
uint32_t screen_cnt;
uint8_t del_prev :
1; /**< 1: Automatically delete the previous screen when the screen load animation is ready*/
lv_opa_t bg_opa; /**
lv_color_t bg_color; /**< Default display color when screens are transparent*/
const void * bg_img; /**< An image source to display as wallpaper*/
/** Invalidated (marked to redraw) areas*/
lv_area_t inv_areas[LV_INV_BUF_SIZE]; //需要重绘的区域,最多32个
uint8_t inv_area_joined[LV_INV_BUF_SIZE];
uint16_t inv_p;
/*Miscellaneous data*/
uint32_t last_activity_time; /**< Last time when there was activity on this display*/
} lv_disp_t;
1.1.1 其中又引出多个结构,‘struct _lv_disp_drv_t’是LVGL层管理显示屏的驱动结构,除了显示相关的回调接口外,还有一些数据结构。
typedef struct _lv_disp_drv_t {
lv_coord_t hor_res; /**< Horizontal resolution.*/
lv_coord_t ver_res; /**< Vertical resolution.*/
lv_coord_t
physical_hor_res; /**< Horizontal resolution of the full / physical display. Set to -1 for fullscreen mode.*/
lv_coord_t
physical_ver_res; /**< Vertical resolution of the full / physical display. Set to -1 for fullscreen mode.*/
lv_coord_t
offset_x; /**< Horizontal offset from the full / physical display. Set to 0 for fullscreen mode.*/
lv_coord_t offset_y; /**< Vertical offset from the full / physical display. Set to 0 for fullscreen mode.*/
/** Pointer to a buffer initialized with `lv_disp_draw_buf_init()`.
* LVGL will use this buffer(s) to draw the screens contents*/
lv_disp_draw_buf_t * draw_buf;
uint32_t direct_mode : 1; /**< 1: Use screen-sized buffers and draw to absolute coordinates*/
uint32_t full_refresh : 1; /**< 1: Always make the whole screen redrawn*/
uint32_t sw_rotate : 1; /**< 1: use software rotation (slower)*/
uint32_t antialiasing : 1; /**< 1: anti-aliasing is enabled on this display.*/
uint32_t rotated : 2; /**< 1: turn the display by 90 degree. @WARNING Does not update coordinates for you!*/ uint32_t screen_transp : 1; /**Handle if the screen doesn't have a solid (opa == LV_OPA_COVER) background.
* Use only if required because it's slower.*/
uint32_t dpi : 10; /** DPI (dot per inch) of the display. Default value is `LV_DPI_DEF`.*/
/** MANDATORY: Write the internal buffer (draw_buf) to the display. 'lv_disp_flush_ready()' has to be
* called when finished*/
void (*flush_cb)(struct _lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
/** OPTIONAL: Extend the invalidated areas to match with the display drivers requirements
* E.g. round `y` to, 8, 16 ..) on a monochrome display*/
void (*rounder_cb)(struct _lv_disp_drv_t * disp_drv, lv_area_t * area);
/** OPTIONAL: Set a pixel in a buffer according to the special requirements of the display
* Can be used for color format not supported in LittelvGL. E.g. 2 bit -> 4 gray scales
* @NOTE Much slower then drawing with supported color formats.*/ void (*set_px_cb)(struct _lv_disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
lv_color_t color, lv_opa_t opa);
void (*clear_cb)(struct _lv_disp_drv_t * disp_drv, uint8_t * buf, uint32_t size);
/** OPTIONAL: Called after every refresh cycle to tell the rendering and flushing time + the
* number of flushed pixels*/
void (*monitor_cb)(struct _lv_disp_drv_t * disp_drv, uint32_t time, uint32_t px);
/** OPTIONAL: Called periodically while lvgl waits for operation to be completed.
* For example flushing or GPU
* User can execute very simple tasks here or yield the task*/
void (*wait_cb)(struct _lv_disp_drv_t * disp_drv);
/** OPTIONAL: Called when lvgl needs any CPU cache that affects rendering to be cleaned*/
void (*clean_dcache_cb)(struct _lv_disp_drv_t * disp_drv);
/** OPTIONAL: called when driver parameters are updated */
void (*drv_update_cb)(struct _lv_disp_drv_t * disp_drv);
/** On CHROMA_KEYED images this color will be transparent.
* `LV_COLOR_CHROMA_KEY` by default. (lv_conf.h)*/
lv_color_t color_chroma_key;
lv_draw_ctx_t * draw_ctx;
void (*draw_ctx_init)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
void (*draw_ctx_deinit)(struct _lv_disp_drv_t * disp_drv, lv_draw_ctx_t * draw_ctx);
size_t draw_ctx_size;
#if LV_USE_USER_DATA
void * user_data; /**< Custom display driver user data*/
#endif
} lv_disp_drv_t;
1.1.1.1 其中‘lv_disp_draw_buf_t’的结构类型如下,其中buf1、buf2、buf_act对应了上篇文章中底层显示屏初始化时开辟的3块framebuffer空间。
typedef struct _lv_disp_draw_buf_t {
void * buf1; /**< First display buffer.*/
void * buf2; /**< Second display buffer.*/
/*Internal, used by the library*/
void * buf_act;
uint32_t size; /*In pixel count*/
/*1: flushing is in progress. (It can't be a bit field because when it's cleared from IRQ Read-Modify-Write issue might occur)*/
volatile int flushing;
/*1: It was the last chunk to flush. (It can't be a bit field because when it's cleared from IRQ Read-Modify-Write issue might occur)*/
volatile int flushing_last;
volatile uint32_t last_area : 1; /*1: the last area is being rendered*/
volatile uint32_t last_part : 1; /*1: the last part of the current area is being rendered*/
} lv_disp_draw_buf_t;
1.1.1.2 ‘lv_draw_ctx_t’的结构如下,包含了底层基础的绘制图形函数接口:
typedef struct _lv_draw_ctx_t {
/**
* Pointer to a buffer to draw into
*/
void * buf;
/**
* The position and size of `buf` (absolute coordinates)
*/
lv_area_t * buf_area;
/**
* The current clip area with absolute coordinates, always the same or smaller than `buf_area`
*/
const lv_area_t * clip_area;
void (*draw_rect)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * dsc, const lv_area_t * coords);
void (*draw_arc)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_arc_dsc_t * dsc, const lv_point_t * center,
uint16_t radius, uint16_t start_angle, uint16_t end_angle);
void (*draw_img_decoded)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * dsc,
const lv_area_t * coords, const uint8_t * map_p, lv_img_cf_t color_format);
lv_res_t (*draw_img)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_img_dsc_t * draw_dsc,
const lv_area_t * coords, const void * src);
void (*draw_letter)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_label_dsc_t * dsc, const lv_point_t * pos_p,
uint32_t letter);
void (*draw_line)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_line_dsc_t * dsc, const lv_point_t * point1,
const lv_point_t * point2);
void (*draw_polygon)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc,
const lv_point_t * points, uint16_t point_cnt);
/**
* Replace the buffer with a rect without decoration like radius or borders
*/
void (*draw_bg)(struct _lv_draw_ctx_t * draw_ctx, const lv_draw_rect_dsc_t * draw_dsc, const lv_area_t * coords);
/**
* Wait until all background operations are finished. (E.g. GPU operations)
*/
void (*wait_for_finish)(struct _lv_draw_ctx_t * draw);
#if LV_USE_USER_DATA
void * user_data;
#endif
} lv_draw_ctx_t;
1.1.2 再回到‘_lv_disp_t’结构,定义了一个‘lv_timer_t’类型的定时器。用于检测需要刷新的区域并刷新到显示屏。结构如下:
typedef struct _lv_timer_t {
uint32_t period; /**< How often the timer should run*/
uint32_t last_run; /**< Last time the timer ran*/
lv_timer_cb_t timer_cb; /**< Timer function*/
void * user_data; /**< Custom user data*/
int32_t repeat_count; /**< 1: One time; -1 : infinity; n>0: residual times*/
uint32_t paused : 1;
} lv_timer_t;
1.1.3 ‘_lv_disp_t’结构中接下来定义了一个‘lv_theme_t’类型的主题。结构如下:
typedef struct _lv_theme_t {
lv_theme_apply_cb_t apply_cb; //在控件上运用主题市的回调函数
struct _lv_theme_t * parent; /**父主题< Apply the current theme's style on top of this theme.*/
void * user_data; //私有数据
struct _lv_disp_t * disp; //display设备,这里互相包含了一下结构指针
lv_color_t color_primary; //主颜色
lv_color_t color_secondary; //次颜色
const lv_font_t * font_small; //小字体
const lv_font_t * font_normal; //正常字体
const lv_font_t * font_large; //大字体
uint32_t flags; /*Any custom flag used by the theme*/
} lv_theme_t;
其中’lv_color_t’通过typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t;定义。LV_COLOR_DEPTH定义为32。上面类型从定义展开为typedef lv_color32_t lv_color_t;。
1.1.3.1 ‘lv_color32_t’定义如下:
typedef union {
struct {
uint8_t blue;
uint8_t green;
uint8_t red;
uint8_t alpha;
} ch;
uint32_t full;
} lv_color32_t;
1.1.3.2 ‘lv_font_t’定义如下:
typedef struct _lv_font_t {
/** Get a glyph's descriptor from a font*/
bool (*get_glyph_dsc)(const struct _lv_font_t *, lv_font_glyph_dsc_t *, uint32_t letter, uint32_t letter_next);
/** Get a glyph's bitmap from a font*/
const uint8_t * (*get_glyph_bitmap)(const struct _lv_font_t *, uint32_t);
/*Pointer to the font in a font pack (must have the same line height)*/
lv_coord_t line_height; /**< The real line height where any text fits*/
lv_coord_t base_line; /**< Base line measured from the top of the line_height*/
uint8_t subpx : 2; /**< An element of `lv_font_subpx_t`*/
int8_t underline_position; /**< Distance between the top of the underline and base line (< 0 means below the base line)*/
int8_t underline_thickness; /**< Thickness of the underline*/
const void * dsc; /**< Store implementation specific or run_time data or caching here*/
const struct _lv_font_t * fallback; /**< Fallback font for missing glyph. Resolved recursively */
#if LV_USE_USER_DATA
void * user_data; /**< Custom user data for font.*/
#endif
} lv_font_t;
1.1.4 ‘lv_obj_t’定义如下:
typedef struct _lv_obj_t {
const lv_obj_class_t * class_p;
struct _lv_obj_t * parent;
_lv_obj_spec_attr_t * spec_attr;
_lv_obj_style_t * styles;
#if LV_USE_USER_DATA
void * user_data;
#endif
lv_area_t coords;
lv_obj_flag_t flags;
lv_state_t state;
uint16_t layout_inv : 1;
uint16_t scr_layout_inv : 1;
uint16_t skip_trans : 1;
uint16_t style_cnt : 6;
uint16_t h_layout : 1;
uint16_t w_layout : 1;
} lv_obj_t;
lv_obj_t是LVGL显示控件的基类,或者叫做基础结构类型。包含了LVGL控件共有的一些属性。
1.1.4.1 ‘lv_obj_class_t’定义如下:
typedef struct _lv_obj_class_t {
const struct _lv_obj_class_t * base_class; //基类
void (*constructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj); //构造函数接口
void (*destructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj); //析构函数接口
#if LV_USE_USER_DATA
void * user_data;
#endif
void (*event_cb)(const struct _lv_obj_class_t * class_p,
struct _lv_event_t * e); /**< Widget type specific event function 事件响应的回调函数*/
lv_coord_t width_def; //宽
lv_coord_t height_def; //高
uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
uint32_t instance_size : 16;
} lv_obj_class_t;
1.1.4.2 ‘_lv_obj_spec_attr_t’定义如下:
主要包含了子控件数组和子控件数量等。
typedef struct {
struct _lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
uint32_t child_cnt; /**< Number of children*/
lv_group_t * group_p;
struct _lv_event_dsc_t * event_dsc; /**< Dynamically allocated event callback and user data array*/
lv_point_t scroll; /**< The current X/Y scroll offset*/
lv_coord_t ext_click_pad; /**< Extra click padding in all direction*/
lv_coord_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
lv_scrollbar_mode_t scrollbar_mode : 2; /**< How to display scrollbars*/
lv_scroll_snap_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally*/
lv_scroll_snap_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/
lv_dir_t scroll_dir : 4; /**< The allowed scroll direction(s)*/
uint8_t event_dsc_cnt; /**< Number of event callbacks stored in `event_dsc` array*/
} _lv_obj_spec_attr_t;
1.1.4.3 ‘_lv_obj_style_t’定义如下:
typedef struct {
lv_style_t * style;
uint32_t selector : 24;
uint32_t is_local : 1;
uint32_t is_trans : 1;
} _lv_obj_style_t;
1.1.4.3.1 ‘lv_style_t’定义如下:
typedef struct {
#if LV_USE_ASSERT_STYLE
uint32_t sentinel;
#endif
/*If there is only one property store it directly.
*For more properties allocate an array*/
union {
lv_style_value_t value1;
uint8_t * values_and_props;
const lv_style_const_prop_t * const_props;
} v_p;
uint16_t prop1 : 15;
uint16_t is_const : 1;
uint8_t has_group;
uint8_t prop_cnt;
} lv_style_t;
其中’lv_style_value_t’定义如下:
typedef union {
int32_t num; /**< Number integer number (opacity, enums, booleans or "normal" numbers)*/
const void * ptr; /**< Constant pointers (font, cone text, etc)*/
lv_color_t color; /**< Colors*/
} lv_style_value_t;
其中’lv_style_const_prop_t’定义如下:
typedef struct {
lv_style_prop_t prop;
lv_style_value_t value;
} lv_style_const_prop_t;
其中’lv_style_prop_t’定义如下:
typedef enum {
LV_STYLE_PROP_INV = 0,
/*Group 0*/
LV_STYLE_WIDTH = 1 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_MIN_WIDTH = 2 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_MAX_WIDTH = 3 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_HEIGHT = 4 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_MIN_HEIGHT = 5 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_MAX_HEIGHT = 6 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_X = 7 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_Y = 8 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_ALIGN = 9 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_TRANSFORM_WIDTH = 10 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_TRANSFORM_HEIGHT = 11 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_TRANSLATE_X = 12 | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR,
LV_STYLE_TRANSLATE_Y = 13 | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR,
LV_STYLE_TRANSFORM_ZOOM = 14 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR,
LV_STYLE_TRANSFORM_ANGLE = 15 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR | LV_STYLE_PROP_PARENT_LAYOUT_REFR,
/*Group 1*/
LV_STYLE_PAD_TOP = 16 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_PAD_BOTTOM = 17 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_PAD_LEFT = 18 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_PAD_RIGHT = 19 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_PAD_ROW = 20 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_PAD_COLUMN = 21 | LV_STYLE_PROP_EXT_DRAW | LV_STYLE_PROP_LAYOUT_REFR,
/*Group 2*/
LV_STYLE_BG_COLOR = 32,
LV_STYLE_BG_COLOR_FILTERED = 32 | LV_STYLE_PROP_FILTER,
LV_STYLE_BG_OPA = 33,
LV_STYLE_BG_GRAD_COLOR = 34,
LV_STYLE_BG_GRAD_COLOR_FILTERED = 34 | LV_STYLE_PROP_FILTER,
LV_STYLE_BG_GRAD_DIR = 35,
LV_STYLE_BG_MAIN_STOP = 36,
LV_STYLE_BG_GRAD_STOP = 37,
LV_STYLE_BG_GRAD = 38,
LV_STYLE_BG_DITHER_MODE = 39,
LV_STYLE_BG_IMG_SRC = 40 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_BG_IMG_OPA = 41,
LV_STYLE_BG_IMG_RECOLOR = 42,
LV_STYLE_BG_IMG_RECOLOR_FILTERED = 42 | LV_STYLE_PROP_FILTER,
LV_STYLE_BG_IMG_RECOLOR_OPA = 43,
LV_STYLE_BG_IMG_TILED = 44,
/*Group 3*/
LV_STYLE_BORDER_COLOR = 48,
LV_STYLE_BORDER_COLOR_FILTERED = 48 | LV_STYLE_PROP_FILTER,
LV_STYLE_BORDER_OPA = 49,
LV_STYLE_BORDER_WIDTH = 50 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_BORDER_SIDE = 51,
LV_STYLE_BORDER_POST = 52,
LV_STYLE_OUTLINE_WIDTH = 58 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_OUTLINE_COLOR = 59,
LV_STYLE_OUTLINE_COLOR_FILTERED = 59 | LV_STYLE_PROP_FILTER,
LV_STYLE_OUTLINE_OPA = 60 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_OUTLINE_PAD = 61 | LV_STYLE_PROP_EXT_DRAW,
/*Group 4*/
LV_STYLE_SHADOW_WIDTH = 64 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_SHADOW_OFS_X = 65 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_SHADOW_OFS_Y = 66 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_SHADOW_SPREAD = 67 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_SHADOW_COLOR = 68,
LV_STYLE_SHADOW_COLOR_FILTERED = 68 | LV_STYLE_PROP_FILTER,
LV_STYLE_SHADOW_OPA = 69 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_IMG_OPA = 70,
LV_STYLE_IMG_RECOLOR = 71,
LV_STYLE_IMG_RECOLOR_FILTERED = 71 | LV_STYLE_PROP_FILTER,
LV_STYLE_IMG_RECOLOR_OPA = 72,
LV_STYLE_LINE_WIDTH = 73 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_LINE_DASH_WIDTH = 74,
LV_STYLE_LINE_DASH_GAP = 75,
LV_STYLE_LINE_ROUNDED = 76,
LV_STYLE_LINE_COLOR = 77,
LV_STYLE_LINE_COLOR_FILTERED = 77 | LV_STYLE_PROP_FILTER,
LV_STYLE_LINE_OPA = 78,
/*Group 5*/
LV_STYLE_ARC_WIDTH = 80 | LV_STYLE_PROP_EXT_DRAW,
LV_STYLE_ARC_ROUNDED = 81,
LV_STYLE_ARC_COLOR = 82,
LV_STYLE_ARC_COLOR_FILTERED = 82 | LV_STYLE_PROP_FILTER,
LV_STYLE_ARC_OPA = 83,
LV_STYLE_ARC_IMG_SRC = 84,
LV_STYLE_TEXT_COLOR = 87 | LV_STYLE_PROP_INHERIT,
LV_STYLE_TEXT_COLOR_FILTERED = 87 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_FILTER,
LV_STYLE_TEXT_OPA = 88 | LV_STYLE_PROP_INHERIT,
LV_STYLE_TEXT_FONT = 89 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_TEXT_LETTER_SPACE = 90 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_TEXT_LINE_SPACE = 91 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_TEXT_DECOR = 92 | LV_STYLE_PROP_INHERIT,
LV_STYLE_TEXT_ALIGN = 93 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
/*Group 6*/
LV_STYLE_RADIUS = 96,
LV_STYLE_CLIP_CORNER = 97,
LV_STYLE_OPA = 98 | LV_STYLE_PROP_INHERIT,
LV_STYLE_COLOR_FILTER_DSC = 99,
LV_STYLE_COLOR_FILTER_OPA = 100,
LV_STYLE_ANIM_TIME = 101,
LV_STYLE_ANIM_SPEED = 102,
LV_STYLE_TRANSITION = 103,
LV_STYLE_BLEND_MODE = 104,
LV_STYLE_LAYOUT = 105 | LV_STYLE_PROP_LAYOUT_REFR,
LV_STYLE_BASE_DIR = 106 | LV_STYLE_PROP_INHERIT | LV_STYLE_PROP_LAYOUT_REFR,
_LV_STYLE_LAST_BUILT_IN_PROP = 111,
LV_STYLE_PROP_ANY = 0xFFFF
} lv_style_prop_t;
1.1.4.4 ‘lv_area_t’定义如下:
typedef struct {
lv_coord_t x1;
lv_coord_t y1;
lv_coord_t x2;
lv_coord_t y2;
} lv_area_t;
至此,终于把lv_demo_widgets()函数内部3~5行代码牵扯到的数据结构都过了一遍。继续往下看。
2 ‘lv_demo_widgets()’函数的第50行,调用了’lv_theme_default_init()’函数,初始化了默认主题,代码如下:
lv_theme_t * lv_theme_default_init(lv_disp_t * disp, lv_color_t color_primary, lv_color_t color_secondary, bool dark,
const lv_font_t * font)
{
/*This trick is required only to avoid the garbage collection of
*styles' data if LVGL is used in a binding (e.g. Micropython)
*In a general case styles could be in simple `static lv_style_t my_style...` variables*/
if(!lv_theme_default_is_inited()) {
inited = false;
LV_GC_ROOT(_lv_theme_default_styles) = lv_mem_alloc(sizeof(my_theme_styles_t));
styles = (my_theme_styles_t *)LV_GC_ROOT(_lv_theme_default_styles);
}
if(LV_HOR_RES <= 320) disp_size = DISP_SMALL;
else if(LV_HOR_RES < 720) disp_size = DISP_MEDIUM;
else disp_size = DISP_LARGE;
theme.disp = disp;
theme.color_primary = color_primary;
theme.color_secondary = color_secondary;
theme.font_small = font;
theme.font_normal = font;
theme.font_large = font;
theme.apply_cb = theme_apply;
theme.flags = dark ? MODE_DARK : 0;
style_init();
if(disp == NULL || lv_disp_get_theme(disp) == &theme) lv_obj_report_style_change(NULL);
inited = true;
return (lv_theme_t *)&theme;
}
LV_GC_ROOT(_lv_theme_default_styles)宏展开为void *_lv_theme_default_styles0.styles和theme都是’lv_theme_default.c’文件下的静态全局变量。用于保存UI的风格类型和主题。
2.1 ‘my_theme_styles_t’定义如下,定义了多个’lv_style_t’成员变量。
typedef struct {
lv_style_t scr;
lv_style_t scrollbar;
lv_style_t scrollbar_scrolled;
lv_style_t card;
lv_style_t btn;
/*Utility*/
lv_style_t bg_color_primary;
lv_style_t bg_color_primary_muted;
lv_style_t bg_color_secondary;
lv_style_t bg_color_secondary_muted;
lv_style_t bg_color_grey;
lv_style_t bg_color_white;
lv_style_t pressed;
lv_style_t disabled;
lv_style_t pad_zero;
lv_style_t pad_tiny;
lv_style_t pad_small;
lv_style_t pad_normal;
lv_style_t pad_gap;
lv_style_t line_space_large;
lv_style_t text_align_center;
lv_style_t outline_primary;
lv_style_t outline_secondary;
lv_style_t circle;
lv_style_t no_radius;
lv_style_t clip_corner;
#if LV_THEME_DEFAULT_GROW
lv_style_t grow;
#endif
lv_style_t transition_delayed;
lv_style_t transition_normal;
lv_style_t anim;
lv_style_t anim_fast;
/*Parts*/
lv_style_t knob;
lv_style_t indic;
//......省略源码中多个类似定义
} my_theme_styles_t;
2.2 ‘theme_apply’回调函数原型如下,主要功能是把styles里的style加入到传入的obj的结构里面。
static void theme_apply(lv_theme_t * th, lv_obj_t * obj)
{
LV_UNUSED(th);
if(lv_obj_get_parent(obj) == NULL) {
lv_obj_add_style(obj, &styles->scr, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
return;
}
if(lv_obj_check_type(obj, &lv_obj_class)) {
#if LV_USE_TABVIEW
lv_obj_t * parent = lv_obj_get_parent(obj);
/*Tabview content area*/
if(lv_obj_check_type(parent, &lv_tabview_class)) {
return;
}
/*Tabview pages*/
else if(lv_obj_check_type(lv_obj_get_parent(parent), &lv_tabview_class)) {
lv_obj_add_style(obj, &styles->pad_normal, 0);
lv_obj_add_style(obj, &styles->scrollbar, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &styles->scrollbar_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
return;
}
#endif
//...省略源码中多行类似代码
}
2.2.1 ‘lv_obj_add_style()’函数原型如下:
void lv_obj_add_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
{
trans_del(obj, selector, LV_STYLE_PROP_ANY, NULL);
uint32_t i;
/*Go after the transition and local styles*/
for(i = 0; i < obj->style_cnt; i++) {
if(obj->styles.is_trans) continue;
if(obj->styles.is_local) continue;
break;
}
/*Now `i` is at the first normal style. Insert the new style before this*/
/*Allocate space for the new style and shift the rest of the style to the end*/
obj->style_cnt++;
obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
uint32_t j;
for(j = obj->style_cnt - 1; j > i ; j--) {
obj->styles[j] = obj->styles[j - 1];
}
lv_memset_00(&obj->styles, sizeof(_lv_obj_style_t));
obj->styles.style = style;
obj->styles.selector = selector;
lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
}
2.3 ‘style_init()’函数定义如下,初始化styles里的style参赛。
static void style_init(void)
{
static const lv_style_prop_t trans_props[] = {
LV_STYLE_BG_OPA, LV_STYLE_BG_COLOR,
LV_STYLE_TRANSFORM_WIDTH, LV_STYLE_TRANSFORM_HEIGHT,
LV_STYLE_TRANSLATE_Y, LV_STYLE_TRANSLATE_X,
LV_STYLE_TRANSFORM_ZOOM, LV_STYLE_TRANSFORM_ANGLE,
LV_STYLE_COLOR_FILTER_OPA, LV_STYLE_COLOR_FILTER_DSC,
0
};
color_scr = theme.flags & MODE_DARK ? DARK_COLOR_SCR : LIGHT_COLOR_SCR;
color_text = theme.flags & MODE_DARK ? DARK_COLOR_TEXT : LIGHT_COLOR_TEXT;
color_card = theme.flags & MODE_DARK ? DARK_COLOR_CARD : LIGHT_COLOR_CARD;
color_grey = theme.flags & MODE_DARK ? DARK_COLOR_GREY : LIGHT_COLOR_GREY;
static lv_style_transition_dsc_t trans_delayed;
lv_style_transition_dsc_init(&trans_delayed, trans_props, lv_anim_path_linear, TRANSITION_TIME, 70, NULL);
static lv_style_transition_dsc_t trans_normal;
lv_style_transition_dsc_init(&trans_normal, trans_props, lv_anim_path_linear, TRANSITION_TIME, 0, NULL);
style_init_reset(&styles->transition_delayed);
lv_style_set_transition(&styles->transition_delayed, &trans_delayed); /*Go back to default state with delay*/
style_init_reset(&styles->transition_normal);
lv_style_set_transition(&styles->transition_normal, &trans_normal); /*Go back to default state with delay*/
style_init_reset(&styles->scrollbar);
lv_style_set_bg_color(&styles->scrollbar, (theme.flags & MODE_DARK) ? lv_palette_darken(LV_PALETTE_GREY,
2) : lv_palette_main(LV_PALETTE_GREY));
lv_style_set_radius(&styles->scrollbar, LV_RADIUS_CIRCLE);
lv_style_set_pad_all(&styles->scrollbar, lv_disp_dpx(theme.disp, 7));
lv_style_set_width(&styles->scrollbar, lv_disp_dpx(theme.disp, 5));
lv_style_set_bg_opa(&styles->scrollbar, LV_OPA_40);
lv_style_set_transition(&styles->scrollbar, &trans_normal);
style_init_reset(&styles->scrollbar_scrolled);
lv_style_set_bg_opa(&styles->scrollbar_scrolled, LV_OPA_COVER);
//......省略源码中多行类似代码
3 ‘lv_demo_widgets()’函数中调用的’lv_style_init()’函数,代码如下,清空传入的style:
void lv_style_init(lv_style_t * style)
{
lv_memset_00(style, sizeof(lv_style_t));
}
4 ‘lv_demo_widgets()’函数中调用了多个’lv_style_set_xxx()’函数。用于设置style属性,以设置text字体为例:
void lv_style_set_text_font(lv_style_t * style, const lv_font_t * value)
{
lv_style_value_t v = {
.ptr = value
};
lv_style_set_prop(style, LV_STYLE_TEXT_FONT, v);
}
5 ‘lv_demo_widgets()’函数第67行,调用了’lv_tabview_create()’函数,创建了一个tabview控件结构:
lv_obj_t * parent 指定父控件。这里传入用lv_scr_act()获得的当前屏幕。
lv_dir_t tab_pos 用于指定标签位置,这里传入的LV_DIR_TOP,标签栏在顶部。
lv_coord_t tab_size 用于设定标签栏高度
lv_obj_t * lv_tabview_create(lv_obj_t * parent, lv_dir_t tab_pos, lv_coord_t tab_size)
{
LV_LOG_INFO("begin");
tabpos_create = tab_pos;
tabsize_create = tab_size;
lv_obj_t * obj = lv_obj_class_create_obj(&lv_tabview_class, parent);
lv_obj_class_init_obj(obj);
return obj;
}
6 ‘lv_demo_widgets()’函数第69行,调用’lv_obj_set_style_text_font()’函数,设置了显示屏的基础字体。各控件不重新设置字体时就会用此字体。
void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector)
{
lv_style_value_t v = {
.ptr = value
};
lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_FONT, v, selector);
}
7 后面的其他操作基本类似,都是创建控件,选定style等属性,设定位置等。这里还想说明的一点是LVGL的颜色问题。
官方给出的三个颜色列表在lv_color.c中定义,但都没有纯色的。比如上面选定的蓝色,为下面的第五个成员(r:0x21,g:0x96,b:0xff)。导致我一直以为是屏幕质量不好,显示的颜色不纯。个人还是喜欢对比度高一点的显示,至少有一些控件显示颜色要纯一些好看。要不然整体看起来灰蒙蒙的,不是很舒服。
下面以’lv_palette_main’的颜色数组为例:
lv_color_t lv_palette_main(lv_palette_t p)
{
static const lv_color_t colors[] = {
LV_COLOR_MAKE(0xF4, 0x43, 0x36), LV_COLOR_MAKE(0xE9, 0x1E, 0x63), LV_COLOR_MAKE(0x9C, 0x27, 0xB0), LV_COLOR_MAKE(0x67, 0x3A, 0xB7),
LV_COLOR_MAKE(0x3F, 0x51, 0xB5), LV_COLOR_MAKE(0x21, 0x96, 0xFf), LV_COLOR_MAKE(0x03, 0xA9, 0xF4), LV_COLOR_MAKE(0x00, 0xBC, 0xD4),
LV_COLOR_MAKE(0x00, 0x96, 0x88), LV_COLOR_MAKE(0x4C, 0xAF, 0x50), LV_COLOR_MAKE(0x8B, 0xC3, 0x4A), LV_COLOR_MAKE(0xCD, 0xDC, 0x39),
LV_COLOR_MAKE(0xFF, 0xEB, 0x3B), LV_COLOR_MAKE(0xFF, 0xC1, 0x07), LV_COLOR_MAKE(0xFF, 0x98, 0x00), LV_COLOR_MAKE(0xFF, 0x57, 0x22),
LV_COLOR_MAKE(0x79, 0x55, 0x48), LV_COLOR_MAKE(0x60, 0x7D, 0x8B), LV_COLOR_MAKE(0x9E, 0x9E, 0x9E)
};
if(p >= _LV_PALETTE_LAST) {
LV_LOG_WARN("Invalid palette: %d", p);
return lv_color_black();
}
return colors[p];
}
至此例程代码简略的看了一遍,基础的使用方法也就有了初步的认识。后面就可以自己写代码,进一步深入学习啦。祝大家好运,学习快乐。
原作者:吉利咕噜2022