在医疗健康领域,用户对APP的核心诉求是​​信息清晰、操作便捷、反馈及时​​。传统开发模式中,UI与逻辑的强耦合常导致界面更新繁琐、状态管理混乱,难以满足医疗场景对“精准性”和“效率”的高要求。ArkUI-X作为鸿蒙生态的声明式UI框架,通过​​数据驱动视图​​、​​组件化设计​​和​​状态自动同步​​等特性,为医疗APP开发提供了更高效的解决方案。本文将结合医疗APP的典型场景(如病历展示、检查报告列表、用药提醒),通过代码实例解析ArkUI-X如何通过声明式UI提升用户体验。


一、医疗APP的用户体验痛点与传统开发困境

医疗APP的用户多为患者或医护人员,其对界面的核心需求包括:

  • ​信息层级清晰​​:关键数据(如检查结果、用药剂量)需突出显示;
  • ​操作路径简单​​:高频操作(如查看报告、设置提醒)需一步直达;
  • ​状态反馈及时​​:数据更新(如新增检查记录)需即时反映在界面。

传统开发中,UI与逻辑的强耦合常导致以下问题:

  • ​代码冗余​​:修改界面布局需同时调整XML/Storyboard和对应的Java/Swift代码;
  • ​状态同步复杂​​:数据变更后需手动调用notifyDataSetChanged()或更新视图属性;
  • ​跨组件通信困难​​:多层级组件间传递数据需依赖接口回调或全局变量,易引发维护难题。

ArkUI-X的声明式UI通过“数据驱动视图”彻底解决了这些问题:​​只需关注数据状态变化,UI会自动同步更新​​,开发者无需手动操作视图。


二、声明式UI在医疗APP中的核心实践

场景1:病历卡片——动态信息的高效展示

患者病历通常包含基础信息(姓名、年龄)、诊断结果、用药记录等多维度数据。传统开发中,展开/收起详情需手动控制布局显隐;而ArkUI-X通过​​状态绑定​​和​​条件渲染​​,可轻松实现流畅的交互。

代码实现:动态病历卡片
// MedicalRecordCard.ets
@Entry
@Component
struct MedicalRecordCard {
  // 模拟从接口获取的病历数据
  @State record: MedicalRecord = {
    id: 'MR001',
    patientName: '张三',
    age: 45,
    diagnosis: '2型糖尿病',
    medications: [
      { name: '二甲双胍', dosage: '0.5g/次', frequency: '每日3次' },
      { name: '胰岛素', dosage: '早12U/晚8U', frequency: '餐前皮下注射' }
    ],
    isExpanded: false // 控制详情是否展开的状态
  };

  build() {
    Card() {
      Column({ space: 12 }) {
        // 基础信息行(始终显示)
        Row({ space: 16 }) {
          Text(`患者:${this.record.patientName}`)
            .fontWeight(FontWeight.Medium)
          
          Text(`年龄:${this.record.age}岁`)
            .fontColor('#666')
          
          Text(`诊断:${this.record.diagnosis}`)
            .fontColor('#222')
        }
        .width('100%')

        // 用药记录(根据isExpanded状态动态显示)
        if (this.record.isExpanded) {
          Text('用药记录:')
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 8 })

          List() {
            ForEach(this.record.medications, (med) => {
              ListItem() {
                Column({ space: 4 }) {
                  Text(med.name)
                    .fontSize(14)
                    .fontWeight(FontWeight.Medium)
                  
                  Text(`剂量:${med.dosage}`)
                    .fontSize(12)
                    .fontColor('#666')
                  
                  Text(`频率:${med.frequency}`)
                    .fontSize(12)
                    .fontColor('#666')
                }
              }
            })
          }
          .width('100%')
          .divider({ strokeWidth: 0.5, color: '#EEE' })
        }

        // 展开/收起按钮
        Button(this.record.isExpanded ? '收起详情' : '查看详情')
          .onClick(() => {
            this.record.isExpanded = !this.record.isExpanded; // 状态变更自动触发展开/收起
          })
          .width('80%')
      }
      .padding(16)
    }
    .width('90%')
    .margin(8)
  }
}

// 模拟病历数据类型
interface MedicalRecord {
  id: string;
  patientName: string;
  age: number;
  diagnosis: string;
  medications: Array<{ name: string; dosage: string; frequency: string }>;
  isExpanded: boolean;
}

