Scrolling(滚动)
Overview(概述)
显示原文
In LVGL scrolling works very intuitively: if a Widget is outside its parent content area (the size without padding), the parent becomes scrollable and scrollbar(s) will appear. That's it.
Any Widget can be scrollable including Base Widget, lv_image
,
lv_button
, lv_meter
, etc
The Widget can either be scrolled horizontally or vertically in one stroke; diagonal scrolling is not possible.
在LVGL中,滚动的工作方式非常直观:如果一个Widget超出了其父对象的内容区域(即去除内边距后的大小),其父对象就会变得可滚动,并且滚动条会出现。就是这样简单。
任何Widget都可以是可滚动的,包括:ref:base_widget、lv_image
、lv_button
、``lv_meter``等。
Widget可以在一次滑动中水平或垂直滚动;但不支持对角线方向的滚动。
Scrollbar(滚动条)
Mode(模式)
显示原文
Scrollbars are displayed according to the configured scrollbar-mode
. The
following modes are available:
LV_SCROLLBAR_MODE_OFF
: Never show the scrollbarsLV_SCROLLBAR_MODE_ON
: Always show the scrollbarsLV_SCROLLBAR_MODE_ACTIVE
: Show scroll bars while a Widget is being scrolledLV_SCROLLBAR_MODE_AUTO
: Show scroll bars when the content is large enough to be scrolled
lv_obj_set_scrollbar_mode(widget, LV_SCROLLBAR_MODE_...) sets the scrollbar mode on a Widget.
滚动条的显示取决于配置的``scrollbar-mode``。可用的模式如下:
LV_SCROLLBAR_MODE_OFF
: 从不显示滚动条LV_SCROLLBAR_MODE_ON
: 始终显示滚动条LV_SCROLLBAR_MODE_ACTIVE
: 当Widget正在被滚动时显示滚动条LV_SCROLLBAR_MODE_AUTO
: 当内容足够大以至于可以滚动时显示滚动条
使用:cpp:expr:`lv_obj_set_scrollbar_mode(widget, LV_SCROLLBAR_MODE_...)`可以为Widget设置滚动条模式。
Styling(样式)
显示原文
A Scrollbar is a dedicated part of a Widget, called
LV_PART_SCROLLBAR
. For example, a scrollbar can turn to red like
this:
static lv_style_t style_red;
lv_style_init(&style_red);
lv_style_set_bg_color(&style_red, lv_color_red());
...
lv_obj_add_style(widget, &style_red, LV_PART_SCROLLBAR);
A Widget goes to the LV_STATE_SCROLLED
state while it's being
scrolled. This allows adding different styles to the Widget that will be effective
while it is being scrolled. For example, this code makes the scrollbar blue while
the Widget is being scrolled:
static lv_style_t style_blue;
lv_style_init(&style_blue);
lv_style_set_bg_color(&style_blue, lv_color_blue());
...
lv_obj_add_style(widget, &style_blue, LV_STATE_SCROLLED | LV_PART_SCROLLBAR);
If the base direction of the LV_PART_SCROLLBAR
is RTL
(LV_BASE_DIR_RTL
) the vertical scrollbar will be placed on the left.
Note that, the base_dir
style property is inherited. Therefore, it
can be set directly on the LV_PART_SCROLLBAR
part of a Widget, or
on the Widget's LV_PART_MAIN part, or that of any of its parents, to make a scrollbar
inherit the base direction.
pad_left/right/top/bottom
sets the spacing around the scrollbars and
width
sets the scrollbar's width.
滚动条是Widget的一个专用部分,称为:cpp:enumerator:LV_PART_SCROLLBAR。例如,可以通过以下代码将滚动条设置为红色:
static lv_style_t style_red;
lv_style_init(&style_red);
lv_style_set_bg_color(&style_red, lv_color_red());
...
lv_obj_add_style(widget, &style_red, LV_PART_SCROLLBAR);
当Widget正在被滚动时,它会进入:cpp:enumerator:`LV_STATE_SCROLLED`状态。这允许为Widget添加在滚动期间有效的不同样式。例如,以下代码会在Widget滚动时将滚动条设置为蓝色:
static lv_style_t style_blue;
lv_style_init(&style_blue);
lv_style_set_bg_color(&style_blue, lv_color_blue());
...
lv_obj_add_style(widget, &style_blue, LV_STATE_SCROLLED | LV_PART_SCROLLBAR);
如果:cpp:enumerator:LV_PART_SCROLLBAR`的基础方向是RTL (:c:macro:`LV_BASE_DIR_RTL),垂直滚动条将会被放置在左侧。请注意,``base_dir``样式属性是继承的。因此,可以直接在Widget的:cpp:enumerator:`LV_PART_SCROLLBAR`部分上设置,也可以在Widget的LV_PART_MAIN部分或其任意父对象上设置,从而让滚动条继承基础方向。
``pad_left/right/top/bottom``属性设置滚动条周围的间距,``width``属性设置滚动条的宽度。
Scrolling Events(事件)
显示原文
The following events are emitted as part of scrolling:
LV_EVENT_SCROLL_BEGIN
: Signals that scrolling has begun. The event parameter isNULL
or anlv_anim_t *
with a scroll animation descriptor that can be modified if required.LV_EVENT_SCROLL_END
: Signals that scrolling has ended.LV_EVENT_SCROLL
: Signals that the scrolling position changed; triggered on every position change.
以下事件与滚动相关:
LV_EVENT_SCROLL_BEGIN
: 滚动开始。事件参数为NULL
或包含滚动动画描述符的lv_anim_t *
,如果需要,可以对其进行修改。LV_EVENT_SCROLL_END
: 滚动结束。LV_EVENT_SCROLL
: 发生滚动。在每次位置变化时触发。滚动事件。
Basic example(基本示例)
TODO
Features of scrolling(滚动的特点)
显示原文
Besides, managing "normal" scrolling there are many interesting and useful additional features.
此外,除了管理“正常”滚动外,还有许多有趣和实用的额外功能。
Scrollable(滚动效果)
显示原文
It is possible to make a Widget non-scrollable with lv_obj_remove_flag(widget, LV_OBJ_FLAG_SCROLLABLE).
Non-scrollable Widgets can still propagate the scrolling (chain) to their parents.
The direction in which scrolling happens can be controlled by lv_obj_set_scroll_dir(widget, LV_DIR_...).
The following values can be used for the direction:
LV_DIR_TOP
: only scroll upLV_DIR_LEFT
: only scroll leftLV_DIR_BOTTOM
: only scroll downLV_DIR_RIGHT
: only scroll rightLV_DIR_HOR
: only scroll horizontallyLV_DIR_VER
: only scroll verticallyLV_DIR_ALL
: scroll any directions
OR-ed values are also possible. E.g. LV_DIR_TOP | LV_DIR_LEFT.
可以使用:cpp:expr:`lv_obj_remove_flag(widget, LV_OBJ_FLAG_SCROLLABLE)`使Widget不可滚动。
即使Widget不可滚动,它仍然可以将滚动事件(链式)传播给其父对象。
滚动发生的方向可以通过:cpp:expr:`lv_obj_set_scroll_dir(widget, LV_DIR_...)`进行控制。
以下是可用的方向值:
LV_DIR_TOP
: 仅向上滚动LV_DIR_LEFT
: 仅向左滚动LV_DIR_BOTTOM
: 仅向下滚动LV_DIR_RIGHT
: 仅向右滚动LV_DIR_HOR
: 仅水平滚动LV_DIR_VER
: 仅垂直滚动LV_DIR_ALL
: 任意方向滚动
还可以使用按位或的值。例如:cpp:expr:LV_DIR_TOP | LV_DIR_LEFT。
Scroll chaining(滚动条)
显示原文
If a Widget can't be scrolled further (e.g. its content has reached the bottom-most position), additional scrolling is propagated to its parent. If the parent can be scrolled in that direction than it will be scrolled instead. It continues propagating up the Widget's parent hierarchy up to the Screen.
The propagation on scrolling is called "scroll chaining" and it can be
enabled/disabled with LV_OBJ_FLAG_SCROLL_CHAIN_HOR/VER
flag. If
chaining is disabled the propagation stops on the Widget and the
parent(s) won't be scrolled.
如果一个对象无法进一步滚动(例如,其内容已经到达最底部位置),额外的滚动将传递给其父对象。如果在该方向上可以滚动父对象,则父对象将被滚动。它会继续传递给祖父和曾祖父对象。
滚动传播被称为“滚动链”,可以使用 LV_OBJ_FLAG_SCROLL_CHAIN_HOR/VER
标志来启用/禁用它。如果禁用了链式滚动,传播将在该对象处停止,并且父对象(们)将不会滚动。
Scroll momentum(滚动惯性效果)
显示原文
When the user scrolls a Widget and releases it, LVGL can emulate inertial momentum for the scrolling. It's like the Widget was "thrown" and scrolling slows down smoothly.
Scroll momentum can be enabled/disabled with the
LV_OBJ_FLAG_SCROLL_MOMENTUM
flag.
当用户滚动一个Widget并释放时,LVGL可以模拟滚动的惯性动量。就像Widget被“抛出”一样,滚动会平滑地减速。
可以通过:cpp:enumerator:`LV_OBJ_FLAG_SCROLL_MOMENTUM`标志启用或禁用滚动动量。
Elastic scroll(弹性卷轴效果)
显示原文
Normally a Widget can't be scrolled past the extremities of its content. That is, the top side of the content can't be below the top side of the Widget, and vice versa for the bottom side.
However, with LV_OBJ_FLAG_SCROLL_ELASTIC
a fancy effect is added
when the user "over-scrolls" the content. The scrolling slows down, and
the content can be scrolled inside the Widget. When the Widget is
released the content scrolled in it is animated back to the closest valid
position.
通常情况下,对象不能滚动超出其内容的极限。也就是说,内容的顶部不能低于对象的顶部。
然而,使用 LV_OBJ_FLAG_SCROLL_ELASTIC
会在用户“过度滚动”内容时添加一种花哨的效果。
滚动会减速,并且内容可以在对象内滚动。当释放对象时,其中滚动的内容将被动画回到有效位置。
Snapping(捕捉)
显示原文
The children of a Widget can be snapped according to specific rules
when scrolling ends. Children can be made snappable individually with
the LV_OBJ_FLAG_SNAPPABLE
flag.
A Widget can align snapped children in four ways:
LV_SCROLL_SNAP_NONE
: Snapping is disabled. (default)LV_SCROLL_SNAP_START
: Align the children to the left/top side of a scrolled WidgetLV_SCROLL_SNAP_END
: Align the children to the right/bottom side of a scrolled WidgetLV_SCROLL_SNAP_CENTER
: Align the children to the center of a scrolled Widget
Snap alignment is set with lv_obj_set_scroll_snap_x(widget, LV_SCROLL_SNAP_...) and lv_obj_set_scroll_snap_y(widget, LV_SCROLL_SNAP_...).
This is what happens under the hood:
user scrolls and releases a Widget;
LVGL calculates where the scroll would end considering scroll momentum;
LVGL finds the nearest scroll point;
LVGL scrolls to the snap point with an animation.
当滚动结束时,Widget的子对象可以根据特定规则对齐(snap)。子对象可以通过设置:cpp:enumerator:`LV_OBJ_FLAG_SNAPPABLE`标志单独启用snap功能。
Widget可以以以下四种方式对齐其子对象:
LV_SCROLL_SNAP_NONE
: 禁用对齐功能。(默认)LV_SCROLL_SNAP_START
: 将子对象对齐到滚动Widget的左侧/顶部。LV_SCROLL_SNAP_END
: 将子对象对齐到滚动Widget的右侧/底部。LV_SCROLL_SNAP_CENTER
: 将子对象对齐到滚动Widget的中心。
可以通过以下方法设置snap对齐方式: - lv_obj_set_scroll_snap_x(widget, LV_SCROLL_SNAP_...):设置水平方向的snap。 - lv_obj_set_scroll_snap_y(widget, LV_SCROLL_SNAP_...):设置垂直方向的snap。
以下是内部处理流程:
用户滚动并释放Widget;
LVGL根据滚动动量计算滚动结束的位置;
LVGL找到最近的snap点;
LVGL通过动画滚动到snap点。
Scroll one(只滚动一个)
显示原文
The "scroll one" feature tells LVGL to allow scrolling only one
snappable child at a time. This requires making the children snappable
and setting scroll snap alignment to something other than
LV_SCROLL_SNAP_NONE
.
This feature can be enabled by the LV_OBJ_FLAG_SCROLL_ONE
flag.
“滚动一个”功能告诉LVGL一次仅允许滚动一个可对齐的子对象。这需要将子对象设置为可对齐,并设置一个与 LV_SCROLL_SNAP_NONE
不同的滚动对齐方式。
可以通过设置 LV_OBJ_FLAG_SCROLL_ONE
标志来启用此功能。
Scroll on focus(滚动焦点)
显示原文
Imagine that there are a lot of Widgets in a group that are on a scrollable Widget. Pressing the "Tab" button moves focus to the next Widget but it might be outside the visible area of the scrollable Widget. If the "scroll on focus" feature is enabled LVGL will automatically scroll Widgets to bring the child Widget with focus into view. The scrolling happens recursively therefore even nested scrollable Widgets are handled properly. The Widget will be scrolled into view even if it is on a different page of a tabview.
想象一下,有很多物体在一个可滚动的对象上。按下“Tab”按钮会将焦点转移到下一个物体,但它可能超出了可滚动对象的可见区域。 如果启用了“焦点时滚动”功能,LVGL会自动滚动物体以使它们的子对象出现在视图中。滚动是递归进行的,因此即使是嵌套的可滚动对象也会被正确处理。 即使对象在选项卡视图的不同页面上,它也会被滚动到视图中。
Scrolling Programmatically
显示原文
The following API functions allow programmatic scrolling of Widgets:
lv_obj_scroll_by(widget, x, y, LV_ANIM_ON/OFF)
scroll byx
andy
valueslv_obj_scroll_to(widget, x, y, LV_ANIM_ON/OFF)
scroll to bring the given coordinate to the top left cornerlv_obj_scroll_to_x(widget, x, LV_ANIM_ON/OFF)
scroll to bring the given coordinate to the left sidelv_obj_scroll_to_y(widget, y, LV_ANIM_ON/OFF)
scroll to bring the given coordinate to the top side
From time to time you may need to retrieve the scroll position of a scrollable Widget, either to restore it later, or to dynamically display some elements according to its current scroll position. Here is an example to illustrate how to combine scroll event and store the scroll-top position.
static int scroll_value = 0;
static void store_scroll_top_value_event_cb(lv_event_t* e) {
lv_obj_t * scr = lv_event_get_target(e);
scroll_value = lv_obj_get_scroll_top(scr);
printf("%d pixels are scrolled above top edge of display.\n", scroll_value);
}
lv_obj_t * scr = lv_obj_create(NULL);
lv_obj_add_event_cb(scr, store_scroll_top_value_event_cb, LV_EVENT_SCROLL, NULL);
Scroll coordinates can be retrieved from different axes with these functions:
lv_obj_get_scroll_x(widget) Pixels scrolled past left edge of Widget's view window.
lv_obj_get_scroll_y(widget) Pixels scrolled past top of Widget's view window.
lv_obj_get_scroll_top(widget) Identical to lv_obj_get_scroll_y(widget)
lv_obj_get_scroll_bottom(widget) Pixels scrolled past bottom of Widget's view window.
lv_obj_get_scroll_left(widget) Identical to lv_obj_get_scroll_x(widget).
lv_obj_get_scroll_right(widget) Pixels scrolled past right edge of Widget's view window.
Setting scroll position can be done with these functions:
lv_obj_scroll_by(widget, dx, dy, anim_enable) Scroll by given amount of pixels.
lv_obj_scroll_by_bounded(widget, dx, dy, animation_enable) Scroll by given amount of pixels.
lv_obj_scroll_to(widget, x, y, animation_enable) Scroll to given coordinate on Widget.
lv_obj_scroll_to_x(widget, x, animation_enable) Scroll to X coordinate on Widget.
lv_obj_scroll_to_y(widget, y, animation_enable) Scroll to Y coordinate on Widget.
lv_obj_scroll_to_view(widget, animation_enable) Scroll
obj
's parent Widget untilobj
becomes visible.lv_obj_scroll_to_view_recursive(widget, animation_enable) Scroll
obj
's parent Widgets recursively untilobj
becomes visible.
以下API函数可以用于以编程方式滚动Widgets:
lv_obj_scroll_by(widget, x, y, LV_ANIM_ON/OFF)
根据``x``和``y``的值进行滚动。lv_obj_scroll_to(widget, x, y, LV_ANIM_ON/OFF)
滚动以将指定坐标移动到左上角。lv_obj_scroll_to_x(widget, x, LV_ANIM_ON/OFF)
滚动以将指定坐标移动到左侧。lv_obj_scroll_to_y(widget, y, LV_ANIM_ON/OFF)
滚动以将指定坐标移动到顶部。
有时,您可能需要获取可滚动Widget的*滚动位置*,以便稍后恢复,或者根据当前的滚动位置动态显示一些元素。以下示例说明了如何结合滚动事件存储滚动顶部的位置:
static int scroll_value = 0;
static void store_scroll_top_value_event_cb(lv_event_t* e) {
lv_obj_t * scr = lv_event_get_target(e);
scroll_value = lv_obj_get_scroll_top(scr);
printf("%d pixels are scrolled above top edge of display.\n", scroll_value);
}
lv_obj_t * scr = lv_obj_create(NULL);
lv_obj_add_event_cb(scr, store_scroll_top_value_event_cb, LV_EVENT_SCROLL, NULL);
可以通过以下函数从不同的轴获取滚动坐标:
lv_obj_get_scroll_x(widget) 滚动超过Widget视窗左侧的像素。
lv_obj_get_scroll_y(widget) 滚动超过Widget视窗顶部的像素。
lv_obj_get_scroll_top(widget) 等同于:cpp:expr:lv_obj_get_scroll_y(widget)。
lv_obj_get_scroll_bottom(widget) 滚动超过Widget视窗底部的像素。
lv_obj_get_scroll_left(widget) 等同于:cpp:expr:lv_obj_get_scroll_x(widget)。
lv_obj_get_scroll_right(widget) 滚动超过Widget视窗右侧的像素。
可以通过以下函数设置滚动位置:
lv_obj_scroll_by(widget, dx, dy, anim_enable) 按指定像素量滚动。
lv_obj_scroll_by_bounded(widget, dx, dy, animation_enable) 按指定像素量滚动。
lv_obj_scroll_to(widget, x, y, animation_enable) 滚动到Widget上的指定坐标。
lv_obj_scroll_to_x(widget, x, animation_enable) 滚动到Widget上的X坐标。
lv_obj_scroll_to_y(widget, y, animation_enable) 滚动到Widget上的Y坐标。
lv_obj_scroll_to_view(widget, animation_enable) 滚动父Widget直到``obj``变得可见。
lv_obj_scroll_to_view_recursive(widget, animation_enable) 递归滚动父Widget直到``obj``变得可见。
Self Size(自身尺寸)
显示原文
Self size is a property of a Widget. Normally, the user shouldn't use this parameter but if a custom widget is created it might be useful.
In short, self size establishes the size of a Widget's content. To understand it better take the example of a table. Let's say it has 10 rows each with 50 px height. So the total height of the content is 500 px. In other words the "self height" is 500 px. If the user sets only 200 px height for the table LVGL will see that the self size is larger and make the table scrollable.
This means not only the children can make a Widget scrollable but a larger self size will as well.
LVGL uses the LV_EVENT_GET_SELF_SIZE
event to get the self size of
a Widget. Here is an example to see how to handle the event:
if(event_code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
/* If x or y < 0 then it doesn't need to be calculated now. */
if(p->x >= 0) {
p->x = 200; /* Set or calculate self width. */
}
if(p->y >= 0) {
p->y = 50; /* Set or calculate self height. */
}
}
自尺寸(Self size)是Widget的一个属性。通常,用户不需要直接使用这个参数,但在创建自定义Widget时可能会很有用。
简而言之,自尺寸确定了Widget内容的大小。为了更好地理解这一点,可以看一个表格的例子。假设表格有10行,每行50像素高。那么内容的总高度是500像素。换句话说,“自高度”是500像素。如果用户仅为表格设置了200像素的高度,LVGL会看到自尺寸更大,从而使表格变为可滚动。
这意味着,不仅子对象的大小可以使Widget变为可滚动,一个较大的自尺寸同样也会让Widget变得可滚动。
LVGL使用:cpp:enumerator:`LV_EVENT_GET_SELF_SIZE`事件来获取Widget的自尺寸。以下是一个处理该事件的示例:
if(event_code == LV_EVENT_GET_SELF_SIZE) {
lv_point_t * p = lv_event_get_param(e);
/* 如果x或y < 0,则不需要立即计算 */
if(p->x >= 0) {
p->x = 200; /* 设置或计算自宽度 */
}
if(p->y >= 0) {
p->y = 50; /* 设置或计算自高度 */
}
}
Examples
Nested scrolling
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES
static lv_obj_t * panel;
static lv_obj_t * save_button;
static lv_obj_t * restore_button;
static int saved_scroll_x;
static int saved_scroll_y;
static void scroll_update_cb(lv_event_t * e);
static void button_event_cb(lv_event_t * e);
static void scroll_update_cb(lv_event_t * e)
{
LV_UNUSED(e);
LV_LOG("scroll info: x:%3"LV_PRId32", y:%3"LV_PRId32", top:%3"LV_PRId32", "
"bottom:%3"LV_PRId32", left:%3"LV_PRId32", right:%3"LV_PRId32"\n",
lv_obj_get_scroll_x(panel),
lv_obj_get_scroll_y(panel),
lv_obj_get_scroll_top(panel),
lv_obj_get_scroll_bottom(panel),
lv_obj_get_scroll_left(panel),
lv_obj_get_scroll_right(panel)
);
}
static void button_event_cb(lv_event_t * e)
{
lv_obj_t * obj = lv_event_get_target_obj(e);
if(obj == save_button) {
saved_scroll_x = lv_obj_get_scroll_x(panel);
saved_scroll_y = lv_obj_get_scroll_y(panel);
}
else {
lv_obj_scroll_to(panel, saved_scroll_x, saved_scroll_y, LV_ANIM_ON);
}
}
/**
* Demonstrate how scrolling appears automatically
*/
void lv_example_scroll_1(void)
{
/*Create an object with the new style*/
lv_obj_t * scr;
scr = lv_screen_active();
panel = lv_obj_create(scr);
lv_obj_set_size(panel, 200, 200);
lv_obj_align(panel, LV_ALIGN_CENTER, 44, 0);
lv_obj_t * child;
lv_obj_t * label;
child = lv_obj_create(panel);
lv_obj_set_pos(child, 0, 0);
lv_obj_set_size(child, 70, 70);
label = lv_label_create(child);
lv_label_set_text(label, "Zero");
lv_obj_center(label);
child = lv_obj_create(panel);
lv_obj_set_pos(child, 160, 80);
lv_obj_set_size(child, 80, 80);
lv_obj_t * child2 = lv_button_create(child);
lv_obj_set_size(child2, 100, 50);
label = lv_label_create(child2);
lv_label_set_text(label, "Right");
lv_obj_center(label);
child = lv_obj_create(panel);
lv_obj_set_pos(child, 40, 160);
lv_obj_set_size(child, 100, 70);
label = lv_label_create(child);
lv_label_set_text(label, "Bottom");
lv_obj_center(label);
/* When LV_OBJ_FLAG_SCROLL_ELASTIC is cleared, scrolling does not go past edge bounaries. */
/* lv_obj_clear_flag(panel, LV_OBJ_FLAG_SCROLL_ELASTIC); */
/* Call `scroll_update_cb` while panel is being scrolled. */
lv_obj_add_event_cb(panel, scroll_update_cb, LV_EVENT_SCROLL, NULL);
/* Set up buttons that save and restore scroll position. */
save_button = lv_button_create(scr);
restore_button = lv_button_create(scr);
lv_obj_t * lbl;
lbl = lv_label_create(save_button);
lv_label_set_text_static(lbl, "Save");
lbl = lv_label_create(restore_button);
lv_label_set_text_static(lbl, "Restore");
lv_obj_align_to(save_button, panel, LV_ALIGN_OUT_LEFT_MID, -10, -20);
lv_obj_align_to(restore_button, panel, LV_ALIGN_OUT_LEFT_MID, -10, 20);
lv_obj_add_event_cb(save_button, button_event_cb, LV_EVENT_CLICKED, NULL);
lv_obj_add_event_cb(restore_button, button_event_cb, LV_EVENT_CLICKED, NULL);
}
#endif
Snapping
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX
static void sw_event_cb(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t * sw = lv_event_get_target(e);
if(code == LV_EVENT_VALUE_CHANGED) {
lv_obj_t * list = lv_event_get_user_data(e);
if(lv_obj_has_state(sw, LV_STATE_CHECKED)) lv_obj_add_flag(list, LV_OBJ_FLAG_SCROLL_ONE);
else lv_obj_remove_flag(list, LV_OBJ_FLAG_SCROLL_ONE);
}
}
/**
* Show an example to scroll snap
*/
void lv_example_scroll_2(void)
{
lv_obj_t * panel = lv_obj_create(lv_screen_active());
lv_obj_set_size(panel, 280, 120);
lv_obj_set_scroll_snap_x(panel, LV_SCROLL_SNAP_CENTER);
lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_ROW);
lv_obj_align(panel, LV_ALIGN_CENTER, 0, 20);
uint32_t i;
for(i = 0; i < 10; i++) {
lv_obj_t * btn = lv_button_create(panel);
lv_obj_set_size(btn, 150, lv_pct(100));
lv_obj_t * label = lv_label_create(btn);
if(i == 3) {
lv_label_set_text_fmt(label, "Panel %"LV_PRIu32"\nno snap", i);
lv_obj_remove_flag(btn, LV_OBJ_FLAG_SNAPPABLE);
}
else {
lv_label_set_text_fmt(label, "Panel %"LV_PRIu32, i);
}
lv_obj_center(label);
}
lv_obj_update_snap(panel, LV_ANIM_ON);
#if LV_USE_SWITCH
/*Switch between "One scroll" and "Normal scroll" mode*/
lv_obj_t * sw = lv_switch_create(lv_screen_active());
lv_obj_align(sw, LV_ALIGN_TOP_RIGHT, -20, 10);
lv_obj_add_event_cb(sw, sw_event_cb, LV_EVENT_ALL, panel);
lv_obj_t * label = lv_label_create(lv_screen_active());
lv_label_set_text(label, "One scroll");
lv_obj_align_to(label, sw, LV_ALIGN_OUT_BOTTOM_MID, 0, 5);
#endif
}
#endif
Styling the scrollbars
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_LIST
/**
* Styling the scrollbars
*/
void lv_example_scroll_4(void)
{
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_set_size(obj, 200, 100);
lv_obj_center(obj);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label,
"Lorem ipsum dolor sit amet, consectetur adipiscing elit.\n"
"Etiam dictum, tortor vestibulum lacinia laoreet, mi neque consectetur neque, vel mattis odio dolor egestas ligula. \n"
"Sed vestibulum sapien nulla, id convallis ex porttitor nec. \n"
"Duis et massa eu libero accumsan faucibus a in arcu. \n"
"Ut pulvinar odio lorem, vel tempus turpis condimentum quis. Nam consectetur condimentum sem in auctor. \n"
"Sed nisl augue, venenatis in blandit et, gravida ac tortor. \n"
"Etiam dapibus elementum suscipit. \n"
"Proin mollis sollicitudin convallis. \n"
"Integer dapibus tempus arcu nec viverra. \n"
"Donec molestie nulla enim, eu interdum velit placerat quis. \n"
"Donec id efficitur risus, at molestie turpis. \n"
"Suspendisse vestibulum consectetur nunc ut commodo. \n"
"Fusce molestie rhoncus nisi sit amet tincidunt. \n"
"Suspendisse a nunc ut magna ornare volutpat.");
/*Remove the style of scrollbar to have clean start*/
lv_obj_remove_style(obj, NULL, LV_PART_SCROLLBAR | LV_STATE_ANY);
/*Create a transition the animate the some properties on state change*/
static const lv_style_prop_t props[] = {LV_STYLE_BG_OPA, LV_STYLE_WIDTH, 0};
static lv_style_transition_dsc_t trans;
lv_style_transition_dsc_init(&trans, props, lv_anim_path_linear, 200, 0, NULL);
/*Create a style for the scrollbars*/
static lv_style_t style;
lv_style_init(&style);
lv_style_set_width(&style, 4); /*Width of the scrollbar*/
lv_style_set_pad_right(&style, 5); /*Space from the parallel side*/
lv_style_set_pad_top(&style, 5); /*Space from the perpendicular side*/
lv_style_set_radius(&style, 2);
lv_style_set_bg_opa(&style, LV_OPA_70);
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_border_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 3));
lv_style_set_border_width(&style, 2);
lv_style_set_shadow_width(&style, 8);
lv_style_set_shadow_spread(&style, 2);
lv_style_set_shadow_color(&style, lv_palette_darken(LV_PALETTE_BLUE, 1));
lv_style_set_transition(&style, &trans);
/*Make the scrollbars wider and use 100% opacity when scrolled*/
static lv_style_t style_scrolled;
lv_style_init(&style_scrolled);
lv_style_set_width(&style_scrolled, 8);
lv_style_set_bg_opa(&style_scrolled, LV_OPA_COVER);
lv_obj_add_style(obj, &style, LV_PART_SCROLLBAR);
lv_obj_add_style(obj, &style_scrolled, LV_PART_SCROLLBAR | LV_STATE_SCROLLED);
}
#endif
Right to left scrolling
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_FONT_DEJAVU_16_PERSIAN_HEBREW
/**
* Scrolling with Right To Left base direction
*/
void lv_example_scroll_5(void)
{
lv_obj_t * obj = lv_obj_create(lv_screen_active());
lv_obj_set_style_base_dir(obj, LV_BASE_DIR_RTL, 0);
lv_obj_set_size(obj, 200, 100);
lv_obj_center(obj);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label,
"میکروکُنترولر (به انگلیسی: Microcontroller) گونهای ریزپردازنده است که دارای حافظهٔ دسترسی تصادفی (RAM) و حافظهٔ فقطخواندنی (ROM)، تایمر، پورتهای ورودی و خروجی (I/O) و درگاه ترتیبی (Serial Port پورت سریال)، درون خود تراشه است، و میتواند به تنهایی ابزارهای دیگر را کنترل کند. به عبارت دیگر یک میکروکنترلر، مدار مجتمع کوچکی است که از یک CPU کوچک و اجزای دیگری مانند تایمر، درگاههای ورودی و خروجی آنالوگ و دیجیتال و حافظه تشکیل شدهاست.");
lv_obj_set_width(label, 400);
lv_obj_set_style_text_font(label, &lv_font_dejavu_16_persian_hebrew, 0);
}
#endif
Translate on scroll
C code
View on GitHub#include "../lv_examples.h"
#if LV_BUILD_EXAMPLES && LV_USE_FLEX
static void scroll_event_cb(lv_event_t * e)
{
lv_obj_t * cont = lv_event_get_target(e);
lv_area_t cont_a;
lv_obj_get_coords(cont, &cont_a);
int32_t cont_y_center = cont_a.y1 + lv_area_get_height(&cont_a) / 2;
int32_t r = lv_obj_get_height(cont) * 7 / 10;
uint32_t i;
uint32_t child_cnt = lv_obj_get_child_count(cont);
for(i = 0; i < child_cnt; i++) {
lv_obj_t * child = lv_obj_get_child(cont, i);
lv_area_t child_a;
lv_obj_get_coords(child, &child_a);
int32_t child_y_center = child_a.y1 + lv_area_get_height(&child_a) / 2;
int32_t diff_y = child_y_center - cont_y_center;
diff_y = LV_ABS(diff_y);
/*Get the x of diff_y on a circle.*/
int32_t x;
/*If diff_y is out of the circle use the last point of the circle (the radius)*/
if(diff_y >= r) {
x = r;
}
else {
/*Use Pythagoras theorem to get x from radius and y*/
uint32_t x_sqr = r * r - diff_y * diff_y;
lv_sqrt_res_t res;
lv_sqrt(x_sqr, &res, 0x8000); /*Use lvgl's built in sqrt root function*/
x = r - res.i;
}
/*Translate the item by the calculated X coordinate*/
lv_obj_set_style_translate_x(child, x, 0);
/*Use some opacity with larger translations*/
lv_opa_t opa = lv_map(x, 0, r, LV_OPA_TRANSP, LV_OPA_COVER);
lv_obj_set_style_opa(child, LV_OPA_COVER - opa, 0);
}
}
/**
* Translate the object as they scroll
*/
void lv_example_scroll_6(void)
{
lv_obj_t * cont = lv_obj_create(lv_screen_active());
lv_obj_set_size(cont, 200, 200);
lv_obj_center(cont);
lv_obj_set_flex_flow(cont, LV_FLEX_FLOW_COLUMN);
lv_obj_add_event_cb(cont, scroll_event_cb, LV_EVENT_SCROLL, NULL);
lv_obj_set_style_radius(cont, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_clip_corner(cont, true, 0);
lv_obj_set_scroll_dir(cont, LV_DIR_VER);
lv_obj_set_scroll_snap_y(cont, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scrollbar_mode(cont, LV_SCROLLBAR_MODE_OFF);
uint32_t i;
for(i = 0; i < 20; i++) {
lv_obj_t * btn = lv_button_create(cont);
lv_obj_set_width(btn, lv_pct(100));
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text_fmt(label, "Button %"LV_PRIu32, i);
}
/*Update the buttons position manually for first*/
lv_obj_send_event(cont, LV_EVENT_SCROLL, NULL);
/*Be sure the fist button is in the middle*/
lv_obj_scroll_to_view(lv_obj_get_child(cont, 0), LV_ANIM_OFF);
}
#endif