How to scroll items circularly?

Important: unclear posts may not receive useful answers.

Before posting

  • Get familiar with Markdown to format and structure your post
  • Be sure to update lvgl from the latest version from the master branch.
  • Be sure you have checked the FAQ and read the relevant part of the documentation.
  • If applicable use the Simulator to eliminate hardware related issues.

Delete this section if you read and applied the mentioned points.

Description

Hi guys,I just wanna know,how can I scroll right from the first item to the last item in a chain of items?
I have no idea,maybe you guys can help me and give me some thoughts.

What MCU/Processor/Board and compiler are you using?

it doesn’t matter.

What LVGL version are you using?

released 8.2.0

What do you want to achieve?

Just like I said above,just want to achieve scrolling circularly in any situations.

What have you tried so far?

I thought so much,but I didn’t know how to do it,

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

    /*Just like a simple code right here*/

    lv_obj_t *parent = lv_obj_create(lv_scr_act());
    lv_obj_set_size(parent, 240, 240);
    lv_obj_center(parent);

    lv_obj_t *item_1 = lv_obj_create(parent);
    lv_obj_set_size(item_1, 240, 240);
    lv_obj_center(item_1);

    lv_obj_t *item_2 = lv_obj_create(parent);
    lv_obj_set_size(item_2, 240, 240);
    lv_obj_align_to(item_2, item_1, LV_ALIGN_TOP_RIGHT, 240, 0);

    lv_obj_t *item_3 = lv_obj_create(parent);
    lv_obj_set_size(item_3, 240, 240);
    lv_obj_align_to(item_3, item_2, LV_ALIGN_TOP_RIGHT, 240, 0);

    /* So code above form a chain  of items,like 1-2-3,and I don't how to scroll 1 right to let screen focus on 3*/

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

Hi,

Unfortunately, there is no out of box solution for this and it’s quite hard to implement.

We were thinking about supporting it in some for but well… it’s a complicated and I’d like to be sure there is a real need for this feature.

However, your example is a special case because a child covers the whole parent. So there are some simplification options. I think the simplest would be to use gestures like this:

static void item_gesture_event(lv_event_t * e)
{
    lv_obj_t * item = lv_event_get_target(e);
    lv_obj_t * parent = lv_obj_get_parent(item);
    lv_dir_t dir = lv_indev_get_gesture_dir(lv_indev_get_act());

    /*Move the last item to first place or vice versa*/ 
    lv_obj_t * item_next = NULL;
    if(dir == LV_DIR_RIGHT) {
        lv_obj_move_to_index(lv_obj_get_child(parent, -1), 0);
        item_next = lv_obj_get_child(parent, lv_obj_get_index(item) - 1);
    }
    else if(dir == LV_DIR_LEFT) {
        lv_obj_move_to_index(lv_obj_get_child(parent, 0), -1);
        item_next = lv_obj_get_child(parent, lv_obj_get_index(item) + 1);
    }

    if(item_next) {
        lv_obj_scroll_to_view(item, LV_ANIM_OFF);
        lv_obj_scroll_to_view(item_next, LV_ANIM_ON);
    }
}

...

  lv_obj_t *parent = lv_obj_create(lv_scr_act());
  lv_obj_set_size(parent, 240, 240);
  lv_obj_center(parent);
  lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_ROW);

  uint32_t i;
  for(i = 0; i < 10; i++) {
      lv_obj_t *item = lv_obj_create(parent);
      lv_obj_set_size(item, lv_pct(100), lv_pct(100));
      lv_obj_clear_flag(item, LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_GESTURE_BUBBLE);
      lv_obj_add_event_cb(item, item_gesture_event, LV_EVENT_GESTURE, NULL);
      
      lv_obj_t * label = lv_label_create(item);
      lv_label_set_text_fmt(label, "%d", i+1);
      lv_obj_center(label);
  }

  /*Move the last item to the first place and focus on the second item*/
  lv_obj_move_to_index(lv_obj_get_child(parent, -1), 0);
  lv_obj_scroll_to_view(lv_obj_get_child(parent, 1), LV_ANIM_OFF);

