LVGL学习(5):物理按键切换焦点之焦点保存和恢复

这篇具有很好参考价值的文章主要介绍了LVGL学习(5):物理按键切换焦点之焦点保存和恢复。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

最近在做的项目用到了LVGL,基于实际产品使用的特殊情况,屏幕没有接触摸屏,而是使用物理按键来控制所有的object,而且硬件上只有四个按键,功能分别是:返回、左/上、右/下和确定,在切换界面的过程中,也引出了一个焦点切换问题。

1 焦点切换问题

既然项目中我们是使用按键来做界面切换的,也就是说我们是没有触摸屏的,所以按键还需要充当切换焦点的作用。但是对于前面的隐藏当前界面再创建新界面来说,隐藏界面并不会删除隐藏界面的焦点,而如果我们删掉之前界面的所有焦点肯定也不行。在这种情况下,在切换焦点时还会切换到隐藏界面中一些对象的焦点,即使你看不到隐藏的对象,这肯定是不能接受的。

解决方案
1、通过lv_group_get_focused()获得当前界面的焦点并记录,然后切换界面时就采用删除界面的方式,再后续返回时,通过之前记录的焦点使用函数lv_group_focus_obj进行focus。
2、隐藏当前界面,但是要记录当前聚焦的对象,然后删除当前界面的所有可聚焦的对象。这样在新界面中按键切换时就不会切换到隐藏的界面中。最后在返回窗口时恢复所有可聚焦的对象和当前聚焦的对象即可。

对于第一种方法来说,如果要判断是哪个对象,需要遍历界面中的每一个对象,这就比较困难了,因为一个界面的所有对象的指针并不在一个数组中,而是可能定义在当前界面的一个结构体中,当然理论上我们也可以遍历结构体,因为一个任意类型指针的大小都是固定的,一般为4字节。接着在获取了这个对象后,由于后续还要删除掉界面,所以不能记录焦点对象的指针,而是需要给一个界面的每个焦点进行编号,所以第一种方法的实现是比较复杂的。

另外,无论对于第一种还是第二种方案来说,还有一个问题需要解决,就是界面中聚焦的焦点可能还会在某个对象的子对象中。经过以上分析,现在我们就来实现上面说的第二种保存对象焦点的方案。

2 焦点切换原理

LVGL中的焦点切换是通过组来实现的,切换焦点时,就是按照组中焦点的顺序进行切换的。所以如果要切换焦点,首先要通过函数lv_group_add_obj把对象加入组中:

void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)

有的对象在创建的时候是自动加入组的,有的则需要手动加入。以lv_btn.clv_imgbtn.c为例,它们的lv_obj_class_t的定义如下

const lv_obj_class_t lv_btn_class  = {
    .constructor_cb = lv_btn_constructor,
    .width_def = LV_SIZE_CONTENT,
    .height_def = LV_SIZE_CONTENT,
    .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
    .instance_size = sizeof(lv_btn_t),
    .base_class = &lv_obj_class
};

const lv_obj_class_t lv_imgbtn_class = {
    .base_class = &lv_obj_class,
    .instance_size = sizeof(lv_imgbtn_t),
    .constructor_cb = lv_imgbtn_constructor,
    .event_cb = lv_imgbtn_event,
};

可以发现对于lv_btn_t来说,它的group_def定义为LV_OBJ_CLASS_GROUP_DEF_TRUE,通过查看代码可以发现,在对象创建的过程中有判断group_def字段:

bool lv_obj_is_group_def(lv_obj_t * obj)
{
    const lv_obj_class_t * class_p = obj->class_p;

    /*Find a base in which group_def is set*/
    while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;

    if(class_p == NULL) return false;

    return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE ? true : false;
}

void lv_obj_class_init_obj(lv_obj_t * obj)
{
	...
	lv_group_t * def_group = lv_group_get_default();
    if(def_group && lv_obj_is_group_def(obj)) {
        lv_group_add_obj(def_group, obj);
    }
    ...
}

也就是说group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE的对象创建时将自动加入当前的默认组中。

