鸿蒙 List 组件解析:从基础列表到高性能界面开发指南

一、引言:列表布局 —— 鸿蒙应用的数据展示中枢

在鸿蒙应用开发体系中,列表布局是处理结构化数据展示的核心场景。从新闻资讯的信息流、电商平台的商品陈列到任务管理的待办事项,几乎所有中大型应用都依赖高效的列表组件实现数据可视化。鸿蒙提供的 List、ListItem、ListItemGroup 三件套组件,通过标准化的接口设计与分层架构,构建了一套完整的列表解决方案。本文将系统解析这三个组件的核心机制、进阶用法与工程实践,帮助开发者掌握高性能列表开发的鸿蒙范式。

二、核心组件架构与协作机制

2.1 组件层级与职责划分

鸿蒙列表体系采用三层架构设计:

  • List:列表容器组件,负责整体布局控制、滚动管理与性能优化
  • ListItem:列表项原子单元,承载具体数据展示与交互逻辑
  • ListItemGroup:列表分组组件,实现数据逻辑分组与吸顶吸底效果

组件层级关系示意图:

List
├─ ListItemGroup(分组容器)
│  ├─ ListItem(列表项1)
│  ├─ ListItem(列表项2)
├─ ListItem(独立列表项)

2.2 核心技术优势

  • 标准化交互模型:内置滑动删除、选中状态、编辑模式等通用交互
  • 高性能渲染引擎:支持懒加载、预渲染与虚拟列表优化
  • 语义化分组能力:通过 ListItemGroup 实现数据分层与视觉分组
  • 多端自适应:自动适配手机、平板、车机等不同设备的屏幕特性

三、List 组件:列表布局的总控制器

3.1 基础接口与布局控制

// xxx.ets
import { ListDataSource } from './ListDataSource';@Entry
@Component
struct ListLanesExample {arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);@State alignListItem: ListItemAlign = ListItemAlign.Start;build() {Column() {List({ space: 20, initialIndex: 0 }) {LazyForEach(this.arr, (item: string) => {ListItem() {Text('' + item).width('100%').height(100).fontSize(16).textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)}.border({ width: 2, color: Color.Green })}, (item: string) => item)}.height(300).width('90%').friction(0.6).border({ width: 3, color: Color.Red }).lanes({ minLength: 40, maxLength: 40 }).alignListItem(this.alignListItem).scrollBar(BarState.Off)Button('点击更改alignListItem:' + this.alignListItem).onClick(() => {if (this.alignListItem == ListItemAlign.Start) {this.alignListItem = ListItemAlign.Center;} else if (this.alignListItem == ListItemAlign.Center) {this.alignListItem = ListItemAlign.End;} else {this.alignListItem = ListItemAlign.Start;}})}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })}
}

3.2 滚动事件与交互控制

// ListDataSource.ets
export class ListDataSource implements IDataSource {private list: number[] = [];private listeners: DataChangeListener[] = [];constructor(list: number[]) {this.list = list;}totalCount(): number {return this.list.length;}getData(index: number): number {return this.list[index];}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {this.listeners.push(listener);}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {this.listeners.splice(pos, 1);}}// 通知控制器数据删除notifyDataDelete(index: number): void {this.listeners.forEach(listener => {listener.onDataDelete(index);});}// 通知控制器添加数据notifyDataAdd(index: number): void {this.listeners.forEach(listener => {listener.onDataAdd(index);});}// 在指定索引位置删除一个元素public deleteItem(index: number): void {this.list.splice(index, 1);this.notifyDataDelete(index);}// 在指定索引位置插入一个元素public insertItem(index: number, data: number): void {this.list.splice(index, 0, data);this.notifyDataAdd(index);}
}
// xxx.ets
import { ListDataSource } from './ListDataSource';@Entry
@Component
struct ListExample {private arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);build() {Column() {List({ space: 20, initialIndex: 0 }) {LazyForEach(this.arr, (item: number) => {ListItem() {Text('' + item).width('100%').height(100).fontSize(16).textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)}}, (item: string) => item)}.listDirection(Axis.Vertical) // 排列方向.scrollBar(BarState.Off).friction(0.6).divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线.edgeEffect(EdgeEffect.Spring) // 边缘效果设置为Spring.onScrollIndex((firstIndex: number, lastIndex: number, centerIndex: number) => {console.info('first' + firstIndex);console.info('last' + lastIndex);console.info('center' + centerIndex);}).onScrollVisibleContentChange((start: VisibleListContentInfo, end: VisibleListContentInfo) => {console.info(' start index: ' + start.index +' start item group area: ' + start.itemGroupArea +' start index in group: ' + start.itemIndexInGroup);console.info(' end index: ' + end.index +' end item group area: ' + end.itemGroupArea +' end index in group: ' + end.itemIndexInGroup);}).onDidScroll((scrollOffset: number, scrollState: ScrollState) => {console.info(`onScroll scrollState = ScrollState` + scrollState + `, scrollOffset = ` + scrollOffset);}).width('90%')}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })}
}

 

