首页/新闻资讯/正文详情

Delphi 12 FMX ListView 复用机制与性能优化避坑指南

发布时间:2026/11/3 17:25:09 来源:云帆数科 栏目:资讯中心
Delphi 12 FMX ListView 复用机制与性能优化避坑指南
简介Delphi 12 FMXUI 跨平台界面组件库的预览版源代码包面向已具备 Delphi 与 FireMonkey 基础、希望深入掌握 ListView 的开发者可用于搭建 Windows、macOS、iOS、Android 及 Linux 多端界面并解决列表展示、数据绑定、交互定制与视觉适配等实际问题。压缩包共 224 个文件约 4.24MB以 100 个 pas 单元、54 个 fmx 窗体及 33 个 png 界面素材为主另含 dproj 与 dpr 工程文件、dpk 包文件、说明文档和少量资源文件目录组织便于直接编译研究适合对照源码学习组件封装、列表项复用及跨平台渲染差异。已有 1134 人学习。读者可从中获得 ListView 的示例实现与组件源码线索围绕数据绑定、ItemObjects 定制视图、OnItemClick 等交互事件、分组折叠、平滑动画、排序过滤及 TListViewBaseAdapter 扩展展开研读理解多平台列表的界面组织与适配思路并据此改进实际项目中的可操作性与视觉体验积累可复用的界面搭建经验。1. 从一次滑动卡顿说起Delphi 12 FMX 的 ListView 到底强在哪把 Delphi 12 的 FMX UI 从头搭一遍最让我改观的控件不是那些带动画的面板而是 ListView。模拟项目X 里有一屏订单列表最初我用 TScrollBox 手堆三十张卡片安卓千元机一滑就掉帧换成 ListView 的 DynamicAppearance 之后同样的视觉、同样的数据滑动直接跟手。后来我把这套写法整理成一份可复用的源代码这里把判断依据、接线细节和出问题的地方一次讲清楚。这篇写给正在纠结 FMX 列表到底该怎么搭、已经上手但被复用机制坑过的人新手能照着抄结构熟手能看到我为什么这么取舍。2. 三种数据组织方式怎么挑内置外观、DynamicAppearance 与 LiveBindingFMX 的 ListView 看起来只有一个控件实际上底层有三条完全不同的路径选错了后面全是返工。我一般先问一句这条列表要显示几个字段、要不要自定义布局、数据是从对象列表来还是从数据集来。三个问题的答案基本就把路定死了。2.1 内置外观能撑到哪一步ItemAppearance 在设计期是一组枚举值常用的就这么几个ListItem 是标题加一行说明ListItemRightDetail 把详情挪到右侧ImageListItem 是左图右文ImageListItemBottomDetail 是图片下面压两行字。选完之后你只要Items.Add再往Text、Detail、Bitmap上赋值就能出效果高度自动算连 ItemHeight 都不用管。这套东西的优点是零代码、稳、不吃性能。缺点是布局写死图片多大、文字离图片多远、行间距多少全部由框架决定你能调的只有 ItemHeight 和字体。我见过有人为了在内置外观上把金额挪到右边去写 OnPainting 自己画画完发现高 DPI 下全歪。判断线其实很朴素如果设计稿上只有一个圆角矩形加一行字加一行灰字内置外观够用只要出现「金额右对齐、标签在标题下方、右上角一个状态点」这种组合直接上 DynamicAppearance别硬撑。2.2 DynamicAppearance 加 OnUpdateObjects 的正确落点DynamicAppearance 的完整流程是结构面板里选中 ListView把 ItemAppearance 设成 DynamicAppearance然后在 Item Designer 里拖 TListItemText、TListItemImage、TListItemAccessory 这些对象逐个改 Name。之后所有赋值都在 OnUpdateObjects 里做。这里有个必须刻进肌肉记忆的点OnUpdateObjects 不是「添加时调一次」而是「这条要被显示时」都会调滚动、重绘、改尺寸、切前后台全都会触发。所以里面必须幂等——每个对象的所有属性都写一遍包括这一条不该显示图片的时候把图片清空。procedure TFormMain.ListView1UpdateObjects(Sender: TObject; const AItem: TListViewItem); var LOrder: TOrderItem; LText: TListItemText; LImg: TListItemImage; begin // Tag 里存数据主键项被复用时 Tag 一定跟着换 if not FOrderMap.TryGetValue(AItem.Tag, LOrder) then Exit; // 每次全量赋值不做「有值才写」的优化复用残留就是这么来的 LText : AItem.Objects.FindObjectTTListItemText(txtTitle); if LText nil then LText.Text : LOrder.Title; LText : AItem.Objects.FindObjectTTListItemText(txtAmount); if LText nil then LText.Text : FormatCurr(0.00, LOrder.Amount); LImg : AItem.Objects.FindObjectTTListItemImage(imgThumb); if LImg nil then LImg.Bitmap : FImageCache.Placeholder; // 先占位异步结果回来再替换 end;AItem.Tag是 NativeInt存主键最省事FindObjectTT按 Name 反查对象查不到返回 nil所以每个都要判空——Item Designer 里删了一个对象忘了改代码运行时就靠这个判空活着。2.3 什么时候该换 TGrid 而不是硬凹 ListViewListView 的每一项是独立布局列与列之间不会互相对齐。只要需求里出现「表头、列宽固定、行与行必须对齐」ListView 就开始难受了你靠手写 Width 对齐换个 DPI、用户改一下系统字体大小立刻错位。我一般在三种情况下直接换 TGrid需要严格列对齐的报表、单元格内可编辑的表单、需要横向滚动的宽表。还有一种情况是条目高度差异极大——这条三行、那条十行——ListView 的 ItemHeight 是全局值虽然可以在 OnUpdateObjects 里改AItem.Height单条设高但重算时机很微妙滚动时容易跳反而不如 TScrollBox 直接塞。LiveBinding 那条路单独说一句DynamicAppearance 配合Item.Data[字段名]和数据集绑定能省掉一批赋值代码适合表单型 CRUD。代价是调试链路变长绑错一个字段名界面上什么都不显示也不报错我一般只在字段多且稳定的场景才用。3. 接线全流程从 TObjectList 到 ItemAppearance 的落地写法选定 DynamicAppearance 之后真正的活是把数据层、控件层和异步资源接起来。这段最容易写成能跑但不敢改的代码我的做法是把三件事分开数据谁管、对象谁取、图片谁加载。3.1 数据层先用 TObjectList 管住生命周期数据对象别直接用字符串数组或 TStringList 存字段一多就散架。我一般定义一条记录类再交给 TObjectList 托管OwnsObjects 保持默认的 TrueFree 的时候一并回收。type TOrderItem class private FId: Integer; FTitle: string; FAmount: Currency; FThumbUrl: string; public property Id: Integer read FId write FId; property Title: string read FTitle write FTitle; property Amount: Currency read FAmount write FAmount; property ThumbUrl: string read FThumbUrl write FThumbUrl; end; // 主键索引Tag 反查用比线性 Find 稳 FOrders : TObjectListTOrderItem.Create(True); FOrderMap : TDictionaryInteger, TOrderItem.Create;关键在于ListView 里不存数据对象只存主键。原因很简单项是复用的你没法保证某个 TListViewItem 永远对应某条数据把对象塞进Item.Data之类的地方滚动几屏之后一定错位。用 Tag 存 Id、用字典回查是最不容易翻车的写法。点击事件里也是同一套拿ItemIndex找到项拿项的 Tag 找数据。填充时记得成对使用 BeginUpdate 与 EndUpdate一次加几百条的时候差别非常明显。ListView1.BeginUpdate; try ListView1.Items.Clear; for LOrder in FOrders do begin LItem : ListView1.Items.Add; LItem.Tag : LOrder.Id; // 只挂主键OnUpdateObjects 里再取值 end; finally ListView1.EndUpdate; end;BeginUpdate期间不做重排EndUpdate之后一次性布局这是 ListView 上性价比最高的一个改动没有之一。添加一千条时不包和包着的差距不是百分之几十是几秒和几十毫秒的差距。3.2 Item Designer 里的对象命名与运行时取值Item Designer 里拖出来的每个对象都要改 Name别用默认的ListItemText1这种名字。我习惯用类型前缀txtTitle、txtAmount、txtTag、imgThumb、accStatus。这样代码里搜txt就知道哪些是文本一目了然。取值统一走一个封装别在业务逻辑里到处Objects[2]按下标取——下标一旦因为改布局变动全篇代码一起崩还不报错。// 统一取文本对象找不到就返回 nil调用方判空 function TFormMain.ItemText(const AItem: TListViewItem; const AName: string): TListItemText; begin Result : AItem.Objects.FindObjectTTListItemText(AName); end;还有一个细节如果 Item Designer 里把默认的 Text 对象删掉了再写Item.Text : ...是不会有任何效果的界面上什么都不变也不报错第一次遇到会怀疑人生。Detail 同理。所以要么保留默认对象要么统一走 FindObjectT别两套混着来。提示改布局之后先改代码再编译靠运行时判空兜底只能保证不崩不能保证显示对。3.3 缩略图异步加载与缓存复用列表里最拖后腿的几乎永远是图片。同步从网络或本地大图加载滚动必然顿。我一般的写法是先给占位图异步取回到主线程前再校验一次这条项是不是还对应同一份数据。procedure TFormMain.LoadThumb(const AItem: TListViewItem; const AUrl: string); var LImg: TListItemImage; LToken: Int64; begin LImg : AItem.Objects.FindObjectTTListItemImage(imgThumb); if LImg nil then Exit; LImg.Bitmap : FImageCache.Placeholder; LToken : AItem.Tag; // 用主键当令牌 TTask.Run( procedure var LBmp: TBitmap; begin LBmp : FImageCache.GetOrLoad(AUrl); // 内部 LRU 磁盘缓存 TThread.Queue(nil, procedure begin // 项可能已被复用给了另一条数据令牌不一致就丢弃结果 if (AItem.Tag LToken) and (LImg nil) then LImg.Bitmap : LBmp; end); end); end;缓存对象我一般设一个上限比如 120 张按最近使用淘汰。位图统一在加载时缩放到目标尺寸再入缓存别指望控件去缩——TListItemImage每帧缩一张 4K 图的代价滚动时不掉帧才怪。缓存里的 TBitmap 由缓存统一持有和释放列表项只是引用这样 Clear 重新填充时不会出现谁释放谁的争议。4. 一万条数据实测几个开关的收益排序与卡顿定位性能这件事不看文档看数据。我在模拟项目X 上灌了一万条订单在千元安卓机上做了一轮粗略对照结论比较反直觉真正决定流畅度的不是 ListView 本身的属性而是你往里塞了多少对象、以及 OnUpdateObjects 里干了什么。4.1 影响最大的四个开关实测对照调整点默认做法建议做法主观收益代价批量填充逐条 Add每次都重排BeginUpdate / EndUpdate 包住极大无Item Designer 对象数一条项拖 12 个对象压到 56 个能合就合大布局要重排图片原图直接赋值靠控件缩放预缩放到目标尺寸再入缓存大多一份缓存管理OnUpdateObjects 里的查找每个字段 FindObjectT一次取出多个字段复用中代码略啰嗦ItemHeight留空让框架算固定一个值中不能自适应阴影/动效项上加 Effect去掉改用静态底图大视觉要妥协必须说清楚这些数字只是我在一台测试机上的相对感受不是基准测试换设备换布局结论可能变。但排序大体稳定——批量填充和对象数量是最值得先动的两刀先砍这两个八成卡顿就没了剩下的再谈细节。还有个容易被忽略的点ListView 是虚拟化的它只创建可视区域附近的项并循环复用。所以「数据一万条」本身不直接等于慢慢的是每一条被显示时你做了多少事。反过来说如果 OnUpdateObjects 里做了 IO、做了字符串拼接、做了数据库查询数据量再小也会卡。4.2 卡顿定位在 OnUpdateObjects 里埋计时点卡顿最难的是定位。我的土办法很有效在 OnUpdateObjects 里挂一个秒表单次超过阈值就打日志跑一遍滚动看慢在哪几条、慢多少。procedure TFormMain.ListView1UpdateObjects(Sender: TObject; const AItem: TListViewItem); var LSw: TStopwatch; begin LSw : TStopwatch.StartNew; try BindItem(AItem); // 真正干活的地方单独抽出去 finally LSw.Stop; FTotalMs : FTotalMs LSw.Elapsed.TotalMilliseconds; Inc(FUpdateCount); // 8ms 大约是一帧的预算单条超了基本就能感觉到 if LSw.ElapsedMilliseconds 8 then OutputDebugString(PChar(Format(slow item id%d cost%dms, [AItem.Tag, LSw.ElapsedMilliseconds]))); end; end;BindItem单独抽出来是为了让计时点干净。跑完看日志如果慢的条目集中在带图的那几条就是图片的锅如果全都慢就是对象太多或者字段赋值里有隐藏开销比如每次都 Format 一遍日期、每次都查字典。这套埋点在 Windows 上跑一遍安卓上跑一遍两边的分布经常不一样别只测一端。注意OutputDebugString 在大量调用时自己也会拖慢定位完记得把阈值调高或者直接去掉。4.3 内存回收项复用与图片缓存上限ListView 的项是复用的但复用的项上挂的位图引用不会自动换成新的。如果图片缓存不设上限滚一遍长列表就等于把所有缩略图堆在内存里安卓上很容易在中低端机被系统请出去。我的做法是缓存设硬上限淘汰时只丢没有被当前可见项引用的Items.Clear之后主动调一次缓存的 Trim。另外一点不要在 OnUpdateObjects 里 new 任何对象包括临时 TBitmap、临时 TStringList。这个方法一秒能被调几百次里面 new 一次就是几百个对象GC 和分配器都扛不住。5. 避坑FMX ListView 最容易出问题的五处下面这五条都是我自己或同事真实遇到过的每条按现象、原因、解决写清楚遇到同类问题可以直接对照。5.1 复用项显示上一条的数据现象滚动几屏之后某些条目的副标题或图片是上一屏某条的内容往回滚又好了。原因OnUpdateObjects 里写了「有值才赋值」的分支比如图片为空就跳过不赋值。项是复用的上一轮挂上去的位图还在于是显示成别人的。解决所有对象的所有属性每轮全量赋值空值也要显式赋成空或占位图。判断标准很简单把这个方法单独拿出来看能不能在任意初始状态下调用都得到正确结果。5.2 运行时换 ItemAppearance自定义对象凭空消失现象代码里动态改ListView1.ItemAppearance做两套视觉切换切完自定义的 txtTitle 全没了取值全是 nil。原因ItemAppearance 变更会重建项的设计结构设计期挂在 DynamicAppearance 上的对象不会跟着搬过去切回来也不一定还原。解决别在运行时切外观。要做多套视觉就在 Item Designer 里放两套对象按状态控制 Visible 和位置确实需要两套差异极大的布局就用两个 ListView 交替显示代价是内存收益是可控。5.3 OnUpdateObjects 里增删项导致卡死现象在 OnUpdateObjects 里根据数据补一条「加载更多」的项运行起来直接卡死或者抛访问异常。原因这个方法是在遍历和布局过程中被回调的你在里面改 Items 集合等于在迭代中修改容器。解决这个方法里只做赋值不碰集合。加载更多放到 OnScrollViewChange 里判断滚动位置接近底部再追加并且用布尔量挡住重入避免用户连续快滑触发多次追加。5.4 点击取 ItemIndex 取到了错的行现象过滤之后再点击打开的是过滤前的那条数据。原因代码里用 ItemIndex 去原始数据列表按下标取。过滤改变了 ListView 的顺序和数量但原始列表没变下标自然对不上。解决一律用Item.Tag存主键回查不按下标取数据。OnItemClick 和 OnItemClickEx 两条路都统一成同一套反查别一个用一个不用。5.5 安卓首次滚动白块与图片闪烁现象列表刚出来时先是一片空白快速滑动时图片一会儿有一会儿没有。原因图片是在项显示之后才异步取的第一轮必然是占位图同时缓存命中率低同一个 URL 被并发请求了多次回填顺序错乱。解决占位图给一个和最终图同尺寸的纯色位图视觉上不会跳缓存里对同一个 key 做请求合并已经有请求在路上就不要重复发回填时用 5.1 里的令牌校验。6. 收口技巧把列表初始化收成一个可复用单元最后落到一个我每次新建列表都会先写的方法。它把那些「忘了就出问题」的设置一次性钉死后面写业务只管 OnUpdateObjects。procedure TFormMain.SetupOrderList; begin ListView1.ItemAppearance : TListViewItemAppearance.DynamicAppearance; ListView1.ItemHeight : 76; // 固定高度别让框架逐条算 ListView1.ShowScrollBar : True; ListView1.AllowSelection : True; ListView1.OnUpdateObjects : ListView1UpdateObjects; ListView1.OnItemClickEx : ListView1ItemClickEx; // 统一走 Tag 反查 FImageCache.Capacity : 120; // 缓存上限超出按 LRU 淘汰 end;这六行里ItemHeight 固定和 OnUpdateObjects 必须挂上是最关键的两条。前者省掉逐条测量后者保证复用不会串数据。剩下的按项目需要加比如搜索框、下拉刷新都是锦上添花。从那以后我每加一个新列表都强制走一遍这套流程先定 ItemAppearance再改对象名再写全量赋值的 OnUpdateObjects再给 Tag 存主键最后才填数据。宁可多写二十行也不让复用残留有机会冒出来。希望帮到你。本文还有配套的精品资源点击获取

