构建多种样式的弹窗(案例)

介绍

img

本篇Codelab将介绍如何使用弹窗功能,实现四种类型弹窗。分别是:警告弹窗、自定义弹窗、日期滑动选择器弹窗、文本滑动选择器弹窗。需要完成以下功能:

  1. 点击左上角返回按钮展示警告弹窗。
  2. 点击出生日期展示日期滑动选择器弹窗。
  3. 点击性别展示文本滑动选择器弹窗。
  4. 点击兴趣爱好(多选)展示自定义弹窗。

相关概念

完整示例

gitee源码地址

源码下载

构建多种样式弹窗(ArkTS).zip

代码结构解读

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在源码下载或gitee中提供。

├──entry/src/main/ets             // 代码区 
│  ├──common
│  │  ├──constants
│  │  │  └──CommonConstants.ets   // 常量类
│  │  └──utils
│  │     ├──CommonUtils.ets       // 弹窗操作工具类
│  │     └──Logger.ets            // 日志打印工具类
│  ├──entryability
│  │  └──EntryAbility.ets         // 程序入口类
│  ├──pages
│  │  └──HomePage.ets             // 主页面
│  ├──view
│  │  ├──CustomDialogWidget.ets   // 自定义弹窗组件
│  │  ├──TextCommonWidget.ets     // 自定义Text组件
│  │  └──TextInputWidget.ets      // 自定义TextInput组件
│  └──viewmodel
│     └──HobbyItem.ets            // 兴趣爱好类
└──entry/src/main/resources       // 资源文件目录

构建主页面

应用主页面采用Column容器嵌套自定义组件形式完成页面整体布局,效果如图所示:

img

从上面效果图可以看出,主界面由2个相同样式的文本输入框和3个相同样式的文本布局组成。我们可以将文本输入框抽取成TextInputWidget子组件。再将文本布局抽取成TextCommonWidget子组件。

在ets目录下,点击鼠标右键 > New > Directory,新建名为view的自定义子组件目录。然后在view目录下,点击鼠标右键 > New > ArkTS File,新建两个ArkTS文件,分别为TextInputWidget子组件、TextCommonWidget子组件。

文本输入框抽取成TextInputWidget子组件,效果如图所示:

img

// TextInputWidget.ets
@Component
export default struct TextInputWidget {
  // 文本框左侧图片
  private inputImage?: Resource; 
  // 文本框提示
  private hintText?: Resource;

  build() {
    Row() {
      Image(this.inputImage !== undefined ? this.inputImage : '')
        ...
      TextInput({ placeholder: this.hintText })
        ...
    }
    ...
  }
}

文本布局抽取成TextCommonWidget子组件,效果如图所示:

img

// TextCommonWidget.ets
@Component
export default struct TextCommonWidget {
  // 显示内容
  @Link content: string;
  // 文字标题左侧图片
  private textImage?: Resource;
  // 文本标题
  private title?: Resource;
  // 点击事件回调
  onItemClick = () => {};

  build() {
    Row() {
      Image(this.textImage !== undefined ? this.textImage : '')
        ...
      Text(this.title)
        ...
      Text(this.content)
        ...
      Image($r('app.media.ic_arrow'))
        ...
    }
    .onClick(this.onItemClick)
    ...
  } 
}

在HomePage主界面引用TextInputWidget和TextCommonWidget子组件,然后初始化出生日期、性别、兴趣爱好默认数据。

// HomePage.ets
@Entry
@Component
struct HomePage {
  @State birthdate: string = '';
  @State sex: string = '';
  @State hobbies: string = '';
  ...

