Dzbj_ESP32-S3/main/ui/screens/ui_ScreenImg.c
Rdzleo 3dced23cfd feat: 新增图片删除功能 + 修复开机/唤醒闪烁问题
## 新增功能
- 图片删除:支持从 SPIFFS 彻底删除图片(物理删除 + 内存管理)
- ContainerDle 管理:新增半透明圆形删除容器(含删除和返回按钮)
- 状态管理:标志位模式管理 UI 状态,离开界面自动清理

## 解决的关键问题
1. 开机闪烁 (️)
   - 原因:LCD GRAM 保留旧数据
   - 方案:背光使能前清空 GRAM(DMA 分批填充黑色,34ms)

2. 低功耗唤醒闪烁 (️)
   - 原因:先恢复亮度后切换界面,看到旧界面
   - 方案:先切换界面(背光=0)→ 延时 100ms → 恢复亮度

3. ContainerDle 状态保留 (️)
   - 原因:LVGL 对象不销毁,状态被保留
   - 方案:离开界面时主动清理(手势/按键回调中调用隐藏函数)

4. 按键回调冲突 (️)
   - 原因:按键系统单回调限制
   - 方案:统一在 main.c 管理 BOOT 按键,其他模块通过接口调用

5. 开机加载图片闪烁 (️)
   - 原因:screen_init() 中 JPEG 解码触发渲染
   - 方案:延迟到 LV_EVENT_SCREEN_LOADED 事件加载

## 功能改动
- BOOT 按键增强:唤醒 + 退出手电筒 + 隐藏容器 + 返回 Home
- 图片界面优化:支持删除当前图片并自动显示下一张
- 休眠管理优化:移除 BOOT 注册,避免回调冲突

## 技术优化
- 资源节约:分批处理大数据(LCD GRAM 清除)
- 时序优化:根据屏幕状态智能调整唤醒时序
- 模块化设计:按键集中管理 + 接口清晰 + 状态标志模式

## 文件变更
- lcd/lcd.c: LCD GRAM 清除逻辑
- main.c: BOOT 按键统一管理
- pages/pages.c: 图片删除功能实现
- ui/screens/ui_ScreenImg.c: ContainerDle 管理 + 状态控制
- sleep_mgr/sleep_mgr.c: 按键回调优化
- ui/images: 新增删除和返回按钮图标 (s13.png, s14.png)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 16:51:51 +08:00