相关推荐

HBase分布式数据库安装与核心操作指南
HBase分布式数据库安装与核心操作指南

1. HBase简介与核心特性HBase是一个开源的、分布式的、面向列的NoSQL数据库,它构建在Hadoop文件系统(HDFS)之上,专为处理海量结构化数据而设计。作为Google BigTable的开源实现,HBase在实时读写访问大规模数据集方面表… · 2026/11/2 12:41:42

合成复用原则:组合优于继承的软件设计实践
合成复用原则:组合优于继承的软件设计实践

1. 合成复用原则的本质解析在软件工程领域,合成复用原则(Composite Reuse Principle)是面向对象设计的七大原则之一,它直指代码复用这一核心命题。我第一次真正理解这个原则的价值,是在维护一个使用深度继承体系的遗留… · 2026/11/2 12:41:42

ACPI PCI配置空间处理机制与异步回调解析
ACPI PCI配置空间处理机制与异步回调解析

1. ACPI PCI配置空间处理机制解析ACPI(Advanced Configuration and Power Interface)规范中的PciConfigSpaceHandlerWorker函数是处理PCI设备配置空间访问的核心例程。在Windows内核实现中,这个函数通过ACPI!RestartCtxtCallback机制实现异步操作完成时的上下文恢复… · 2026/11/2 12:40:41