​用户体验提升点​​:

  • ​动态渲染​​:通过if (this.record.isExpanded)条件渲染用药记录,避免无关信息干扰;
  • ​状态驱动​​:点击按钮仅修改isExpanded状态,UI自动展开/收起,无需手动调整布局;
  • ​信息层级​​:关键诊断信息始终显示,次要用药记录按需展开,符合医疗场景的“信息优先级”原则。

场景2:检查报告列表——实时数据的高效同步

检查报告(如血常规、影像)是医疗APP的核心功能模块。用户需要快速浏览报告状态(待查看/已完成),并点击查看详情。传统开发中,列表数据更新需手动调用notifyDataSetChanged(),易导致界面卡顿;ArkUI-X通过​​@State管理列表数据​​和​​List组件自动刷新​​,实现“数据变更即视图更新”。

代码实现:动态检查报告列表
// TestReportList.ets
@Entry
@Component
struct TestReportList {
  // 模拟从接口获取的报告数据(含加载状态)
  @State reports: Array<TestReport> = [];
  @State isLoading: boolean = true;

  aboutToAppear() {
    // 模拟网络请求加载数据
    setTimeout(() => {
      this.reports = [
        { 
          id: 'R001', 
          type: '血常规', 
          time: '2024-03-15 09:30', 
          status: 'completed', 
          result: '白细胞计数:5.2×10⁹/L(正常)' 
        },
        { 
          id: 'R002', 
          type: '胸部CT', 
          time: '2024-03-14 14:15', 
          status: 'pending', 
          result: '' 
        }
      ];
      this.isLoading = false;
    }, 1500);
  }

  build() {
    Column() {
      Text('检查报告')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 16, bottom: 16 })

      // 加载状态提示
      if (this.isLoading) {
        LoadingProgress()
          .color('#007DFF')
          .width(40)
          .height(40)
      } else {
        // 报告列表
        List() {
          ForEach(this.reports, (report) => {
            ListItem() {
              Row({ space: 16 }) {
                // 报告类型图标(根据状态动态颜色)
                Image($r('app.media.ic_test'))
                  .width(24)
                  .height(24)
                  .tint(report.status === 'completed' ? '#4CAF50' : '#FF9800')

                Column({ space: 4 }) {
                  Text(report.type)
                    .fontSize(16)
                    .fontWeight(FontWeight.Medium)
                  
                  Text(`时间:${report.time}`)
                    .fontSize(12)
                    .fontColor('#666')
                  
                  // 状态标签(不同状态不同样式)
                  Text(report.status === 'completed' ? '已完成' : '待查看')
                    .fontSize(12)
                    .fontColor(report.status === 'completed' ? '#4CAF50' : '#FF9800')
                    .backgroundColor(report.status === 'completed' ? '#E8F5E9' : '#FFF3E0')
                    .padding({ left: 8, right: 8, top: 2, bottom: 2 })
                }

                Blank()

                // 箭头图标(引导点击)
                Image($r('app.media.ic_arrow_right'))
                  .width(16)
                  .height(16)
              }
              .width('100%')
              .padding(16)
              .borderRadius(8)
              .backgroundColor('#FFF')
              .onClick(() => {
                // 跳转到报告详情页(状态传递)
                router.pushUrl({
                  url: 'pages/TestReportDetail',
                  params: { reportId: report.id }
                });
              })
            }
          })
        }
        .width('100%')
        .divider({ strokeWidth: 0.5, color: '#EEE' })
      }
    }
    .width('100%')
    .padding(16)
  }
}

// 模拟检查报告数据类型
interface TestReport {
  id: string;
  type: string;
  time: string;
  status: 'completed' | 'pending'; // 已完成/待查看
  result: string;
}

​用户体验提升点​​:

  • ​实时加载反馈​​:通过isLoading状态控制加载进度条,避免用户误以为界面卡死;
  • ​状态可视化​​:报告状态(完成/待查看)通过图标颜色和标签样式直观区分,降低用户理解成本;
  • ​流畅交互​​:点击列表项直接跳转详情页,数据通过router.pushUrl传递,状态管理由框架自动处理。

场景3:用药提醒——交互闭环的高效实现

用药提醒是慢性病患者的核心需求。用户需设置提醒时间、选择重复周期,并接收系统通知。传统开发中,时间选择、周期勾选和通知注册需跨多个组件,状态同步复杂;ArkUI-X通过​​@State绑定表单数据​​和​​跨组件事件响应​​,实现“设置-保存-提醒”的闭环体验。

