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

原生鸿蒙像素画板实战 16:多项目管理

单个像素画布能保存,并不意味着编辑器已经具备项目管理。用户真正需要的是:新建一个项目不会覆盖旧画布,切换项目前会把当前修改收束,重命名不会让列表和当前标题不一致,删除当前项目后还能自然回到一个可用状态。bitArt 把这些动作放在 EditorProjectWorkflowService 中,页面只负责打开管理面板和传入用户选择。

项目切换的第一原则是先收束当前会话

如果项目切换时直接 loadProject,正在进行的笔触可能还没有结束,自动保存队列也可能没有把最新像素写盘。用户看到的现象是切回来少了一笔,或者项目列表的更新时间不对。删除当前项目也不能只删文件:当前页面仍持有旧 projectId,下一次保存会把一个“已删除项目”写回去。

项目面板和持久化服务如何配合

项目管理的关键不是列出一堆文件,而是让当前会话能安全交给下一份项目。笔触、动画和保存队列需要在切换前被明确收束。

管理模式和打开模式不是同一个点击语义

当前打开项目的流程先判断是否处于管理模式。管理模式下点击卡片只切换选中状态,不加载项目;普通模式下如果点击当前项目则关闭面板,点击其他项目才依次停止动画、结束笔触、保存当前项目、读取目标项目、应用持久化状态、重新安排自动保存并刷新摘要。这个顺序把“项目切换”从一个跳转动作变成一个带保护的会话交接。

项目名称也有独立的草稿字段。提交时先用 EditorPersistenceService.normalizeProjectName 取得有效名称,再根据它是不是当前项目决定走 saveNow 还是单独 renameProject。这样列表里的非当前项目不需要被完整加载就能改名。

切换项目的顺序必须先结束当前会话,再读取目标项目。

static async openProject(target: EditorProjectWorkflowTarget, context: common.UIAbilityContext | null, projectId: string, hooks: EditorProjectWorkflowHooks): Promise<void> { if (context === null || projectId.length === 0) return; if (target.projectManageMode) { hooks.toggleProjectSelection(projectId); return; } if (projectId === target.currentProjectId) { target.projectManagerOpen = false; return; } hooks.stopAnimationPlayback(); hooks.endStroke(); await hooks.saveNow(); const project = await EditorPersistenceService.loadProject(context, projectId); if (project !== null) { hooks.applyPersistentProject(project); hooks.scheduleAutoSave(); target.projectManagerOpen = false; await hooks.refreshProjectSummaries(); } }

重命名与删除都需要明确的后续状态

项目管理中最重要的不是循环,而是状态机。一个项目卡片在普通模式代表“打开”,在管理模式代表“选择”;删除动作完成后,如果删掉的是当前项目,工作流要从刷新后的摘要中打开第一个剩余项目,若一个也没有则创建默认项目。每一步都有明确的来源和去向,因此不会把空 projectId 留给后续导出或保存。

const fallbackName = EditorProjectPanelService.getProjectNameFallback(target.projectSummaries, projectId, target.canvasWidth, target.canvasHeight); const normalizedName = EditorPersistenceService.normalizeProjectName(target.editingProjectNameText, fallbackName); if (EditorProjectPanelService.updateProjectNameDraft(target, projectId, normalizedName)) { hooks.markProjectDirty(); } if (projectId === target.currentProjectId) { target.currentProjectName = normalizedName; await hooks.saveNow(true); } else { await EditorPersistenceService.renameProject(context, projectId, normalizedName); }

把当前会话交给下一个项目之前

saveCurrentProject、openProjectManager 与 openProject 都先收束动画和笔触。项目面板的 selectedProjectIds 只服务于批量操作,currentProjectId 才是当前工作区的唯一来源。刷新摘要后再关闭面板,可以避免 UI 先关闭却仍显示旧列表的短暂错位。

项目摘要、当前项目 ID 和管理模式各有职责,刷新摘要不能替代应用实际项目数据。

const deletingCurrent = target.selectedProjectIds.indexOf(target.currentProjectId) >= 0; await hooks.saveNow(true); await EditorProjectActionService.deleteProjects(context, target.selectedProjectIds.slice()); target.selectedProjectIds = []; await hooks.refreshProjectSummaries(); if (deletingCurrent) { await EditorProjectWorkflowService.openFallbackProjectAfterDeletion(target, context, defaultCanvasSize, hooks); }

项目管理不能省略的保护步骤

  • 切换项目前先结束笔触和保存,不能依赖页面销毁时的偶然写盘。
  • 管理模式下的点击只能改变 selectedProjectIds,不能同时打开项目。
  • 删除当前项目后必须立即选择后备项目或创建默认项目,不能留下悬空 currentProjectId。

项目管理的价值在于把“可编辑状态”从页面生命周期里拿出来。用户可以创建多个尺寸不同的画布,保存它们各自的图层、帧、调色板和设置,之后在管理器里切换而不需要重新初始化工作区。对于创作工具,这比单纯加一个“新建”按钮更接近真实使用场景。

用两个项目验证切换顺序