  build() {
    Column() {
      ...
      TextInputWidget({
        inputImage: $r('app.media.ic_nickname'),
        hintText: $r('app.string.text_input_hint')
      })
      TextCommonWidget({
        textImage: $r('app.media.ic_birthdate'),
        title: $r('app.string.title_birthdate'),
        content: $birthdate,
        onItemClick: () => {
          CommonUtils.datePickerDialog((birthValue: string) => {
            this.birthdate = birthValue;
          });
        }
      })
      TextCommonWidget({
        textImage: $r('app.media.ic_sex'),
        title: $r('app.string.title_sex'),
        content: $sex,
        onItemClick: () => {
          CommonUtils.textPickerDialog(this.sexArray, (sexValue: string) => {
            this.sex = sexValue;
          });
        }
      })
      TextInputWidget({
        inputImage: $r('app.media.ic_signature'),
        hintText: $r('app.string.text_input_signature')
      })
      TextCommonWidget({
        textImage: $r('app.media.ic_hobbies'),
        title: $r('app.string.title_hobbies'),
        content: $hobbies,
        onItemClick: () => {
          this.customDialogController.open();
        }
      })
    }
    ...
  }
}

警告弹窗

点击主页面左上角返回按钮,通过CommonUtils.alertDialog方法弹出警告弹窗,提醒用户是否进行当前操作,效果如图所示:

img

// CommonUtils.ets
alertDialog(context: Context.UIAbilityContext) {
  AlertDialog.show({
    // 提示信息
    message: $r('app.string.alert_dialog_message'), 
    // 弹窗显示位置
    alignment: DialogAlignment.Bottom,
    // 弹窗偏移位置
    offset: {
      dx: 0,
      dy: CommonConstants.DY_OFFSET
    },
    primaryButton: {
      value: $r('app.string.cancel_button'),
      action: () => {
        ...
      }
    },
    secondaryButton: {
      value: $r('app.string.definite_button'),
      action: () => {
        // 退出应用
        context.terminateSelf();
        ...
      }
    }
  });
}

日期滑动选择器弹窗

点击出生日期选项,通过CommonUtils.datePickerDialog方法弹出日期选择器弹窗,根据需要选择相应时间,效果如图所示:

img

// CommonUtils.ets
datePickerDialog(dateCallback: Function) {
  DatePickerDialog.show({
    // 开始时间
    start: new Date(CommonConstants.START_TIME),
    // 结束时间
    end: new Date(), 
    // 当前选中时间
    selected: new Date(CommonConstants.SELECT_TIME),
    // 是否显示农历
    lunar: false,
    onAccept: (value: DatePickerResult) => {
      let year: number = Number(value.year);
      let month: number = Number(value.month) + CommonConstants.PLUS_ONE;
      let day: number = Number(value.day);
      let birthdate: string = this.getBirthDateValue(year, month, day);
      dateCallback(birthdate);
    }
  });
}

// 获取出生日期值
getBirthDateValue(year: number, month: number, day: number): string {
  let birthdate: string = `${year}${CommonConstants.DATE_YEAR}${month}` +
    `${CommonConstants.DATE_MONTH}${day}${CommonConstants.DATE_DAY}`;
  return birthdate;
}

// HomePage.ets
build() {
  Column() {
    ...
    TextCommonWidget({
      textImage: $r('app.media.ic_birthdate'),
      title: $r('app.string.title_birthdate'),
      content: $birthdate,
      onItemClick: () => {
        CommonUtils.datePickerDialog((birthValue: string) => {
          this.birthdate = birthValue;
        });
      }
    })
    ...
  }
  ...
}

文本滑动选择器弹窗

点击性别选项,通过CommonUtils.textPickerDialog方法弹出性别选择器弹窗,根据需要选择相应性别,效果如图所示:

img

// CommonUtils.ets
textPickerDialog(sexArray: Resource, sexCallback: Function) {
  ...
  TextPickerDialog.show({
    range: sexArray,
    selected: 0,
    onAccept: (result: TextPickerResult) => {
      sexCallback(result.value);
    },
    onCancel: () => {
      ...
    }
  });
}

// HomePage.ets
build() {
  Column() {
    ...
    TextCommonWidget({
      textImage: $r('app.media.ic_sex'),
      title: $r('app.string.title_sex'),
      content: $sex,
      onItemClick: () => {
        CommonUtils.textPickerDialog(this.sexArray, (sexValue: string) => {
          this.sex = sexValue;
        });
      }
    })
    ...
  }
  ...
}