旋转圆叠加方波动画全解析:从傅里叶级数到Python实现
旋转圆叠加方波动画全解析:从傅里叶级数到Python实现

你肯定刷到过那个画面:一个接一个的圆套着圆,圆心之间互相牵着走,最外圈的小笔尖拖出一条不断延伸的波形,最后硬生生拼出一个棱角分明的方波。这几乎是“傅里叶级数”的官方代言图,我第一次看到时第一反应是“这玩意儿… · 2026/11/3 17:24:34

SMP平台函数功能实现与优化实践
SMP平台函数功能实现与优化实践

1. 函数功能在SMP平台中的实现历程 作为一名长期从事企业信息化系统开发的工程师,我在使用SMP(软件制作平台)进行应用开发时遇到了一个关键瓶颈——平台原生缺乏函数功能。这导致我们在处理复杂业务逻辑时,不得不重复编写大量相似… · 2026/11/3 17:24:34

用Wails v2 + Go + Vue3构建轻量级AI桌面应用:从选型到打包的实战指南
用Wails v2 + Go + Vue3构建轻量级AI桌面应用:从选型到打包的实战指南

做 AI 桌面应用这件事,我一直觉得是"听起来简单,做起来全是细节"。浏览器里开个网页聊大模型当然容易,但一旦涉及本地文件读取、长期保存的会话记录、系统级快捷键、托盘常驻这些需求,网页就不够用了。去年我决定把手里… · 2026/11/3 17:24:34