代码实现:智能用药提醒设置
// MedicationReminder.ets
@Entry
@Component
struct MedicationReminder {
  // 提醒配置数据(初始为空)
  @State reminder: ReminderConfig = {
    medicineName: '',
    time: '08:00',
    repeatDays: [] as Array<number>, // 0=周日,1=周一...6=周六
    isActive: false
  };

  build() {
    Column({ space: 20 }) {
      Text('设置用药提醒')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      // 药品名称输入
      TextInput({ placeholder: '请输入药品名称' })
        .width('90%')
        .onChange((value) => {
          this.reminder.medicineName = value;
        })

      // 提醒时间选择(使用鸿蒙原生时间选择器)
      TimePicker({ type: TimePickerType.HoursMinutes })
        .value(new Date(`2024-03-15T${this.reminder.time}`))
        .onChange((value: Date) => {
          // 格式化时间为HH:MM
          const hours = value.getHours().toString().padStart(2, '0');
          const minutes = value.getMinutes().toString().padStart(2, '0');
          this.reminder.time = `${hours}:${minutes}`;
        })

      // 重复周期选择(多选框组)
      Text('重复周期').fontSize(16).fontWeight(FontWeight.Medium)
      Row({ space: 16 }) {
        ForEach([0, 1, 2, 3, 4, 5, 6], (day) => {
          Row({ space: 8 }) {
            Checkbox()
              .select(this.reminder.repeatDays.includes(day))
              .onChange((checked) => {
                if (checked) {
                  // 添加选中日期
                  if (!this.reminder.repeatDays.includes(day)) {
                    this.reminder.repeatDays.push(day);
                  }
                } else {
                  // 移除取消日期
                  this.reminder.repeatDays = this.reminder.repeatDays.filter(d => d !== day);
                }
              })
            Text(this.getDayName(day))
              .fontSize(14)
          }
        })
      }

      // 启用/禁用提醒开关
      Row({ space: 8 }) {
        Text('启用提醒')
          .fontSize(16)
        Toggle({ type: ToggleType.Switch })
          .checked(this.reminder.isActive)
          .onChange((checked) => {
            this.reminder.isActive = checked;
            if (checked) {
              this.saveReminder(); // 启用时保存配置并注册通知
            } else {
              this.cancelReminder(); // 禁用时取消通知
            }
          })
      }

      // 保存按钮
      Button('保存设置')
        .width('80%')
        .onClick(() => {
          this.saveReminder();
        })
    }
    .width('100%')
    .padding(20)
  }

  // 根据数字获取星期名称
  private getDayName(day: number): string {
    const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
    return days[day];
  }

  // 保存提醒配置并注册系统通知
  private saveReminder() {
    console.info('保存提醒配置:', this.reminder);
    // 实际开发中调用鸿蒙通知API注册提醒
    // 例如:使用NotificationManager设置周期性通知
  }

  // 取消已注册的通知
  private cancelReminder() {
    console.info('取消提醒配置');
    // 实际开发中调用鸿蒙通知API取消提醒
  }
}

// 提醒配置数据类型
interface ReminderConfig {
  medicineName: string;
  time: string;
  repeatDays: number[]; // 0-6对应周日-周六
  isActive: boolean;
}

​用户体验提升点​​:

  • ​表单联动​​:药品名称、时间、周期的修改实时同步到reminder状态,无需手动提交;
  • ​交互闭环​​:启用开关直接触发通知注册/取消,用户操作与实际效果强关联;
  • ​清晰反馈​​:保存按钮明确提示操作完成,符合医疗场景“确认即安全”的用户心理。

三、ArkUI-X声明式UI的核心优势总结

通过以上医疗APP场景的实践,ArkUI-X的声明式UI在以下方面显著提升了用户体验:

​维度​​传统开发模式​​ArkUI-X声明式UI​
​界面更新​需手动调用invalidate()或更新视图属性数据变化自动触发UI刷新,无需手动干预
​状态管理​状态分散在各组件,同步复杂通过@State/@Prop集中管理,状态变更自动同步
​代码可维护性​模板代码多,修改易引发连锁问题声明式语法简洁,逻辑与UI分离,修改成本低
​交互流畅性​列表/表单更新需手动优化性能框架自动优化渲染流程,界面响应更流畅
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