续接上一篇文章,分析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
|