嵌入式技术论坛
直播中

青sky

8年用户 1275经验值
擅长:模拟技术
私信 关注
[经验]

简要分析N9H30开发板RTThread框架下的LVGL demo源码

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

更多回帖

×
20
完善资料,
赚取积分