Redis Lua实现大模型API配额防透支的原子扣减方案
Redis Lua实现大模型API配额防透支的原子扣减方案

1. 项目概述:为什么“API配额防透支”不是锦上添花,而是生死线你刚上线一个面向中小企业的AI助手SaaS平台,后端调用的是OpenRouter或智谱千问这类大模型服务商的API。初期用户增长不错,某天凌晨三点,运维告警疯狂弹窗&… · 2026/11/3 17:24:34

Bookinfo微服务故障排查与性能优化实战
Bookinfo微服务故障排查与性能优化实战

1. 项目背景与核心价值 Bookinfo作为微服务架构的经典示例应用,几乎成为每个云原生工程师的"必修课"。但在实际生产环境中,从简单的服务调用失败到复杂的性能瓶颈,各种问题往往让开发者头疼不已。这次我们以6.8版本Bookinfo为例&a… · 2026/11/3 17:24:34

Whisper语音转写实战:从环境搭建到批量出稿的完整指南
Whisper语音转写实战:从环境搭建到批量出稿的完整指南

1. 手动整理一小时的痛,和 Whisper 给我的转机先说说我为什么会专门写这篇 Whisper 语音转写实战。我手里头有大量音频要处理:每周至少两场客户访谈、一期播客、还有零零散散的会议录音。以前靠的是最原始的办法——听一句暂停一下,把重点敲进… · 2026/11/3 17:23:32

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59

双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28

STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24

DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码