当前位置: 首页 > article >正文

android11关机安卓充电的UI定制化

引言

首先上一张安卓充电的图片:
安卓关机状态下有两种充电模式:uboot-charge和android-charge,可通过dts配置使用哪一种充电模式。
dts配置中uboot-charge和android-charge是互斥的,如下配置的是开启android-charge:

kernel/arch/arm64/boot/dts/rockchip/rk3566_xxproject.dts
在这里插入图片描述

本片主要讲解安卓充电的定制化。

安卓充电流程讲解

InitAnimation解析animation.txt配置文件

实现安卓充电的是一个名为charger的文件,源码位置:system/core/healthd
程序启动时首先会通过InitAnimation函数根据animation.txt解析得到图片和字体文件的路径。animation.txt内容如下:

#动画循环次数 首帧显示次数 动画压缩文件名(charge_scale是多张图片合成的一张图片)
animation: 3 1 charge_scale
#fail文件名
fail: fail_scale
#c c r g b a 字体文件名
clock_display: c c 255 255 255 255 font 
percent_display: c c 255 255 255 255 font 
#电量20以下显示的图片
frame: 500 0 19
#电量40以下显示的图片
frame: 600 0 39
frame: 700 0 59
frame: 750 0 79
frame: 750 0 89
frame: 750 0 100

Charger实现在system/core/healthd/healthd_mode_charger.cpp