多项目管理最适合用“两个不同状态”的对照实验验证。项目 A 使用 32×64 画布、两层和一个自定义色;项目 B 使用 64×64 画布、三层和不同活动帧。在 A 中完成一笔还未显式点击保存的绘制后切到 B,再回到 A,检查像素、尺寸、图层顺序、当前工具和颜色。若任一字段回到默认值,说明保存或 applyPersistentProject 的边界不完整。

删除路径也不能只看文件是否消失。删除非当前项目后,当前工作区应稳定;删除当前项目后,应自动进入另一个可用项目;删除最后一个项目时,应产生默认项目而不是空白页面。项目管理的好体验来自这些后续状态,而不是管理器里那个删除按钮本身。把这组回归固定下来,后续加导入、合并或批量操作时也能及时发现会话交接被破坏。

怎样确认项目切换不会丢修改

项目检查应覆盖当前项目与非当前项目两条路径,尤其要验证删除后的后备状态。

  • 建立两个不同尺寸、不同图层数的项目,切换前后确认每个项目保留自己的像素和活动图层。
  • 在管理模式中点按多个项目,确认只改变选中状态,不改变当前画布。
  • 重命名当前项目与非当前项目,关闭并重开管理器,确认名称一致。
  • 删除当前项目、删除非当前项目和删除最后一个项目,确认都有明确的后续工作区。
项目管理检查记录 切换前收束笔触并保存 管理模式只选择,不打开 改名后摘要和当前标题一致 删除当前项目后有可编辑的后备项目

本地项目管理的范围

当前项目管理针对单设备本地项目,不包含多人协作、云端版本分支或跨设备冲突解决。项目文件导入导出提供了可迁移入口,但不等同于实时同步。

这一篇的重点是把项目切换做成一次安全的会话交接。项目可以并列存在之后,下一篇继续处理更难的情况:怎样把多个项目合成一个新项目,而不覆盖原稿。

http://www.jsqmd.com/news/1242464/

相关文章:

  • 原子工程用AC6编译不过问题
  • 计算机毕业设计之基于SpringBoot的全民健康守护平台设计与实现
  • 江棉斌:一个做新媒体培训机构的老板为什么要开发AI?
  • 2026 年广东厂房隔断、加气块隔墙施工,业主容易踩的几个大坑 - LYL仔仔
  • 2026年AI漫剧制作平台如何实现“漫剧一键生成”,6款AI动态漫生成平台盘点 - 品牌深度评测
  • 深入解析TI AM335x USB DMA:RNDIS与CDC模式配置差异与调试实践
  • 2026抚州口碑好的专业防水公司推荐:卫生间漏水、楼顶漏水、外墙漏水、阳台+阳光房漏水,全场景专业防水解决方案 - 吉林同城获客
  • 什么场景下可以直接做PCF?从食管腺癌进展文献看空间蛋白组价值
  • ComfyUI-LTXVideo终极指南:掌握220亿参数LTX-2视频生成的专业级技术
  • 深入解析TMS320F2837xS DMA与CLA:从寄存器到高性能实时控制实战
  • 从零开始掌握OpenToonz:开源动画软件完全指南
  • 模型越做越大,产线却越跑越慢,怎么破?
  • 厦门闽南特色伴手礼:本地人推荐选购指南解析
  • Honeyview——把看图变成闪电侠的小工具
  • HighFive实战案例:科学数据可视化与大型数据集处理方案
  • 甘肃环保胶粘剂和艺术漆怎么选?别只看价格,先看产品资质、施工保障和配送能力 - 中国品牌价值观察网
  • 【Python毕业设计】基于 Python Web 的医院门诊预约系统 高校 / 社区医院预约挂号服务系统设计(源码+文档+远程调试,全bao定制等)
  • 深入解析DMA描述符与队列管理:构建高效嵌入式数据传输引擎
  • 2026 上海全屋定制实力优选品牌:尊久装饰全屋定制自有设计师与安装团队 - 速递信息
  • 计算机毕业设计之基于SpringBoot的球迷用品销售网站的设计与实现
  • 如何解决DBdeployer部署中的常见问题:错误排查与优化指南
  • 半导体集成电路·设计EDA 应届生初级工程师 简历10维填写范本
  • 惠州买陈皮哪家比较靠谱:二十年品牌参考
  • 实用必备!大连非急救救护车租赁,正规直营车队实力盘点 - 速递信息
  • LambdaWorks与Cairo集成:构建高效ZK应用的完整流程
  • 张家界甲醛检测公司怎么选:只做检测不除醛的专业CMA资质实验室——国慷测研CMA甲醛检测及公共卫生检测 - CMA甲醛检测中心
  • 原生鸿蒙像素画板实战 17:项目合并
  • 关注春城实时动态,合扬整合昆明全市各区当下热点信息 - 生活商业速报
  • 嵌入式软件测试:现状、痛点与专业工具突破
  • 2026哈尔滨平房区名表回收新风向:易奢福全国连锁深耕三十年,百家门店护航,上门估价1公里极速响应 - 肉松卷