自定义弹窗

点击兴趣爱好选项,通过customDialogController.open方法弹出自定义弹窗,根据需要选择相应的兴趣爱好,效果如图所示:

img

在view目录下,点击鼠标右键 > New > ArkTS File,新建一个ArkTS文件,然后命名为CustomDialogWidget子组件。

在CustomDialogWidget的aboutToAppear方法,通过manager.getStringArrayValue方法获取本地资源数据进行初始化。

// CustomDialogWidget.ets
@State hobbyItems: HobbyItem[] = [];
...
aboutToAppear() {
  let context: Context = getContext(this);
  if (CommonUtils.isEmpty(context) || CommonUtils.isEmpty(context.resourceManager)) {
    Logger.error(CommonConstants.TAG_CUSTOM, 'context or resourceManager is null');
    return;
  }
  let manager = context.resourceManager;
  manager.getStringArrayValue($r('app.strarray.hobbies_data').id, (error, hobbyArray) => {
    if (!CommonUtils.isEmpty(error)) {
      Logger.error(CommonConstants.TAG_CUSTOM, 'error = ' + JSON.stringify(error));
    } else {
      hobbyArray.forEach((hobby: string) => {
        let hobbyItem = new HobbyItem();
        hobbyItem.label = hobby;
        hobbyItem.isChecked = false;
        this.hobbyItems.push(hobbyItem);
      });
    }
  });
}

当用户点击确定按钮时,通过setHobbiesValue方法处理自定义弹窗选项结果。

// CustomDialogWidget.ets
@State hobbyItems: HobbyItem[] = [];
@Link hobbies: string;
private controller?: CustomDialogController;

// 处理自定义弹窗选项结果
setHobbiesValue(hobbyItems: HobbyItem[]) {
  if (CommonUtils.isEmptyArr(hobbyItems)) {
    Logger.error(CommonConstants.TAG_HOME, 'hobbyItems length is 0');
    return;
  }
  let hobbiesText: string = '';
  hobbiesText = hobbyItems.filter((isCheckItem: HobbyItem) => isCheckItem?.isChecked)
    .map<string>((checkedItem: HobbyItem) => {
      return checkedItem.label!;
    })
    .join(CommonConstants.COMMA);
  if (hobbiesText.length > 0) {
    this.hobbies = hobbiesText;
  }
}

build() {
  Column() {
    ...
    Row() {
      Button($r('app.string.cancel_button'))
        .dialogButtonStyle()
        .onClick(() => {
          this.controller?.close();
        })
      Blank()
        ...
      Button($r('app.string.definite_button'))
        .dialogButtonStyle()
        .onClick(() => {
          this.setHobbiesValue(this.hobbyItems);
          this.controller?.close();
        })
    }
  }
  ...
}

@Extend(Button) function dialogButtonStyle() {
  ....
}

通过@Link修饰的hobbies把值赋给HomePage的hobbies,然后hobbies刷新显示内容。

// HomePage.ets
@State hobbies: string = '';
customDialogController: CustomDialogController = new CustomDialogController({
  builder: CustomDialogWidget({
    hobbies: $hobbies
  }),
  alignment: DialogAlignment.Bottom,
  customStyle: true,
  offset: {
    dx: 0,
    dy: CommonConstants.DY_OFFSET
  }
});

build() {
  Column() {
    ...
    TextCommonWidget({
      textImage: $r('app.media.ic_hobbies'),
      title: $r('app.string.title_hobbies'),
      content: $hobbies,
      onItemClick: () => {
        // 打开自定义弹窗
        this.customDialogController.open();
      }
    })
  }
  ...
}

总结

您已经完成了本次Codelab的学习,并了解到以下知识点:

  1. 使用CustomDialogController实现自定义弹窗。
  2. 使用AlertDialog实现警告弹窗。
  3. 使用DatePickerDialog实现日期滑动选择弹窗。
  4. 使用TextPickerDialog实现文本滑动选择弹窗。
  • 26
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一起学鸿蒙呀~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值