3 焦点的保存和恢复

3.1 源码分析

首先我们需要知道在LVGL中是使用什么结构将各个对象保存到group中的。以lv_group_add_obj函数为例我们看看是怎么实现的(省略与我们需要的无关代码):

void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
{
    ...
    lv_obj_t ** next = _lv_ll_ins_tail(&group->obj_ll);
    LV_ASSERT_MALLOC(next);
    if(next == NULL) return;
    *next = obj;
	...

可以看到,在每个组中有一个obj_ll来保存组中的对象,它的数据类型是lv_ll_t,不难看出它是一个链表。如果要详细分析的话,篇幅有些过长,所以单独写了一篇博客,参考:LVGL源码分析(1):lv_ll链表的实现

  • 对于这个LVGL源码分析系列的博客,其实LVGL中很多代码还是挺有意思的,如觉得有必要写就评论一下哪个部分的源码,不然我暂时应该不会继续更新了。

3.2 焦点保存的实现

假设我们所有页面使用一个group,且它为默认组,首先在每一个界面的结构体中我们都声明一个lv_ll_t并初始化,用于记录焦点。

lv_ll_t focus_ll;
_lv_ll_init(&page->focus_ll, sizeof(lv_obj_t *));

在切换窗口时,我们就可以通过下面的函数获取当前的焦点对应的对象然后插入到链表的最后。最后要从group中删除当前窗口的所有焦点。

lv_obj_t *tmp_focus = lv_group_get_focused(lv_group_get_default());
lv_obj_t ** next = _lv_ll_ins_tail(&page->focus_ll);
LV_ASSERT_MALLOC(next);
*next = tmp_focus;
lv_group_remove_all_objs_self(lv_group_get_default(), &page->focus_ll);

这里我小小地修改了lv_group_remove_all_objs函数为lv_group_remove_all_objs_self,就是将要移除的焦点先调用_lv_ll_ins_tail加入focus_ll再删除。然后链表的第一个元素为当前焦点所在的对象。

3.3 焦点的恢复

我们需要在切换回界面时将刚刚保存的焦点恢复:

/* 恢复之前记录的焦点 */
lv_obj_t ** obj;
_LV_LL_READ(&page->focus_ll, obj) {
	lv_group_add_obj(lv_group_get_default(), *obj);
}
obj = _lv_ll_get_head(&page->focus_ll);
lv_group_focus_obj(*obj);

所以上面的代码首先恢复所有的焦点,最后再取出链表中的第一个元素聚焦即可。前面我们把当前焦点所在的对象先加入到了链表中的第一个,实际上是通过阅读lv_group_add_obj源码可以知道,加入一个已经存在的对象,会将之前的删除掉然后再加入到链表的最后面。如果源码中没有替换的话,就是加入焦点所在对象到链表最后一个。

4 总结

本文实现了页面中焦点的保存和恢复,了解了LVGL中对于对象及焦点的保存。而在我写博客的过程中,又想到一个方案:在每一个界面创建时都创建一个group,并设置为默认组,然后在删除界面时删除这个group。最后我们需要在每次创建或切换界面的同时,调用lv_indev_set_group来修改输入设备所对应的group。这种方式似乎比我上面实现的方式要更简洁高效。所以啊,办法都是想出来的,遇到了问题还是可以全方位地思考一下所有可能的解决方式。文章来源地址https://www.toymoban.com/news/detail-478803.html

到了这里,关于LVGL学习(5):物理按键切换焦点之焦点保存和恢复的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 简单的LVGL按键例程

    以下是一个简单的LVGL按键例程,使用LVGL的按键事件来处理按钮的单击和长按操作。 在这个例程中,我们创建了一个名为 btn 的按钮对象,并将其添加到当前活动屏幕上。然后,在按钮上创建了一个标签并设置了文本内容。接着,我们使用 lv_obj_set_event_cb() 函数注册了按钮事件

    2024年02月03日
    浏览(28)
  • 安卓焦点窗口切换

    我们经常会遇到一种 Application does not hava focused window 的ANR异常,这种异常一般是没有焦点窗口FocusedWindow导致,且这类异常只会发生在key事件的派发,因为key事件是需要找到一个焦点窗口然后再派发,而触摸事件只需要找到当前显示的窗口即可 焦点窗口是指当前正在与用户交互

    2024年04月14日
    浏览(34)
  • lvgl8.x 对接实体按键驱动

    实体按键属于 lvgl 的输入设备中的一种,所以对接外部的硬件实体按键实际上就是为 lvgl 添加输入设备。为 lvgl 添加输入设备需要在 lv_port_indev.c 这个 c 文件中完成, 注意这个文件并不存在于 lvgl src 源码文件夹下,而是位于 examples/porting 文件夹下,在这个目录下官方为我们准

    2024年02月11日
    浏览(25)
  • 【Vue】输入框状态切换&自动获取输入框焦点の实现

    场景:点击 button 按钮展示输入框,并自动获取对话框焦点,失去焦点时展示 button 按钮 实现: 点击 button 按钮展示输入框,失去焦点时展示 button 按钮 在data中定义 visibility ,确定输入框的展示状态,默认为 false 定义 changeVisibility 方法,并给 button 绑定点击事件@ click=\\\"changeVi

    2024年02月12日
    浏览(25)
  • 【esp32&lvgl】-2.6 #lvgl-多页面(screen)设定/切换

    目录 一、前言 二、实现原理 2.1 各个screen的定义及初始化 2.2 各个screen内的内容绘制 2.3 页面切换的events_handler 三、代码实现 3.1 PageManage库 3.2 Page_mainMenu库(界面) 3.3 Page_wifi库(界面) 参考文献         利用lvgl框架绘制GUI免不了需要实现多个页面的切换,毕竟把所有功

    2024年02月02日
    浏览(36)
  • lvgl实现动态切换横竖屏

    有两种方式。一种是通过lvgl自带的软件选择。但是这个效率很慢。而且只支持90度、180度、270度的旋转。不一定达到想要的效果。我需要实现的是这种效果。软件旋转没有办法实现。旋转后会镜像过去。而且如果你的屏幕不是等比例的。比如240*240  320*320软件旋转270度或者9

    2024年02月12日
    浏览(27)
  • Android物理按键事件处理

    当处理物理按钮事件的时候提供的回调方法有 onKeyDown() onKeyUp() onKeyLongPress() 如下是例子:返回键被按下,提示退出程序 按键名 中文名 code KEYCODE_POWER 电源键 26 KEYCODE_HOME HOME键 3 KEYCODE_BACK 返回键 4 KEYCODE_MENU 菜单键 82 KEYCODE_CAMERA 拍照键 27 KEYCODE_VOLUME_UP 音量增加键 24 KEYCODE_VOL

    2023年04月11日
    浏览(31)
  • 解决elementUI或elementPlus的按钮点击后需要失去焦点才能恢复原本样式问题

    废话不多说直接上代码,只需要在button中添加如下代码即可

    2024年01月22日
    浏览(35)
  • Qt之事件过滤器讲解并且实现快捷键切换鼠标焦点

    现在有一个类似于下方图的ui,用户需要在输入前一行内容后,需要摁下指定案件能够跳转到下一行继续进行输入。 一种更为直接的解决方案是子类化 QLineEdit 并且重新实现鼠标事件 keyPressEvent() ,然后调用 focusNextChild() 。 首先需要创建一个子类MyLineEdit继承于QLineEdit类。然后重

    2024年02月12日
    浏览(36)
  • 详解集群级备份恢复:物理细粒度备份恢复

    摘要: 在实际使用过程中,数据库集群级的故障并非高概率事件,如何安全高效地帮助客户备份恢复一部分数据库元素,才是更加实际的需求,这也是细粒度备份恢复的意义所在。 本文分享自华为云社区《GaussDB(DWS)之物理细粒度备份恢复》,作者:我的橘子呢 。 相对于

    2024年02月04日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包