3.3 性能优化属性

属性名称类型功能描述
cachedCountnumber预加载相邻项数量,默认值 5,提升滚动流畅度
itemSizenumber固定列表项高度,避免动态计算布局开销
layoutWeightnumber弹性布局权重,配合 ListItem 使用
useVirtualizedboolean启用虚拟列表模式,仅渲染可见区域(API 10+)

四、ListItem 组件:列表项的原子实现单元

4.1 基础结构与样式配置

// xxx.ets
export class ListDataSource implements IDataSource {private list: number[] = [];constructor(list: number[]) {this.list = list;}totalCount(): number {return this.list.length;}getData(index: number): number {return this.list[index];}registerDataChangeListener(listener: DataChangeListener): void {}unregisterDataChangeListener(listener: DataChangeListener): void {}
}@Entry
@Component
struct ListItemExample {private arr: ListDataSource = new ListDataSource([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);build() {Column() {List({ space: 20, initialIndex: 0 }) {LazyForEach(this.arr, (item: number) => {ListItem() {Text('' + item).width('100%').height(100).fontSize(16).textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)}}, (item: string) => item)}.width('90%').scrollBar(BarState.Off)}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })}
}

4.2 交互能力实现

    ListItem().selectable(true)        // 可选中状态.selected($$this.isSelected)   // 双向绑定选中状态.onSelect((selected: boolean) => {// 选中状态变更回调console.log(`选中状态: ${selected}`);}).swipeAction({          // 滑动操作end: {                // 向右滑动显示builder: () => Row()}})

4.3 卡片样式优化(API 10+)

// xxx.ets
@Entry
@Component
struct ListItemExample3 {build() {Column() {List({ space: '4vp', initialIndex: 0 }) {ListItemGroup({ style: ListItemGroupStyle.CARD }) {ForEach([ListItemStyle.CARD, ListItemStyle.CARD, ListItemStyle.NONE], (itemStyle: number, index?: number) => {ListItem({ style: itemStyle }) {Text('' + index).width('100%').textAlign(TextAlign.Center)}})}ForEach([ListItemStyle.CARD, ListItemStyle.CARD, ListItemStyle.NONE], (itemStyle: number, index?: number) => {ListItem({ style: itemStyle }) {Text('' + index).width('100%').textAlign(TextAlign.Center)}})}.width('100%').multiSelectable(true).backgroundColor(0xDCDCDC)}.width('100%').padding({ top: 5 })}
}

五、ListItemGroup 组件:列表的逻辑分组器

5.1 分组结构与吸顶效果

// ListDataSource.ets
export class TimeTableDataSource implements IDataSource {private list: TimeTable[] = [];private listeners: DataChangeListener[] = [];constructor(list: TimeTable[]) {this.list = list;}totalCount(): number {return this.list.length;}getData(index: number): TimeTable {return this.list[index];}registerDataChangeListener(listener: DataChangeListener): void {if (this.listeners.indexOf(listener) < 0) {this.listeners.push(listener);}}unregisterDataChangeListener(listener: DataChangeListener): void {const pos = this.listeners.indexOf(listener);if (pos >= 0) {this.listeners.splice(pos, 1);}}// 通知控制器数据变化notifyDataChange(index: number): void {this.listeners.forEach(listener => {listener.onDataChange(index);});}// 修改第一个元素public change1stItem(temp: TimeTable): void {this.list[0] = temp;this.notifyDataChange(0);}
}export class ProjectsDataSource implements IDataSource {private list: string[] = [];constructor(list: string[]) {this.list = list;}totalCount(): number {return this.list.length;}getData(index: number): string {return this.list[index];}registerDataChangeListener(listener: DataChangeListener): void {}unregisterDataChangeListener(listener: DataChangeListener): void {}
}export interface TimeTable {title: string;projects: string[];
}
// xxx.ets
import { TimeTable, ProjectsDataSource, TimeTableDataSource } from './ListDataSource';
@Entry
@Component
struct ListItemGroupExample {itemGroupArray: TimeTableDataSource = new TimeTableDataSource([]);aboutToAppear(): void {let timeTable: TimeTable[] = [{title: '星期一',projects: ['语文', '数学', '英语']},{title: '星期二',projects: ['物理', '化学', '生物']},{title: '星期三',projects: ['历史', '地理', '政治']},{title: '星期四',projects: ['美术', '音乐', '体育']}];this.itemGroupArray = new TimeTableDataSource(timeTable);}@BuilderitemHead(text: string) {Text(text).fontSize(20).backgroundColor(0xAABBCC).width('100%').padding(10)}@BuilderitemFoot(num: number) {Text('共' + num + '节课').fontSize(16).backgroundColor(0xAABBCC).width('100%').padding(5)}build() {Column() {List({ space: 20 }) {LazyForEach(this.itemGroupArray, (item: TimeTable) => {ListItemGroup({ header: this.itemHead(item.title), footer: this.itemFoot(item.projects.length) }) {LazyForEach(new ProjectsDataSource(item.projects), (project: string) => {ListItem() {Text(project).width('100%').height(100).fontSize(20).textAlign(TextAlign.Center).backgroundColor(0xFFFFFF)}}, (item: string) => item)}.divider({ strokeWidth: 1, color: Color.Blue }) // 每行之间的分界线})}.width('90%').sticky(StickyStyle.Header | StickyStyle.Footer).scrollBar(BarState.Off)}.width('100%').height('100%').backgroundColor(0xDCDCDC).padding({ top: 5 })}
}

5.2 分组布局规则

  • 垂直布局:ListItemGroup 高度由内容自动计算,禁止显式设置 height
  • 水平布局:宽度由内容自动计算,禁止显式设置 width
  • 性能优化:分组内 ListItem 共享滚动上下文,减少内存占用
  • 交互限制:分组头部 / 尾部不支持滑动操作,仅内容区支持

六、实战案例:从基础到复杂的列表开发

6.1 新闻资讯垂直列表

@Entry
@Component
struct NewsFeed {// 使用类替代接口定义数据模型@State newsData: NewsItem[] = generateNews(20) // 生成模拟数据private dataSource: NewsDataSource = new NewsDataSource(this.newsData)build() {List({ space: 12 }) {// 使用正确的LazyForEach语法LazyForEach(this.dataSource, (item: NewsItem) => {ListItem() {Column() {Image(item.image).width('100%').height(180).objectFit(ImageFit.Cover).borderRadius(4)Text(item.title).fontSize(16).fontWeight(FontWeight.Medium).margin({ top: 8, bottom: 4 }).maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })Text(item.summary).fontSize(14).fontColor('#666666').lineHeight(20).maxLines(2).textOverflow({ overflow: TextOverflow.Ellipsis })}.padding(16).backgroundColor('#FFFFFF').borderRadius(8)}}, (item: NewsItem) => item.id) // 唯一键}.width('100%').height('100%').onReachEnd(() => this.loadMoreNews()) // 滚动到底部加载更多.cachedCount(8) // 预加载8项.divider({ strokeWidth: 0.5, color: '#EEEEEE' }) // 添加分割线}// 加载更多数据private loadMoreNews() {const newItems = generateNews(10)this.newsData = this.newsData.concat(newItems)this.dataSource.updateData(this.newsData)}
}// 实现LazyForEach所需的数据源
class NewsDataSource implements IDataSource {private data: NewsItem[] = []private listeners: DataChangeListener[] = []constructor(data: NewsItem[]) {this.data = data}// 更新数据源updateData(newData: NewsItem[]) {this.data = newDatathis.notifyDataReload()}// 通知数据变化private notifyDataReload() {this.listeners.forEach(listener => listener.onDataReloaded())}totalCount(): number {return this.data.length}getData(index: number): NewsItem {return this.data[index]}registerDataChangeListener(listener: DataChangeListener): void {this.listeners.push(listener)}unregisterDataChangeListener(listener: DataChangeListener): void {const index = this.listeners.indexOf(listener)if (index !== -1) {this.listeners.splice(index, 1)}}
}// 新闻数据模型
class NewsItem {id: string = ''title: string = ''summary: string = ''image: Resource = $r('app.media.default_news') // 默认图片资源
}// 模拟数据生成函数
function generateNews(count: number): NewsItem[] {const result: NewsItem[] = []for (let i = 0; i < count; i++) {result.push({id: `news_${Date.now()}_${i}`,title: `新闻标题 ${i + 1}`,summary: `这是新闻摘要内容,展示了ArkTS新闻列表的实现方式...`,image: $r('app.media.news_image') // 实际项目中替换为真实资源})}return result
}

6.2 任务管理分组列表

@Entry
@Component
struct TaskManager {// 任务分组数据模型@State tasks: TaskGroup[] = [{title: '今日待办',items: [{ id: '1', title: '完成工作报告', completed: false },{ id: '2', title: '准备会议材料', completed: false }]},{title: '已完成',items: [{ id: '3', title: '晨跑锻炼', completed: true },{ id: '4', title: '回复邮件', completed: true }]}]// 更新任务状态private updateTaskStatus(groupIndex: number, itemIndex: number, checked: boolean) {this.tasks[groupIndex].items[itemIndex].completed = checked// 创建新数组触发UI更新this.tasks = [...this.tasks]}// 分组头部构建器@BuildergroupHeaderBuilder(title: string) {Text(title).fontSize(18).fontWeight(FontWeight.Bold).padding({ top: 20, bottom: 12, left: 16 }).width('100%').backgroundColor('#f5f5f5')}build() {List({ space: 8 }) {ForEach(this.tasks, (group: TaskGroup, groupIndex: number) => {ListItemGroup({ header: this.groupHeaderBuilder(group.title) }) {ForEach(group.items, (task: TaskItem, itemIndex: number) => {ListItem() {Row() {Checkbox().onChange((checked: boolean) => {this.updateTaskStatus(groupIndex, itemIndex, checked)})Text(task.title).margin({ left: 8 }).fontSize(16)}.padding(16).width('100%')}.borderRadius(8).margin({ bottom: 8 }).backgroundColor('#FFFFFF')}, (task: TaskItem) => task.id)}}, (group: TaskGroup) => group.title)}.width('100%').height('100%').divider({ strokeWidth: 0 }) // 隐藏分割线.listDirection(Axis.Vertical)}
}// 数据模型定义
class TaskGroup {title: string = ''items: TaskItem[] = []
}class TaskItem {id: string = ''title: string = ''completed: boolean = false
}

七、工程实践最佳指南

7.1 性能优化策略

虚拟列表实现
 List() {// 虚拟列表不需要子组件}.width('100%').height('100%').cachedCount(10)  // 预加载项数.onScrollIndex((start, end) => {// 滚动事件处理(可选)console.log(`当前可见项: ${start}-${end}`)})
长列表优化组合
List()
.cachedCount(10) // 预加载10项

7.2 兼容性处理方案

API 分级适配
#if (API >= 9)List().editMode(true).onItemDelete((index) => {// 新API编辑逻辑})
#elseList().onClick((index) => {// 旧API模拟编辑逻辑})
#endif
多端布局适配
List()
.listDirection(DeviceType.isTablet() ? Axis.Horizontal : Axis.Vertical
)
.then((list) => {if (DeviceType.isPhone()) {list.itemSize(80)} else {list.itemSize(100)}
})

7.3 常见问题解决方案

问题场景解决方案
列表滚动卡顿1. 启用虚拟列表模式 .useVirtualized (true)
2. 设置固定项高度 .itemSize (80)
分组头部不吸顶确认 .sticky (StickyStyle.Header) 已设置,且 List 为垂直布局
滑动删除无响应1. 检查 API 版本是否≥9
2. 确保 actionAreaDistance < ListItem 宽度
选中状态不同步使用双向绑定 .selected ($isSelected),避免直接操作状态变量
列表项点击穿透在最外层容器添加 .onClick (() => {}) 消耗点击事件

八、总结:三层架构构建高效列表体系

鸿蒙 List 组件体系通过 List、ListItem、ListItemGroup 的三层架构,为开发者提供了完整的列表解决方案:

  1. List 容器:负责整体布局控制、滚动管理与性能优化,是列表的总控制器
  2. ListItem 单元:承载数据展示与交互逻辑,是列表的原子组件
  3. ListItemGroup 分组:实现数据逻辑分组与吸顶效果,提升复杂列表的信息层级

在实际开发中,建议遵循以下最佳实践:

  • 长列表优先使用 LazyForEach + 虚拟列表模式
  • 复杂数据采用 ListItemGroup 进行语义化分组
  • 交互操作通过组件内置 API 实现,避免自定义事件系统
  • 多端适配结合 DeviceType 与条件编译实现

随着鸿蒙生态向全场景拓展,列表组件将持续进化,未来版本可能加入 AI 驱动的布局优化、3D 滚动效果等创新功能。建议开发者从基础案例入手,结合 DevEco Studio 的实时预览与性能调试工具,逐步掌握列表开发的核心技巧,为用户打造流畅、高效的数据浏览体验

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/86406.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/86406.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

原生微信小程序中限制多选框(Checkbox)可选个数的实现详解

在实际业务场景中&#xff0c;我们经常会遇到表单中的复选框多选限制需求。例如最多只能选择 3 个爱好、标签、兴趣点等&#xff0c;这时就需要在微信小程序中手动控制 Checkbox 的选择行为。 本文将通过一个完整的示例&#xff0c;演示如何实现最多只能选择 N 个的 Checkbox …

OpenCV CUDA模块设备层-----线性插值函数log()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 该函数用于创建线性插值访问器&#xff0c;支持对GPU内存中的图像数据进行双线性插值采样。主要应用于图像缩放、旋转等几何变换中需要亚像素级精…

Redis 单线程的“天花板”与集群的必要性

虽然 Redis 以其单线程模型&#xff08;主要是处理请求的核心逻辑&#xff09;带来了极高的性能和简洁性&#xff0c;但这并不意味着它没有瓶颈。 CPU 瓶颈&#xff1a;当业务逻辑复杂&#xff0c;或者 Redis 执行大量计算密集型操作&#xff08;比如使用 Lua 脚本进行复杂处理…

16.7 Prometheus+Grafana实战:容器化监控与日志聚合一站式解决方案

《Prometheus+Grafana实战:容器化监控与日志聚合一站式解决方案》 关键词:容器化监控、日志聚合、Prometheus、Grafana、ELK Stack、用户反馈收集 容器化监控与日志系统的架构设计 在 LanguageMentor Agent 生产部署中,监控系统需要覆盖以下维度: #mermaid-svg-I7cOqUK0i…

商务创业项目策划计划书PPT模版

创业商业融资计划书PPT模版&#xff0c;商务商业计划融资书PPT模版&#xff0c;创业融资计划书PPT模版&#xff0c;框架完整创业融资计划书PPT模版 商务创业项目策划计划书PPT模版&#xff1a;https://pan.quark.cn/s/d07d22408497

【软考高级系统架构论文】论面向方面的编程技术及其应用

论文真题: 请围绕“论软件测试中缺陷管理及其应用”论题,依次从以下三个方面进行论述: 概要叙述你参与管理和开发的软件项目以及承担的工作。详细论述常见的缺陷种类及级别,论述缺陷管理的基本流程。结合你具体参与管理和开发的实际项目,说明是如何进行缺陷管理的。请具体…

人机协作新范式:GEO与COKE框架的融合应用与品牌大模型种草实践

在人工智能迅猛发展的今天&#xff0c;我们正经历着人机关系的根本性重构。从工具性使用到协作伙伴关系&#xff0c;AI正在以前所未有的方式融入企业运营和品牌建设的各个环节。尤其是在品牌传播领域&#xff0c;“品牌大模型种草”正在成为品牌实现优质曝光和用户信任构建的新…

速通KVM(云计算学习指南)

第一章 云端的变形金刚&#xff1a;KVM的云计算基因 1.1 云计算与KVM的共生关系 想象一下&#xff0c;你有一台魔法服务器&#xff0c;它能像变形金刚一样随时分解成多台独立的小服务器&#xff0c;又能瞬间合体恢复原状——这就是KVM在云计算中扮演的角色。作为Linux内核的原…

C#最佳实践:为何优先使用隐式类型

C#最佳实践:为何优先使用隐式类型 在C#的编程世界里,类型声明是编写代码的重要环节。从早期严格指定变量类型,到引入隐式类型var,编程方式发生了不小的变革。隐式类型并非简单的语法糖,合理使用它能让代码更简洁、更易读,还能适应复杂的编程场景。接下来,我们就深入探讨…

PG靶机复现 Squid

官方定义为easy级别&#xff0c;因为省略了提权阶段&#xff0c;这个靶机主要是利用3128 Squid服务 枚举 通过nmap扫描到3128端口开启。 Squid 是一个缓存和转发的 HTTP 网络代理。它有多种用途&#xff0c;包括通过缓存重复请求来加速 web 服务器&#xff0c;为共享网络资源…

Java底层原理:深入理解JVM类加载机制与反射机制

一、JVM类加载机制 JVM类加载机制是Java运行时环境的重要组成部分&#xff0c;它负责将字节码文件加载到JVM内存中&#xff0c;并将其转换为可执行的类。类加载机制的实现涉及类加载器&#xff08;ClassLoader&#xff09;、类加载过程和类加载器的层次结构。 &#xff08;一…

系统思考:结构影响行为

感谢今天参与沙龙伙伴的评价&#xff0c;虽然只有短短半天的时间&#xff0c;希望今天的交流能为大家带来一些思考的火花。真正的改变&#xff0c;往往不仅来自一次启发&#xff0c;更来自一个支持改变的结构。 就像系统思考中所说的&#xff1a;“结构影响行为。”如果我们希望…

Ubuntu 20.04 系统上运行 SLAM卡顿是什么原因

在 Ubuntu 20.04 系统上运行 SLAM&#xff08;Simultaneous Localization and Mapping&#xff09;数据集时出现卡顿&#xff0c;可能是由硬件、软件配置或数据集处理需求等多方面原因导致。以下是一些可能的原因和解决建议&#xff1a; 1. 硬件性能瓶颈 尽管你使用的是 NVID…

Starwind商用共享存储解决方案——安装篇

介绍 StarWind 是一家提供虚拟化和存储解决方案的软件公司&#xff0c;主要专注于为中小企业&#xff08;SME&#xff09;和远程办公室/分支机构&#xff08;ROBO&#xff09;环境提供高性价比的虚拟化存储解决方案。其核心产品包括 StarWind Virtual SAN 和 StarWind NAS 等&…

Unity | AmplifyShaderEditor插件基础(第十集:噪声的种类+火焰制作-中)

一、&#x1f44b;&#x1f3fb;前言 你现在看见的是一套非常系统的ASE入门学习教程&#xff0c;并不是心血来潮随心创作的。 up原来是初中物理老师&#xff08;有教师资格证&#xff09;后转入程序行业&#xff0c;认真学习过课程设计等相关知识&#xff0c;只要你认真的学每一…

从零开始的二三维CAD|CAE轻量级软件开发:学习以及研发,Gmsh的脚本编辑器设计!

背景: 痛点: 1.编写.geo脚本, 没有智能提示很头大; 2.没有高亮显示很头大! 在数值仿真过程中,大家离不开gmsh这个软件,而在学习的过程中,也离不开要编写.geo脚本, 写这种脚本麻烦的要死,那么多脚本函数要记? 反正写的很头大, 所以,既然为了方便大家,也为了方便自己,不如自…

ModerationModel温和模式

ModerationModel能够对内容进⾏合规检测&#xff0c;屏蔽那些不合规的内容。未来如果你的⼤模型应⽤需要对外提供 服务时&#xff0c;合规就是⼀个必须的标准。 1、代码 import dev.langchain4j.model.moderation.Moderation; import dev.langchain4j.model.moderation.Modera…

OpenDeepWiki:AI代码对话新纪元

OpenDeepWiki 现已支持更智能的多轮对话能力&#xff0c;让您可以与代码库进行深入交流&#xff0c;像与真人对话一样理解代码逻辑和架构。新的对话系统能够保持上下文连贯性&#xff0c;理解复杂查询&#xff0c;并提供更精准的回答。 系统接入能力 现在您可以轻松将 OpenDeep…

Ubuntu安装Docker部署Python Flask Web应用

一、Ubuntu安装Docker 下面是具体的步骤&#xff1a; 1.准备条件:安装前先卸载操作系统默认安装的docker&#xff0c;再安装必要支持。 #安装前先卸载操作系统默认安装的docker&#xff0c; sudo apt-get remove docker docker-engine docker.io containerd runc#安装必要支持…

7. 实现接口多重断言pytest-assume

pytest-assume 终极指南&#xff1a;实现多重断言的无缝验证 在自动化测试中&#xff0c;单个测试往往需要验证多个条件。本文将深入解析如何通过pytest-assume插件优雅解决多重断言问题。 一、为什么需要多重断言&#xff1f; 传统断言的局限性 def test_user_profile():use…