static constexpr const char* product_animation_desc_path = "/product/etc/res/values/charger/animation.txt";
static constexpr const char* product_animation_root = "/product/etc/res/images/";void Charger::InitAnimation() {bool parse_success;std::string content;if (base::ReadFileToString(product_animation_desc_path, &content)) {parse_success = parse_animation_desc(content, &batt_anim_);batt_anim_.set_resource_root(product_animation_root);} else if (base::ReadFileToString(animation_desc_path, &content)) {parse_success = parse_animation_desc(content, &batt_anim_);} else {LOGW("Could not open animation description at %s\n", animation_desc_path);parse_success = false;}
//parse_animation_desc实现在system/core/healthd/AnimationParser.cpp
bool parse_animation_desc(const std::string& content, animation* anim) {static constexpr const char* animation_prefix = "animation: ";static constexpr const char* fail_prefix = "fail: ";static constexpr const char* clock_prefix = "clock_display: ";static constexpr const char* percent_prefix = "percent_display: ";std::vector<animation::frame> frames;for (const auto& line : base::Split(content, "\n")) {animation::frame frame;const char* rest;if (can_ignore_line(line.c_str())) {continue;} else if (remove_prefix(line, animation_prefix, &rest)) {int start = 0, end = 0;if (sscanf(rest, "%d %d %n%*s%n", &anim->num_cycles, &anim->first_frame_repeats,&start, &end) != 2 ||end == 0) {LOGE("Bad animation format: %s\n", line.c_str());return false;} else {anim->animation_file.assign(&rest[start], end - start);}} else if (remove_prefix(line, fail_prefix, &rest)) {anim->fail_file.assign(rest);} else if (remove_prefix(line, clock_prefix, &rest)) {if (!parse_text_field(rest, &anim->text_clock)) {LOGE("Bad clock_display format: %s\n", line.c_str());return false;}} else if (remove_prefix(line, percent_prefix, &rest)) {if (!parse_text_field(rest, &anim->text_percent)) {LOGE("Bad percent_display format: %s\n", line.c_str());return false;}} else if (sscanf(line.c_str(), " frame: %d %d %d",&frame.disp_time, &frame.min_level, &frame.max_level) == 3) {frames.push_back(std::move(frame));} else {LOGE("Malformed animation description line: %s\n", line.c_str());return false;}}if (anim->animation_file.empty() || frames.empty()) {LOGE("Bad animation description. Provide the 'animation: ' line and at least one 'frame: ' ""line.\n");return false;}anim->num_frames = frames.size();anim->frames = new animation::frame[frames.size()];std::copy(frames.begin(), frames.end(), anim->frames);return true;
}
//parse_text_field实现在system/core/healthd/AnimationParser.cpp
bool parse_text_field(const char* in, animation::text_field* field) {int* x = &field->pos_x;int* y = &field->pos_y;int* r = &field->color_r;int* g = &field->color_g;int* b = &field->color_b;int* a = &field->color_a;int start = 0, end = 0;if (sscanf(in, "c c %d %d %d %d %n%*s%n", r, g, b, a, &start, &end) == 4) {*x = CENTER_VAL;*y = CENTER_VAL;} else if (sscanf(in, "c %d %d %d %d %d %n%*s%n", y, r, g, b, a, &start, &end) == 5) {*x = CENTER_VAL;} else if (sscanf(in, "%d c %d %d %d %d %n%*s%n", x, r, g, b, a, &start, &end) == 5) {*y = CENTER_VAL;} else if (sscanf(in, "%d %d %d %d %d %d %n%*s%n", x, y, r, g, b, a, &start, &end) != 6) {return false;}if (end == 0) return false;field->font_file.assign(&in[start], end - start);return true;
}

初始化GRSurface

根据上一步解析得到的图片的路径转化成绘图表面

void Charger::Init(struct healthd_config* config) {...InitAnimation();ret = res_create_display_surface(batt_anim_.fail_file.c_str(), &surf_unknown_);if (ret < 0) {LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);ret = res_create_display_surface("charger/battery_fail", &surf_unknown_);if (ret < 0) {LOGE("Cannot load built in battery_fail image\n");surf_unknown_ = NULL;}}GRSurface** scale_frames;int scale_count;int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text// chunk). We are using hard-coded frame.disp_time instead.ret = res_create_multi_display_surface(batt_anim_.animation_file.c_str(), &scale_count,&scale_fps, &scale_frames);if (ret < 0) {LOGE("Cannot load battery_scale image\n");batt_anim_.num_frames = 0;batt_anim_.num_cycles = 1;} else if (scale_count != batt_anim_.num_frames) {LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,batt_anim_.num_frames);batt_anim_.num_frames = 0;batt_anim_.num_cycles = 1;} else {for (i = 0; i < batt_anim_.num_frames; i++) {batt_anim_.frames[i].surface = scale_frames[i];}}

绘制电量图片、电量百分比和时间文字,用的minui框架

void Charger::UpdateScreenState(int64_t now) {...if (healthd_draw_ == nullptr) {...//初始化healthd_draw_healthd_draw_.reset(new HealthdDraw(&batt_anim_));if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {healthd_draw_->blank_screen(true);screen_blanked_ = true;}}//执行具体的绘制healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);...
}HealthdDraw::HealthdDraw(animation* anim): kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {int ret = gr_init();if (ret < 0) {LOGE("gr_init failed\n");graphics_available = false;return;}graphics_available = true;sys_font = gr_sys_font();if (sys_font == nullptr) {LOGW("No system font, screen fallback text not available\n");} else {gr_font_size(sys_font, &char_width_, &char_height_);}screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);screen_height_ = gr_fb_height();int res;if (!anim->text_clock.font_file.empty() &&(res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {LOGE("Could not load time font (%d)\n", res);}if (!anim->text_percent.font_file.empty() &&(res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {LOGE("Could not load percent font (%d)\n", res);}
}void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {if (!graphics_available) return;clear_screen();/* try to display *something* */if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||batt_anim->num_frames == 0)draw_unknown(surf_unknown);elsedraw_battery(batt_anim);gr_flip();
}void HealthdDraw::draw_battery(const animation* anim) {if (!graphics_available) return;const animation::frame& frame = anim->frames[anim->cur_frame];if (anim->num_frames != 0) {//绘制电量图片draw_surface_centered(frame.surface);LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame, frame.min_level,frame.disp_time);}//绘制时间和电量百分比文字draw_clock(anim);draw_percent(anim);
}void HealthdDraw::draw_percent(const animation* anim) {if (!graphics_available) return;int cur_level = anim->cur_level;if (anim->cur_status == BATTERY_STATUS_FULL) {cur_level = 100;}if (cur_level < 0) return;const animation::text_field& field = anim->text_percent;if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {return;}std::string str = base::StringPrintf("%d%%", cur_level);int x, y;determine_xy(field, str.size(), &x, &y);LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);gr_color(field.color_r, field.color_g, field.color_b, field.color_a);draw_text(field.font, x, y, str.c_str());
}

HealthdDraw实现在system/core/healthd/healthd_draw.cpp

电量刷新和事件响应

充电状体下会监听power按键、充电器插拔事件和电量更新事件。
监听power按键实现在HandleInputState函数,按下power键会触发重新显示充电动画。

void Charger::HandleInputState(int64_t now) {//监听power按键ProcessKey(KEY_POWER, now);if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
}void Charger::ProcessKey(int code, int64_t now) {key_state* key = &keys_[code];if (code == KEY_POWER) {if (key->down) {int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;if (now >= reboot_timeout) {/* We do not currently support booting from charger mode onall devices. Check the property and continue booting or rebootaccordingly. */if (property_get_bool("ro.enable_boot_charger_mode", false)) {LOGW("[%" PRId64 "] booting from charger mode\n", now);property_set("sys.boot_from_charger_mode", "1");} else {if (batt_anim_.cur_level >= boot_min_cap_) {LOGW("[%" PRId64 "] rebooting\n", now);reboot(RB_AUTOBOOT);} else {LOGV("[%" PRId64"] ignore power-button press, battery level ""less than minimum\n",now);}}} else {/* if the key is pressed but timeout hasn't expired,* make sure we wake up at the right-ish time to check*/SetNextKeyCheck(key, POWER_ON_KEY_TIME);/* Turn on the display and kick animation on power-key press* rather than on key release*/kick_animation(&batt_anim_);request_suspend(false);}} else {/* if the power key got released, force screen state cycle */if (key->pending) {kick_animation(&batt_anim_);request_suspend(false);}}}key->pending = false;
}

如果想要监听更多的按键事件,只需要在HandleInputState函数中新增ProcessKey(KEY_xxx, now),然后在ProcessKey实现对应键值的逻辑即可。

充电器插拔回调到HandlePowerSupplyState函数

void Charger::HandlePowerSupplyState(int64_t now) {int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;if (!have_battery_state_) return;if (!charger_online()) {//断开充电器...} else {//插入充电器...}
}

电量刷新会回调到OnHealthInfoChanged函数

void Charger::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {set_charger_online(health_info);if (!have_battery_state_) {have_battery_state_ = true;next_screen_transition_ = curr_time_ms() - 1;request_suspend(false);reset_animation(&batt_anim_);kick_animation(&batt_anim_);}health_info_ = health_info.legacy.legacy;AdjustWakealarmPeriods(charger_online());
}

在rk3566 android11中动画执行完后,如果电量刷新了不会触发界面的刷新。如要实现电量实时更新到界面,在此方法中新增逻辑即可,下面贴下我实现电量实时刷新的patch

diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
--- a/healthd/healthd_mode_charger.cpp	(revision 6ae575fc403d2504435366ac34ff233e537e78bd)
+++ b/healthd/healthd_mode_charger.cpp	(revision 1122ab003e599072fa194f23b593fbd4ad84205e)
@@ -617,6 +617,15 @@reset_animation(&batt_anim_);kick_animation(&batt_anim_);}
+    //huanghp add: refresh screen when batteryLevel changed
+    if (health_info_.batteryLevel != health_info.legacy.legacy.batteryLevel){
+        LOGV("batteryLevel changed : %d\n",health_info.legacy.legacy.batteryLevel);
+        request_suspend(false);
+        reset_animation(&batt_anim_);
+        kick_animation(&batt_anim_);
+    }
+    //huanghp end;health_info_ = health_info.legacy.legacy;AdjustWakealarmPeriods(charger_online());

源码更新了后可以单编charger,ado root && adb remount后替换charger文件重启机器就能看到效果,不需要刷机。对于下面的充电图标和字体也是找到对应目录直接替换后重启就可以看效果。

充电图标替换

修改默认关机充电图标实际上要替换battery_scale.png,charge_scale.png实际是由多张图片合成的一张图片。
在这里插入图片描述
对应c源码配置

void Charger::InitDefaultAnimationFrames() {owned_frames_ = {{.disp_time = 750,.min_level = 0,.max_level = 19,.surface = NULL,},{.disp_time = 750,.min_level = 0,.max_level = 39,.surface = NULL,},{.disp_time = 750,.min_level = 0,.max_level = 59,.surface = NULL,},{.disp_time = 750,.min_level = 0,.max_level = 79,.surface = NULL,},{.disp_time = 750,.min_level = 80,.max_level = 95,.surface = NULL,},{.disp_time = 750,.min_level = 0,.max_level = 100,.surface = NULL,},};
}

合成和拆分charge_scale用到的脚本:bootable/recovery/interlace-frames.py

#合成命令
python interlace-frames.py -o battery_scale.png oem/battery00.png oem/battery01.png oem/battery02.png oem/battery03.png oem/battery04.png oem/battery05.png
#拆分命令
python interlace-frames.py -d battery_scale.png -o battery.png

font.png字体文件替换

在这里插入图片描述
在这里插入图片描述
bootable/recovery/fonts目录下默认有些不同大小的字体文件,官方的说法是字体都是用font
Inconsolata自动生成的。

The images in this directory were generated using the font
Inconsolata, which is released under the OFL license and was obtained
from:
https://code.google.com/p/googlefontdirectory/source/browse/ofl/inconsolata/

打开链接发现内容不在了,没有找到制作字体的工具。
因此如果要使用更大字号的字体,就需要自己想办法制作字体,这里我从stackoverflow找到个
可以自动生成的python脚本,试了生成的字体可以使用。

'auto generate font png'
from PIL import Image, ImageDraw, ImageFont
import os
def draw_png(name, font_size = 40):font_reg  = ImageFont.truetype(name + '-Regular' + '.ttf', font_size)font_bold = ImageFont.truetype(name + '-Bold' + '.ttf', font_size)text=r''' !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~'''text_width, text_height = font_bold.getsize(text)max_w = 0max_h = 0for c in text:w, h = font_bold.getsize(c)if w > max_w:max_w = wif h > max_h:max_h = hprint max_w, max_himage = Image.new(mode='L', size=(max_w*96, max_h*2))draw_table = ImageDraw.Draw(im=image)i = 0for c in text:text_width, text_height = font_bold.getsize(c)print c , text_width, text_heightdraw_table.text(xy=(max_w*i, 0), text=c, fill='#ffffff', font=font_reg, anchor="mm", align="center")draw_table.text(xy=(max_w*i, max_h), text=c, fill='#ffffff', font=font_bold, anchor="mm",align="center")i = i + 1image.show()image.save( name + '.png', 'PNG')image.close()if __name__ == "__main__":print('running:')try:draw_png('Roboto',100)except Exception as e:print( ' ERR: ', e)

字体文件直接在aosp源码目录查找find ./ -name *.ttf |grep Roboto

参考:

  • https://blog.csdn.net/lmpt90/article/details/103390395
  • https://stackoverflow.com/questions/65180151/how-to-generate-a-font-image-used-in-android-power-off-charging-animation

相关文章:

android11关机安卓充电的UI定制化

引言 首先上一张安卓充电的图片&#xff1a; 安卓关机状态下有两种充电模式&#xff1a;uboot-charge和android-charge&#xff0c;可通过dts配置使用哪一种充电模式。 dts配置中uboot-charge和android-charge是互斥的&#xff0c;如下配置的是开启android-charge&#xff1a;…...

Web前端之JavaScript的DOM操作冷门API

MENU 前言1、Element.checkVisibility()2、TreeWalker3、Node.compareDocumentPosition()4、scrollIntoViewIfNeeded()5、insertAdjacentElement()6、Range.surroundContents()7、Node.isEqualNode()8、document.createExpression()小结 前言 作为前端开发者&#xff0c;我们每…...

惠普(HP)和联想(Lenovo)作为全球两大电脑品牌,并不是简单的“拼接电脑”

惠普&#xff08;HP&#xff09;和联想&#xff08;Lenovo&#xff09;作为全球两大电脑品牌&#xff0c;并不是简单的“拼接电脑”&#xff0c;它们都有自己的核心技术、专利设计和生态体系。以下是它们“自己的”核心部分&#xff1a; 1. 关键自研技术 品牌自研技术/专利说明…...

集成开发环境革新:IntelliJ IDEA与Cursor AI的智能演进

集成开发环境革新&#xff1a;IntelliJ IDEA 与 Cursor AI 的智能演进 集成开发环境&#xff08;IDE&#xff09; 是软件开发者必不可少的工具。一个优秀的 IDE 不仅能够帮助编写和调试代码&#xff0c;还能集成版本控制和代码优化等多种功能。如今&#xff0c;随着人工智能&a…...

EXCEL报错:无法共享此工作薄,因表包含excel表或xml映射的解决方法

在分享工作薄是&#xff0c;如果出现了“无法共享此工作薄&#xff0c;因表包含excel表或xml映射”的报错&#xff0c;那么有两个原因&#xff1a; 1.包含Excel表格&#xff0c;这个也是相对比较常见的原因。 首先选中表格。如果你不知道表的位置在哪&#xff0c;那么在Excel左…...

《深度剖析SQL之WHERE子句:数据过滤的艺术》

在当今数据驱动的时代&#xff0c;数据处理和分析能力已成为职场中至关重要的技能。SQL作为一种强大的结构化查询语言&#xff0c;在数据管理和分析领域占据着核心地位。而WHERE子句&#xff0c;作为SQL中用于数据过滤的关键组件&#xff0c;就像是一把精准的手术刀&#xff0c…...

【悲观锁和乐观锁有什么区别】以及在Spring Boot、MybatisPlus、PostgreSql中使用

悲观锁和乐观锁是两种常见的并发控制方式&#xff0c;它们在处理并发数据访问时的策略和实现方式有很大的不同。下面是这两者的主要区别&#xff1a; 1. 锁的策略 悲观锁&#xff08;Pessimistic Locking&#xff09;&#xff1a; 假设并发冲突频繁发生&#xff0c;因此在操作…...

《Linux运维实战:Ubuntu 22.04配置pam实现密码复杂度策略》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;Linux运维实战总结 一、背景信息 由于安全方面的考虑&#xff0c;先要求Ubuntu 22.04系统需配置密码复杂度策略&#xff0c;先要求如下&#xff1…...

域名解析:从基础概念到安全风险全面指南

目录 什么是域名&#xff1f; 域名在哪里注册&#xff1f; 域名层级解析&#xff1a;二级与多级域名 域名发现对安全测试的意义 二到多级域名面临的网络安全风险 如何加强域名安全管理 总结 什么是域名 域名(Domain Name)是互联网上用于标识和定位计算机、网络服务的字…...

从代码学习深度学习 - 使用块的网络(VGG)PyTorch版

文章目录 前言一、VGG网络简介1.1 VGG的核心特点1.2 VGG的典型结构1.3 优点与局限性1.4 本文的实现目标二、搭建VGG网络2.1 数据准备2.2 定义VGG块2.3 构建VGG网络2.4 辅助工具2.4.1 计时器和累加器2.4.2 准确率计算2.4.3 可视化工具2.5 训练模型2.6 运行实验总结前言 深度学习…...

Java课程设计(双人对战游戏)持续更新......

少废话&#xff0c;当然借助了ai&#xff0c;就这么个实力&#xff0c;后续会逐渐完善...... 考虑添加以下功能&#xff1a; 选将&#xff0c;选图&#xff0c;技能&#xff0c;天赋&#xff0c;道具&#xff0c;防反&#xff0c;反重力&#xff0c;物理反弹&#xff0c;击落…...

Windows 安装多用户和其它一些问题 VMware Onedrive打不开

以下以win10家庭版为例&#xff0c;win11、专业版类似。 Onedrive相关问题参看我的其他文章&#xff1a; Windows如何同时登录两个OneDrive个人版账号_onedrive登录两个账号-CSDN博客 win10 win11 设置文件权限以解决Onedrive不能同步问题_onedrive没有同步权限-CSDN博客 O…...

深入解析:MySQL 中 NULL 值是否占用 1 bit 存储空间?

在 MySQL 的存储机制中,关于 NULL 值是否占用 1 bit 的存储空间,存在一个常见的理解误区。许多人认为“每个 NULL 值占用 1 bit”,但这并不完全准确。本文将通过 InnoDB 引擎的存储原理,详细解释 NULL 值的实际存储开销,并澄清这一误解。 一、核心结论 允许为 NULL 的列会…...

java基础自用笔记:异常、泛型、集合框架(List、Set、Map)、Stream流

异常 异常体系 编译时异常代表程序觉得你可能会出错。 运行时异常代表已经出错 异常基本处理 异常的作用 可以在可能出现的异常的地方用返回异常来代替return&#xff0c;这样提醒程序出现异常简洁清晰 自定义异常 最好用运行时异常&#xff0c;不会像编译时异常那样烦人&a…...

深度学习中常见的专业术语汇总

本硕博都是搞机械的匠人&#xff0c;当然也想做一下交叉学科的东西&#xff0c;蹭一下人工智能的热点。虽然世界是个草台班子&#xff0c;但是来都来了&#xff0c;咱也要把这场戏演好。 记得之前网上爆料有位大学生发了很多水文&#xff0c;对&#xff0c;是交叉学科的&#x…...

Python Cookbook-4.14 反转字典

任务 给定一个字典&#xff0c;此字典将不同的键映射到不同的值。而你想创建一个反转的字典&#xff0c;将各个值反映射到键。 解决方案 可以创建一个函数&#xff0c;此函数传递一个列表推导作为dict的参数以创建需要的字典。 def invert_dict(d):return dict([(v,k) for …...

第六届 蓝桥杯 嵌入式 省赛

参考 第六届蓝桥杯嵌入式省赛程序设计题解析&#xff08;基于HAL库&#xff09;_蓝桥杯嵌入式第六届真题-CSDN博客 一、分析功能 RTC 定时 1&#xff09;时间初始化 2&#xff09;定时上报电压时间 ADC测量 采集电位器的输出电压信号。 串行功能 1&#xff09;传送要设置…...

爱普生FC-135晶振5G手机的极端温度性能守护者

在5G时代&#xff0c;智能手机不仅需要高速率与低延迟&#xff0c;更需在严寒、酷暑、振动等复杂环境中保持稳定运行。作为 5G 手机的核心时钟源&#xff0c;爱普生32.768kHz晶振FC-135凭借其宽温适应性、高精度稳定性与微型化设计&#xff0c;成为5G手机核心时钟源的理想选择&…...

C# StreamReader/StreamWriter 使用详解

总目录 前言 在 C# 开发中&#xff0c;StreamReader 和 StreamWriter 是处理文本文件的核心类&#xff0c;属于 System.IO 命名空间。它们基于流&#xff08;Stream&#xff09;操作文本数据&#xff0c;支持读写、编码设置、异步操作等&#xff0c;适用于日志记录、配置文件处…...

如何备份你的 Postman 所有 Collection?

团队合作需要、备份&#xff0c;还是迁移到其他平台&#xff0c;我们都需要在 Postman 中将这些珍贵的集合数据导出。 如何从 Postman 中导出所有集合(Collection)教程...

SQL IF(xxx, 1, 0) 窗口函数

IF(xxx, 1, 0)是SQL中的条件表达式函数&#xff0c;它的工作原理如下&#xff1a; 功能&#xff1a;如果条件xxx为真(TRUE)&#xff0c;则返回1&#xff1b;如果条件xxx为假(FALSE)&#xff0c;则返回0 参数&#xff1a; 第一个参数(xxx)&#xff1a;要评估的条件表达式 第二…...

【Qt】三种操作sqlite3的方式及其三种多表连接

一、sqlite3与MySQL数据库区别&#xff1a; 1. 数据库类型 SQLite3&#xff1a;是嵌入式数据库&#xff0c;它将整个数据库存储在单个文件中&#xff0c;不需要独立的服务器进程。这意味着它可以很方便地集成到各种应用程序中&#xff0c;如移动应用、桌面应用等。MySQL&…...

MinGW下编译ffmpeg源码时生成compile_commands.json

在前面的博文MinGW下编译nginx源码中&#xff0c;有介绍到使用compiledb工具在MinGW环境中生成compile_commands.json&#xff0c;以为compiledb是捕获的make时的输出&#xff0c;而nginx生成时控制台是有输出编译时的命令行信息的&#xff0c;笔者之前编译过ffmpeg的源码&…...

【数据结构】树与森林

目录 树的存储方法 双亲表示法 孩子表示法 孩子兄弟表示法 树、森林与二叉树的转换 树转换成二叉树 森林转换成二叉树 二叉树转换成森林 树与森林的遍历 树的遍历 森林的遍历 树的存储方法 双亲表示法 这种存储结构采用一组连续空间来存储每个结点&#xff0c;同时…...

跟着StatQuest学知识08-RNN与LSTM

一、RNN &#xff08;一&#xff09;简介 整个过程权重和偏置共享。 &#xff08;二&#xff09;梯度爆炸问题 在这个例子中w2大于1&#xff0c;会出现梯度爆炸问题。 当我们循环的次数越来越多的时候&#xff0c;这个巨大的数字会进入某些梯度&#xff0c;步长就会大幅增加&…...

【SpringCloud】Eureka的使用

3. Eureka 3.1 Eureka 介绍 Eureka主要分为两个部分&#xff1a; EurekaServer: 作为注册中心Server端&#xff0c;向微服务应用程序提供服务注册&#xff0c;发现&#xff0c;健康检查等能力。 EurekaClient: 服务提供者&#xff0c;服务启动时&#xff0c;会向 EurekaS…...

nuxt3 seo优化

在 Nuxt3 中&#xff0c;通过 nuxtjs/seo、nuxtjs/sitemap 和 nuxtjs/robots 模块可以生成包含动态链接的站点地图&#xff08;sitemap.xml&#xff09;&#xff0c;但具体是“实时生成”还是“部署时生成”&#xff0c;取决于你的配置方式和数据更新频率。以下是具体分析&…...

初识MySQL · 数据类型

目录 前言&#xff1a; 数值类型 文本、二进制数据类型 时间类型 String类型 前言&#xff1a; 对于MySQL来说&#xff0c;是一门编程语言&#xff0c;可能定义不是那么的严格&#xff0c;但是对于MySQL来说也是拥有自己的数据类型的&#xff0c;比如tinyint&#xff0c;…...

【Go】数组

数组Array 重点&#xff1a; 数组是值类型 注意点: 1. 数组&#xff1a;是同一种数据类型的固定长度的序列。2. 数组定义&#xff1a;var a [len]int&#xff0c;比如&#xff1a;var a [5]int&#xff0c;数组长度必须是常量&#xff0c;且是类型的组成部分。一旦定义&…...

QT图片轮播器(QT实操学习2)

1.项目架构 1.UI界面 2.widget.h​ #ifndef WIDGET_H #define WIDGET_H#include <QWidget>#define TIMEOUT 1 * 1000 QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent n…...