s

Wow,that kind of solution is quite amazing!
Yes,I agree with you,it is just a need of my single test project,and really it is hard to implement.But I guess kind of that feature will finally be needed sooner or later,after all,circular is more fancy.
I do remember if you apply SCROLLABLE,then cannot use GUSTURE.And I saw your code haven’t use ‘lv_obj_clear_flag(item1,LV_OBJ_FLAG_SCROLLABLE)’,why use it without faults?

This line makes the trick:

      lv_obj_clear_flag(item, LV_OBJ_FLAG_SCROLL_CHAIN | LV_OBJ_FLAG_GESTURE_BUBBLE);

Due to this the items won’t propagate the scrolling and gestures to the parent. This way the gesture is consumed by the item which is not scrolled (the parent is scrolled).

1 Like

Thanks for that.And I think I’ve figured out the problem.I got a easy way to complete that.
What if I create two more items at the head of children and tail of chilren to fake our eyes when scrolling?I set the first item duplicated of the second to last one,and set the last item duplicated of the second item,which means,1-4 are the real items I need,0 and 5 are faker two.That makes items can scroll circularly although waste two items.
Here is my code,and I’ve tested it,performances well.

void test_circular_scroll_end_cb(lv_event_t *e)
{
    uint32_t i, active_index;
    lv_obj_t *obj_current = lv_event_get_current_target(e);
    uint32_t child_cnt = lv_obj_get_child_cnt(obj_current);

    for (i = 0; i < child_cnt; i++)
    {
        lv_obj_t *item = lv_obj_get_child(obj_current, i);
        lv_area_t area;
        lv_obj_get_coords(item, &area);
        if (area.x1 >= 0 && area.x2 <= LV_HOR_RES && area.y1 >= 0 && area.y1 <= LV_VER_RES)
        {
            active_index = i;
            break;
        }
    }

    if (active_index == 0) // The first place which is prepared to fill the second to last item in.
    {
        lv_obj_scroll_to_view(lv_obj_get_child(obj_current, child_cnt - 2), LV_ANIM_OFF);
    }
    else if (active_index == child_cnt - 1) // The last place which is prepared to fill the second item in.
    {
        lv_obj_scroll_to_view(lv_obj_get_child(obj_current, 1), LV_ANIM_OFF);
    }
}

...

void test_circular_scroll2()
{
    uint32_t item_cnt = 6;
    lv_obj_t *parent = lv_obj_create(lv_scr_act());
    lv_obj_set_size(parent, 240, 240);
    lv_obj_center(parent);
    lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_ROW);
    lv_obj_set_scroll_snap_x(parent, LV_SCROLL_SNAP_CENTER);
    lv_obj_set_style_pad_all(parent, 0, 0);

    uint32_t i;
    for (i = 0; i < item_cnt ; i++)
    {
        lv_obj_t *item = lv_obj_create(parent);
        lv_obj_set_size(item, lv_pct(100), lv_pct(100));
        lv_obj_add_flag(item, LV_OBJ_FLAG_EVENT_BUBBLE);

        lv_obj_t *label = lv_label_create(item);
        if (i == 0) 
        {
            lv_label_set_text(label, "4"); // Simple duplication
        }
        else if (i == item_cnt  - 1)
        {
            lv_label_set_text(label, "1"); // Simple duplication
        }
        else
        {
            lv_label_set_text_fmt(label, "%d", i);
        }

        lv_obj_center(label);
    };

    lv_obj_add_event_cb(parent, test_circular_scroll_end_cb, LV_EVENT_SCROLL_END, NULL);

    lv_obj_scroll_to_view(lv_obj_get_child(parent, 1), LV_ANIM_OFF);
}

It can also work, but what if someone scrolls a really large. If the momentum lasts for 2-3 items. In this case even the first/last fake items will be crossed.

The ONE_SCROLL flag can help with it as shown in this example.

Yeah,just meets my single situations,I will add the flag to code.
Thanks a lot again,learn much from you! :slightly_smiling_face:

1 Like

Great, I’m happy to help if I can. :slight_smile: