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

HarmonyOS 5 双向滚动课程表:技术实现与交互设计解析(附:源代码)

在移动应用开发中,复杂数据的可视化展示一直是用户体验的关键环节。本文将围绕鸿蒙OS平台的双向滚动课程表展开,深入解析其技术实现原理、交互设计逻辑以及双向滚动功能的应用场景,为开发者提供从原理到实践的完整技术指南。

鸿蒙OS双向滚动课程表的核心架构

鸿蒙OS的课程表应用采用了双向滚动技术,实现了时间维度与课程维度的高效展示。整个系统由以下几个核心部分构成:

数据模型设计

课程表的基础数据模型定义了课程的基本属性,包括课程名称和背景颜色,这些属性不仅用于展示课程信息,还通过不同颜色区分不同课程类型,提升视觉辨识度:

export class Course {public name: ResourceStr = '-'public backColor: ResourceColor = '#EDC7FF'constructor(name: ResourceStr, backgroundColor: ResourceColor) {this.name = name;this.backColor = backgroundColor;}
}

课程数据数组COURSE_MODEL预定义了各类课程实例,包括空课程和不同科目的课程,每种课程分配了独特的背景色以增强可视化效果:

export const COURSE_MODEL: Course[] = [new Course($r('app.string.empty'), Color.White),new Course($r('app.string.course8'), '#EDC7FF'), new Course($r('app.string.course5_1'), '#FFE1E8'),// 更多课程定义...new Course($r('app.string.course9'), '#ECDC96')]

双向滚动核心实现

双向滚动功能的实现依赖于鸿蒙OS的滚动控制器机制,通过多个Scroller实例协同工作,实现水平与垂直方向的滚动同步:

@Entry
@Component
export struct Index {// 滚动控制器定义classScroller = new Scroller();timeScroller = new Scroller();weekdaysScroller = new Scroller();horizontalScroller = new Scroller();verticalScroller = new Scroller();// 课程数据矩阵,实现二维数据展示data: Course[][] = [[COURSE_MODEL[0], COURSE_MODEL[1], COURSE_MODEL[4], COURSE_MODEL[1],COURSE_MODEL[10], COURSE_MODEL[2], COURSE_MODEL[0]],// 更多课程数据...]// 其他组件状态定义...
}

核心布局部分通过嵌套的Scroll组件实现双向滚动功能,水平滚动控制器与垂直滚动控制器通过事件监听实现联动:

Column() {// 表头部分,包含星期和课时信息Row() {// 课时序号列Column() {Text('节').fontSize(14).fontWeight(400).textAlign(TextAlign.Center)}.width(50).height(42)// 时间列Column() {Text('上课时间').fontSize(14).fontWeight(400).textAlign(TextAlign.Center)}.width(60).height(42)// 星期水平滚动列Column() {Scroll(this.weekdaysScroller) {List() {ForEach(this.classificationNames, (item: Resource, index: number) => {ListItem() {Text(item).fontSize(14).textAlign(TextAlign.Center)}})}}.onScrollFrameBegin((offset: number) => {this.horizontalScroller.scrollBy(offset, 0);return { offsetRemain: offset };})}.width('70%')}// 内容区双向滚动实现Row() {// 左侧课时和时间垂直滚动区Column() {Scroll(this.classScroller) { /* 课时序号垂直滚动 */ }Scroll(this.timeScroller) { /* 时间垂直滚动 */ }}// 右侧课程内容双向滚动区Column() {Scroll(this.horizontalScroller) {Scroll(this.verticalScroller) {// 课程内容矩阵渲染ForEach(this.arr, (_temp: number, index: number) => {Row() {ForEach(this.data[this.arr[_temp]], (item: Course) => {this.itemBuilder(item.name, 100, 120, item.backColor, Color.Black)})}})}.onScrollFrameBegin((offset: number) => {this.classScroller.scrollBy(0, offset);this.timeScroller.scrollBy(0, offset)})}.onScrollFrameBegin((offset: number) => {this.weekdaysScroller.scrollBy(offset, 0);})}}
}

双向滚动功能的技术原理

双向滚动技术在鸿蒙OS中的实现,突破了传统列表只能单向滚动的限制,通过多个滚动控制器的协同工作,实现了二维数据的高效展示。其核心技术要点包括:

滚动控制器的协同机制

鸿蒙OS的Scroller类提供了精确控制滚动位置的能力,通过scrollByscrollTo方法可以实现程序化滚动,而onScrollFrameBegin事件则允许在滚动过程中实时同步多个控制器的位置:

// 水平滚动同步逻辑
.onScrollFrameBegin((offset: number) => {this.horizontalScroller.scrollBy(offset, 0);return { offsetRemain: offset };
})// 垂直滚动同步逻辑
.onScrollFrameBegin((offset: number) => {this.verticalScroller.scrollBy(0, offset);this.timeScroller.scrollBy(0, offset);return { offsetRemain: offset };
})

这种同步机制确保了用户在水平滚动查看不同星期的课程时,左侧的课时和时间列能够保持固定位置,反之亦然,从而实现类似Excel表格的冻结表头效果。

二维数据的渲染优化

课程表采用了二维数组data: Course[][]存储课程数据,通过双重ForEach循环渲染课程卡片,实现了行(课时)与列(星期)的矩阵式展示:

ForEach(this.arr, (_temp: number, index: number) => {Row() {ForEach(this.data[this.arr[_temp]], (item: Course) => {this.itemBuilder(item.name, 100, 120, item.backColor, Color.Black)}, (item: Course) => getResourceID().toString())}
}, (_temp: number) => _temp + new Date().toString())

这种渲染方式结合滚动控制器,使得大规模课程数据能够按需加载,避免了一次性渲染大量组件带来的性能问题。

交互体验优化细节

课程表在交互细节上做了多项优化,包括:

  1. 初始位置自动定位:根据当前星期自动滚动到对应的日期列
.onAppear(() => {if (this.weekDay > 1) {this.weekdaysScroller.scrollTo({ xOffset: (this.weekDay - 1) * 120, yOffset: 0 })this.horizontalScroller.scrollTo({ xOffset: (this.weekDay - 1) * 120, yOffset: 0 })}
})
  1. 卡片式UI设计:每个课程使用独立卡片展示,通过背景色区分不同课程,提升视觉层次感
@Builder
itemBuilder(msg: ResourceStr, curHeight: Length = 100, curWidth: Length = 120,curBackgroundColor: ResourceColor = Color.White, curFontColor: ResourceColor = Color.Black) {Row() {Column() {Stack() {Column() { }.backgroundColor(curBackgroundColor).width('100%').height('100%').opacity(0.7)Text(msg).fontSize(12).textAlign(TextAlign.Center).fontColor(curFontColor).opacity(0.9).padding(6)}}.padding(6).width(curWidth).height(curHeight).backgroundColor(Color.White)}
}
  1. 标签页导航:通过底部标签页切换不同功能模块,课程表作为核心功能位于默认标签页
@Builder
tabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {Column() {Image(this.currentIndex === targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 })Text(title).fontColor(this.currentIndex === targetIndex ? '#0A59F7' : '#60000000')}.height('100%').margin({ top: 8 })
}

双向滚动技术的应用场景拓展

双向滚动技术不仅适用于课程表,还能在多种需要二维数据展示的场景中发挥价值,其核心优势在于能够同时展示两个维度的信息,并保持维度之间的关联关系。

企业应用中的数据可视化

在企业管理系统中,双向滚动技术可用于:

  1. 财务报表分析:横向展示时间维度(月份/季度),纵向展示科目维度(收入/支出),交叉点显示具体数值
  2. 项目甘特图:横向为时间轴,纵向为任务列表,直观展示项目进度
  3. 人力资源管理:横向为时间周期,纵向为员工列表,展示考勤、绩效等信息

教育领域的创新应用

除课程表外,教育类应用还可拓展:

  1. 知识图谱可视化:横向为学科分支,纵向为难度层级,帮助学生梳理知识结构
  2. 学习进度跟踪:横向为时间维度,纵向为知识点列表,显示掌握程度
  3. 多维度测试分析:横向为题型,纵向为学生列表,展示答题情况

生活服务类应用场景

在生活服务领域,双向滚动技术可用于:

  1. 日程管理:横向为日期,纵向为不同类型的日程(工作/生活/家庭)
  2. 健身数据监测:横向为时间,纵向为健康指标(心率/血压/体重)
  3. 旅行规划:横向为旅行天数,纵向为行程类型(交通/住宿/景点)

技术实现的挑战与优化策略

双向滚动技术在带来强大展示能力的同时,也面临一些技术挑战:

性能优化关键点

  1. 虚拟列表渲染:对于大规模数据,使用LazyForEach代替ForEach,实现按需渲染
// 虚拟列表优化示例
LazyForEach(this.arr, (_temp: number, index: number) => {// 渲染逻辑...
}, (_temp: number) => _temp.toString())
  1. 滚动事件节流:限制滚动事件触发频率,避免频繁重绘
// 滚动事件节流实现
let lastScrollTime = 0;
.onScrollFrameBegin((offset: number) => {const now = new Date().getTime();if (now - lastScrollTime > 100) { // 100ms内只处理一次lastScrollTime = now;// 滚动同步逻辑...}return { offsetRemain: offset };
})
  1. 组件复用:将课程卡片等重复组件封装为独立@Builder方法,提高渲染效率

多设备适配方案

  1. 响应式布局:根据屏幕宽度动态调整列宽和卡片大小
@State screenWidth: number = getContentRect().width;// 根据屏幕宽度获取合适的列宽
private getColumnWidth(): number {if (this.screenWidth < 600) return 100;   // 小屏设备if (this.screenWidth < 1024) return 120;  // 平板return 150;  // 大屏设备
}
  1. 手势适配:针对不同设备优化滚动灵敏度和惯性效果
Scroll(this.horizontalScroller) {// 内容...
}
.scrollBar(BarState.Off)
.scrollable(ScrollDirection.Horizontal)
.dragEffect(DragEffect.Fling)  // 惯性滚动效果
.friction(0.8)  // 滚动摩擦系数

附:代码

import { common } from '@kit.AbilityKit';import { CommonModifier } from '@kit.ArkUI';let preResourceId = 0;export class Course {public name: ResourceStr = '-'public backColor: ResourceColor = '#EDC7FF'constructor(name: ResourceStr, backgroundColor: ResourceColor) {this.name = name;this.backColor = backgroundColor;}
}export const COURSE_MODEL: Course[] = [new Course($r('app.string.empty'), Color.White),new Course($r('app.string.course8'), '#EDC7FF'), new Course($r('app.string.course5_1'), '#FFE1E8'),new Course($r('app.string.course2'), '#E1F9FF'), new Course($r('app.string.course1_1'), '#E5F0E1'),new Course($r('app.string.course7'), '#FFDACC'), new Course($r('app.string.course6'), '#ECFC9A'),new Course($r('app.string.course5'), '#FFE1E8'), new Course($r('app.string.course4_1'), '#FFFCA3'),new Course($r('app.string.course4'), '#FBF0FF'), new Course($r('app.string.course3'), '#FFE4C7'),new Course($r('app.string.course1'), '#FFF0F0'), new Course($r('app.string.course9'), '#ECDC96')]@Entry
@Component
export struct Index {@State classIndex: Array<string> = ['1', '2', '3', '4', '5', '6', '7', '8']@State classTime: Array<string> = ['8:30-9:15', '9:25-10:10', '10:30-11:15', '11:15-12:10','14:10-14:55', '15:05-15:45', '15:55-16:30', '16:45-17:00']weekDay: number = new Date().getDay()classScroller = new Scroller();timeScroller = new Scroller();weekdaysScroller = new Scroller();horizontalScroller = new Scroller();verticalScroller = new Scroller();data: Course[][] = [[COURSE_MODEL[0], COURSE_MODEL[1], COURSE_MODEL[4], COURSE_MODEL[1],COURSE_MODEL[10], COURSE_MODEL[2], COURSE_MODEL[0]],[COURSE_MODEL[3], COURSE_MODEL[1], COURSE_MODEL[4], COURSE_MODEL[1],COURSE_MODEL[10], COURSE_MODEL[2], COURSE_MODEL[0]],[COURSE_MODEL[5], COURSE_MODEL[8], COURSE_MODEL[9], COURSE_MODEL[0],COURSE_MODEL[0], COURSE_MODEL[7], COURSE_MODEL[10]],[COURSE_MODEL[5], COURSE_MODEL[8], COURSE_MODEL[9], COURSE_MODEL[0],COURSE_MODEL[0], COURSE_MODEL[7], COURSE_MODEL[10]],[COURSE_MODEL[11], COURSE_MODEL[1], COURSE_MODEL[2], COURSE_MODEL[0],COURSE_MODEL[0], COURSE_MODEL[0], COURSE_MODEL[0]],[COURSE_MODEL[11], COURSE_MODEL[12], COURSE_MODEL[2], COURSE_MODEL[12],COURSE_MODEL[0], COURSE_MODEL[0], COURSE_MODEL[0]],[COURSE_MODEL[11], COURSE_MODEL[12], COURSE_MODEL[2], COURSE_MODEL[12],COURSE_MODEL[4], COURSE_MODEL[5], COURSE_MODEL[0]],[COURSE_MODEL[0], COURSE_MODEL[0], COURSE_MODEL[6], COURSE_MODEL[1],COURSE_MODEL[4], COURSE_MODEL[5], COURSE_MODEL[6]]]arr: number[] = []classificationNames: Array<Resource> = [$r('app.string.monday'), $r('app.string.tuesday'),$r('app.string.wednesday'),$r('app.string.thursday'), $r('app.string.friday'), $r('app.string.saturday'), $r('app.string.sunday')]@State currentIndex: number = 2;private controller: TabsController = new TabsController();@StorageProp('bottomRectHeight') bottomRectHeight: number = 0;@StorageProp('topRectHeight') topRectHeight: number = 0;@State tabBarModifier: CommonModifier = new CommonModifier();aboutToAppear() {for (let index = 0; index < this.classIndex.length; index++) {this.arr.push(index)}this.tabBarModifier.alignRules({})}@BuilderitemBuilder(msg: ResourceStr, curHeight: Length = 100, curWidth: Length = 120,curBackgroundColor: ResourceColor = Color.White, curFontColor: ResourceColor = Color.Black) {Row() {Column() {Stack() {Column() {}.backgroundColor(curBackgroundColor).width('100%').height('100%').opacity(0.7)Text(msg).fontSize(12).fontWeight(400).textAlign(TextAlign.Center).fontColor(curFontColor).opacity(0.9).width('100%').height('100%').padding(6)}}.padding(6).justifyContent(FlexAlign.Center).width(curWidth).height(curHeight).backgroundColor(Color.White)}}@BuildertabBuilder(title: string, targetIndex: number, selectedImg: Resource, normalImg: Resource) {Column() {Image(this.currentIndex === targetIndex ? selectedImg : normalImg).size({ width: 25, height: 25 })Text(title).fontColor(this.currentIndex === targetIndex ? '#0A59F7' : '#60000000').fontSize(10).fontWeight(500).margin({ top: 4 })}.height('100%').margin({ top: 8 }).width('auto').alignItems(HorizontalAlign.Center)}build() {Tabs({barPosition: BarPosition.End,index: this.currentIndex,controller: this.controller,barModifier: this.tabBarModifier}) {TabContent() {Column() {}.padding({ top: px2vp(this.topRectHeight) }).height('100%')}.height('100%').tabBar(this.tabBuilder('主页', 0, $r('app.media.main_page'), $r('app.media.main_page')))TabContent() {Column() {}.padding({ top: px2vp(this.topRectHeight) }).height('100%')}.height('100%').tabBar(this.tabBuilder('消息', 1, $r('app.media.message'), $r('app.media.message')))TabContent() {Column() {Row() {Image($r('app.media.back')).width(42).margin({ right: 8 })Text('第8周').fontSize(22).fontWeight(800).lineHeight(27)}.margin({ left: 32, bottom: 8, top: 12 }).height(42).width('100%').justifyContent(FlexAlign.Start)Column() {Row() {Column() {Text('节').fontSize(14).fontWeight(400).textAlign(TextAlign.Center).fontColor(Color.Black).width('100%').height('100%')}.justifyContent(FlexAlign.Center).width(50).height(42)Column() {Text('上课时间').fontSize(14).fontWeight(400).textAlign(TextAlign.Center).fontColor(Color.Black).width('100%').height('100%')}.justifyContent(FlexAlign.Center).width(60).height(42)Column() {Scroll(this.weekdaysScroller) {List() {ForEach(this.classificationNames, (item: Resource, index: number) => {ListItem() {Row() {Column() {Text(item).fontSize(14).fontWeight(400).textAlign(TextAlign.Center).fontColor(Color.Black).width('100%')}.padding(4).justifyContent(FlexAlign.Center).width(120).height(42).backgroundColor(Color.White)}}}, (item: string) => item + new Date().toString())}.listDirection(Axis.Horizontal).edgeEffect(EdgeEffect.None)}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off).height(42).onScrollFrameBegin((offset: number) => {this.horizontalScroller.scrollBy(offset, 0);return { offsetRemain: offset };})}.width('70%')}.width('100%').position({ x: 0, y: 0 }).backgroundColor(Color.White).borderRadius({ topLeft: 16 }).shadow({ radius: 50, color: '#25000000' }).zIndex(10)Row() {Column() {Scroll(this.classScroller) {Column() {List({ space: 0, initialIndex: 0 }) {ForEach(this.classIndex, (item: string, index: number) => {ListItem() {Row() {Column() {Text(item).fontSize(14).fontWeight(400).textAlign(TextAlign.Center).fontColor(Color.Black).width('100%')}.padding(4).justifyContent(FlexAlign.Center).width(50).height(100).backgroundColor(Color.White)}}}, (item: string) => JSON.stringify(item))}.listDirection(Axis.Vertical).edgeEffect(EdgeEffect.None) // 滑动到边缘无效果}}.position({ x: 0, y: 0 }).width(50).backgroundColor('#F8F8FF').scrollBar(BarState.Off).onScrollFrameBegin((offset: number) => {this.verticalScroller.scrollBy(0, offset);this.timeScroller.scrollBy(0, offset);return { offsetRemain: offset };})Scroll(this.timeScroller) {Column() {List({ space: 0, initialIndex: 0 }) {ForEach(this.classTime, (item: string, index: number) => {ListItem() {Row() {Column() {Text(item.split('-')[0] + ' -').fontSize(12).textAlign(TextAlign.Center).fontColor(Color.Black).width('100%').height('50%').align(Alignment.Bottom).lineHeight(20)Text(item.split('-')[1]).fontSize(12).textAlign(TextAlign.Center).fontColor(Color.Black).width('100%').height('50%').align(Alignment.Top).lineHeight(20)}.padding(4).justifyContent(FlexAlign.Center).width(60).height(100).backgroundColor(Color.White)}}}, (item: string) => JSON.stringify(item))}.listDirection(Axis.Vertical).edgeEffect(EdgeEffect.None)}}.position({ x: 50, y: 0 }).width(60).backgroundColor('#F8F8FF').scrollBar(BarState.Off).onScrollFrameBegin((offset: number) => {this.verticalScroller.scrollBy(0, offset);this.classScroller.scrollBy(0, offset);return { offsetRemain: offset };})}.width(110).height('100%').backgroundColor(Color.White).shadow({ radius: 45, color: '#25000000', offsetY: -15 }).padding({top:4, bottom:20})Column() {Scroll(this.horizontalScroller) {Scroll(this.verticalScroller) {Column() {ForEach(this.arr, (_temp: number, index: number) => {Row() {ForEach(this.data[this.arr[_temp]], (item: Course) => {this.itemBuilder(item.name, 100, 120, item.backColor, Color.Black)}, (item: Course) => getResourceID().toString())}}, (_temp: number) => _temp + new Date().toString())}}.scrollBar(BarState.Off).scrollable(ScrollDirection.Vertical).height('100%').width(this.classificationNames.length * 120).onScrollFrameBegin((offset: number) => {this.classScroller.scrollBy(0, offset);this.timeScroller.scrollBy(0, offset)return { offsetRemain: offset };})}.padding({top:4, left:4, right:4, bottom:20}).scrollBar(BarState.Off).scrollable(ScrollDirection.Horizontal).onScrollFrameBegin((offset: number) => {this.weekdaysScroller.scrollBy(offset, 0);return { offsetRemain: offset };})}.zIndex(-100).width('70%')}.position({ x: 0, y: 42 }).width('100%').height('100%').backgroundColor(Color.White)}.height('88%').onAppear(() => {if (this.weekDay > 1) {this.weekdaysScroller.scrollTo({ xOffset: (this.weekDay - 1) * 120, yOffset: 0 })this.horizontalScroller.scrollTo({ xOffset: (this.weekDay - 1) * 120, yOffset: 0 })}}).width('100%')}.padding({ top: px2vp(this.topRectHeight) }).height('100%')}.height('100%').backgroundColor('#F1F3F5').tabBar(this.tabBuilder('课表', 2, $r('app.media.course_table_clicked'), $r('app.media.course_table_unclicked')))}.onChange((index: number) => {this.currentIndex = index;}).width('100%').height('100%').barHeight(80).barBackgroundColor('#F1F3F5')}
}function getResourceID(): string {preResourceId++;return preResourceId.toString()
}

// string.json

{"string": [{"name": "module_desc","value": "module description"},{"name": "EntryAbility_desc","value": "description"},{"name": "EntryAbility_label","value": "label"},{"name": "course1","value": "B4037 软件项目管理   1-18周 2C304"},{"name": "course2","value": "B1469 Java Web框架技术   1-18周 1504"},{"name": "course3","value": "B1427 统一建模语言   1-18周 1509"},{"name": "course4","value": "B1678 网络管理与维护   1-18周 1510"},{"name": "course5","value": "B2857 软件工程   1-18周 1411"},{"name": "course6","value": "B0383 大学生就业指导   1-8周 3406"},{"name": "course1_1","value": "B4037 软件项目管理   1、3、5、7、9、11、13、15、17周 2C304"},{"name": "course7","value": "B3873 信息检索   1-8周 3408"},{"name": "course8","value": "B1478 移动应用开发综合实训   1-18周 2C602"},{"name": "course4_1","value": "B1678 网络管理与维护   2、4、6、8、10、12、14、16、18周 1604"},{"name": "course5_1","value": "B2857 软件工程   1、3、5、7、9、11、13、15、17周 1509"},{"name": "course9","value": "B2857 软件工程综合实训   11-12周 1502"},{"name": "empty","value": "-"},{"name": "monday","value": "星期一"},{"name": "tuesday","value": "星期二"},{"name": "wednesday","value": "星期三"},{"name": "thursday","value": "星期四"},{"name": "friday","value": "星期五"},{"name": "saturday","value": "星期六"},{"name": "sunday","value": "星期天"}]
}

鸿蒙OS双向滚动课程表展示了二维数据可视化的优雅解决方案,通过滚动控制器的协同工作和精心设计的UI布局,实现了时间与课程维度的高效展示。这种技术不仅适用于教育领域的课程表,还能在企业管理、健康医疗、生活服务等多个领域发挥重要作用。

对于开发者而言,双向滚动技术的核心在于理解多个滚动控制器的协同机制,以及二维数据的高效渲染方式。通过合理的性能优化和多设备适配策略,可以将这种技术应用于更广泛的场景,为用户提供更加直观、高效的数据可视化体验。随着鸿蒙OS生态的不断发展,双向滚动技术必将在更多创新应用中展现其价值。

http://www.lqws.cn/news/493381.html

相关文章:

  • 谷歌地图的3d街景使用的是什么数据格式?
  • Java 程序设计试题​
  • 常见JavaScript 代理模式应用场景解析
  • 6.23_JAVA_RabbitMQ
  • 2025年中科院三区全新算法,恒星振荡优化器:受自然启发的元启发式优化,完整MATLAB代码免费获取
  • hive集群优化和治理常见的问题答案
  • 综述AI生成工具推荐:高效自动化生成学术综述
  • 网络安全之某cms的漏洞分析
  • MocapApi 中文文档 和github下载地址 NeuronDataReader(以下简称 NDR)的下一代编程接口
  • 1 Studying《Systems.Performance》7-13
  • Maven 多模块项目调试与问题排查总结
  • SpreadJS 迷你图:数据趋势可视化的利器
  • Web基础 -SpringBoot入门 -HTTP-分层解耦 -三层架构
  • HTML语义化标签
  • 最近小峰一直在忙国际化项目,确实有点分身乏术... [特殊字符] 不过! 我正紧锣密鼓准备一系列干货文章/深度解析
  • [HTML]iframe显示pdf,隐藏左侧分页
  • Python异步爬虫编程技巧:从入门到高级实战指南
  • 从本地到云端:通过ToolJet和cpolar构建远程开发环境实践过程
  • ​​FFmpeg命令全解析:三步完成视频合并、精准裁剪​​、英伟达显卡加速
  • systemd[1]: Failed to start LSB: Bring up/down networking
  • 在大数据求职面试中如何回答分布式协调与数据挖掘问题
  • 开疆智能CCLinkIE转ModbusTCP网关连接 BORUNTE伯朗特机器人案例
  • 百度AIP:Springboot人脸对比
  • 用安卓手机,怎样远程管理孩子iPhone屏幕使用时间?
  • LNMP 一键部署脚本 shell脚本
  • R语言入门课| 05 一文掌握R语言常见数据类型
  • 面试150 分发糖果
  • 【设计模式精讲 Day 12】代理模式(Proxy Pattern)
  • idea 报错:java: 非法字符: ‘\ufeff‘
  • ISO 26262-11 半导体功能安全学习(二)