219 lines
8.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file was generated by SquareLine Studio
// SquareLine Studio version: SquareLine Studio 1.6.0
// LVGL version: 8.3.11
// Project name: Lcd_Pro
#include "../ui.h"
#include "ui_ScreenSet.h" // 引入ScreenSet的函数声明
#include "../../pages/include/pages.h" // 引入图片管理函数
#include "esp_log.h" // 用于日志输出
extern void init_spiffs_image_list(void);
extern void update_ui_ImgBle(const char *img_name);
extern void free_spiffs_image_list(void);
extern const char* get_next_image(void);
extern const char* get_prev_image(void);
lv_obj_t *ui_ScreenImg = NULL;
lv_obj_t *ui_ImgBle = NULL;
lv_obj_t *ui_ContainerDle = NULL;
lv_obj_t *ui_ImageDel = NULL;
lv_obj_t *ui_ImageReturn = NULL;
// 标志:是否首次加载
static bool first_load = true;
// 标志:是否需要显示 ContainerDle只有从 ScreenSet 点击 ImgDelete 时才为 true
static bool should_show_container = false;
// 显示 ContainerDle
void ui_ScreenImg_show_delete_container(void) {
should_show_container = true; // 设置标志,表示需要显示
if (ui_ContainerDle) {
lv_obj_clear_flag(ui_ContainerDle, LV_OBJ_FLAG_HIDDEN);
}
}
// 隐藏 ContainerDle
void ui_ScreenImg_hide_delete_container(void) {
should_show_container = false; // 清除标志
if (ui_ContainerDle) {
lv_obj_add_flag(ui_ContainerDle, LV_OBJ_FLAG_HIDDEN);
}
}
// ImageReturn 点击事件:隐藏容器
void ui_event_ImageReturn(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if (event_code == LV_EVENT_CLICKED) {
ui_ScreenImg_hide_delete_container();
}
}
// ImageDel 点击事件:删除当前图片
void ui_event_ImageDel(lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
if (event_code == LV_EVENT_CLICKED) {
// 删除当前图片
if (delete_current_image()) {
// 删除成功,隐藏容器
ui_ScreenImg_hide_delete_container();
// 获取下一张图片(内部已更新索引)
const char *next_img = get_current_image();
if (next_img) {
// 显示下一张图片
update_ui_ImgBle(next_img);
} else {
// 没有图片了,可以显示提示信息
// 这里暂不处理,保持当前界面
}
} else {
// 删除失败,仍然隐藏容器
ui_ScreenImg_hide_delete_container();
}
}
}
// event funtions
void ui_event_ScreenImg( lv_event_t * e) {
lv_event_code_t event_code = lv_event_get_code(e);
// 界面加载完成事件:首次进入时才初始化图片
if ( event_code == LV_EVENT_SCREEN_LOADED ) {
if (first_load) {
first_load = false;
// 初始化图片列表
init_spiffs_image_list();
// 获取第一张可用图片
const char *first_img = get_current_image();
if (first_img) {
// 显示第一张SPIFFS图片
update_ui_ImgBle(first_img);
} else {
// 没有图片保持显示UI资源图片 ui_img_s1_png
ESP_LOGI("ScreenImg", "SPIFFS无可用图片显示默认UI图片");
}
}
// 每次加载界面时检查是否需要显示 ContainerDle
// 只有从 ScreenSet 点击 ImgDelete 时 should_show_container 才为 true
if (should_show_container) {
// 需要显示,保持显示状态(已在 ui_ScreenImg_show_delete_container 中显示)
should_show_container = false; // 清除标志(下次进入默认不显示)
} else {
// 不需要显示,确保隐藏
ui_ScreenImg_hide_delete_container();
}
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_TOP ) {
lv_indev_wait_release(lv_indev_get_act());
// 离开界面前隐藏容器
ui_ScreenImg_hide_delete_container();
_ui_screen_change( &ui_ScreenHome, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenHome_screen_init);
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_BOTTOM ) {
lv_indev_wait_release(lv_indev_get_act());
// 离开界面前隐藏容器
ui_ScreenImg_hide_delete_container();
// 设置返回到Img界面
ui_ScreenSet_set_previous(&ui_ScreenImg, &ui_ScreenImg_screen_init);
_ui_screen_change( &ui_ScreenSet, LV_SCR_LOAD_ANIM_NONE, 0, 0, &ui_ScreenSet_screen_init);
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_LEFT ) {
lv_indev_wait_release(lv_indev_get_act());
const char *next_img = get_next_image();
if(next_img) {
update_ui_ImgBle(next_img);
}
}
if ( event_code == LV_EVENT_GESTURE && lv_indev_get_gesture_dir(lv_indev_get_act()) == LV_DIR_RIGHT ) {
lv_indev_wait_release(lv_indev_get_act());
const char *prev_img = get_prev_image();
if(prev_img) {
update_ui_ImgBle(prev_img);
}
}
}
// build funtions
void ui_ScreenImg_screen_init(void)
{
ui_ScreenImg = lv_obj_create(NULL);
lv_obj_clear_flag( ui_ScreenImg, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ImgBle = lv_img_create(ui_ScreenImg);
lv_img_set_src(ui_ImgBle, &ui_img_s1_png);
lv_obj_set_width( ui_ImgBle, 360);
lv_obj_set_height( ui_ImgBle, 360);
lv_obj_set_align( ui_ImgBle, LV_ALIGN_CENTER );
lv_obj_set_flex_flow(ui_ImgBle,LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(ui_ImgBle, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START);
lv_obj_add_flag( ui_ImgBle, LV_OBJ_FLAG_ADV_HITTEST ); /// Flags
lv_obj_clear_flag( ui_ImgBle, LV_OBJ_FLAG_SCROLLABLE ); /// Flags
ui_ContainerDle = lv_obj_create(ui_ScreenImg);
lv_obj_remove_style_all(ui_ContainerDle);
lv_obj_set_width( ui_ContainerDle, 360);
lv_obj_set_height( ui_ContainerDle, 360); // 圆形容器
lv_obj_set_x( ui_ContainerDle, 0 );
lv_obj_set_y( ui_ContainerDle, 240 ); // 向下偏移 240px显示顶部 1/3
lv_obj_set_align( ui_ContainerDle, LV_ALIGN_CENTER );
lv_obj_clear_flag( ui_ContainerDle, LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLLABLE );
// 设置圆形和裁剪
lv_obj_set_style_radius(ui_ContainerDle, 180, LV_PART_MAIN); // 圆角半径 180
lv_obj_set_style_clip_corner(ui_ContainerDle, true, LV_PART_MAIN); // 裁剪圆角外内容
lv_obj_set_style_bg_color(ui_ContainerDle, lv_color_hex(0x000000), LV_PART_MAIN);
lv_obj_set_style_bg_opa(ui_ContainerDle, 180, LV_PART_MAIN); // 半透明180/255 约 70%
lv_obj_add_flag( ui_ContainerDle, LV_OBJ_FLAG_HIDDEN ); // 默认隐藏
ui_ImageDel = lv_img_create(ui_ContainerDle);
lv_img_set_src(ui_ImageDel, &ui_img_s13_png);
lv_obj_set_width( ui_ImageDel, LV_SIZE_CONTENT);
lv_obj_set_height( ui_ImageDel, LV_SIZE_CONTENT);
lv_obj_set_x( ui_ImageDel, -60 ); // 左侧位置
lv_obj_set_y( ui_ImageDel, -120 ); // 容器顶部区域(屏幕底部可见区域)
lv_obj_set_align( ui_ImageDel, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImageDel, LV_OBJ_FLAG_CLICKABLE ); // 可点击
lv_obj_clear_flag( ui_ImageDel, LV_OBJ_FLAG_SCROLLABLE );
ui_ImageReturn = lv_img_create(ui_ContainerDle);
lv_img_set_src(ui_ImageReturn, &ui_img_s14_png);
lv_obj_set_width( ui_ImageReturn, LV_SIZE_CONTENT);
lv_obj_set_height( ui_ImageReturn, LV_SIZE_CONTENT);
lv_obj_set_x( ui_ImageReturn, 60 ); // 右侧位置
lv_obj_set_y( ui_ImageReturn, -120 ); // 容器顶部区域(屏幕底部可见区域)
lv_obj_set_align( ui_ImageReturn, LV_ALIGN_CENTER );
lv_obj_add_flag( ui_ImageReturn, LV_OBJ_FLAG_CLICKABLE ); // 可点击
lv_obj_clear_flag( ui_ImageReturn, LV_OBJ_FLAG_SCROLLABLE );
// 添加点击事件回调
lv_obj_add_event_cb(ui_ImageDel, ui_event_ImageDel, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui_ImageReturn, ui_event_ImageReturn, LV_EVENT_ALL, NULL);
lv_obj_add_event_cb(ui_ScreenImg, ui_event_ScreenImg, LV_EVENT_ALL, NULL);
// 注意:不在此处加载图片,延迟到 LV_EVENT_SCREEN_LOADED 事件中加载
// 这样避免ui_init()时触发渲染导致开机闪烁
}
void ui_ScreenImg_screen_destroy(void)
{
if (ui_ScreenImg) lv_obj_del(ui_ScreenImg);
// NULL screen variables
ui_ScreenImg= NULL;
ui_ImgBle= NULL;
ui_ContainerDle= NULL;
ui_ImageDel= NULL;
ui_ImageReturn= NULL;
// 重置首次加载标志,下次进入时重新初始化
first_load = true;
free_spiffs_image_list();
}