鸿蒙HarmonyOS NEXT开发实战往期文章必看(持续更新......)
最新版!"非常详细的" 鸿蒙HarmonyOS Next应用开发学习路线!(从零基础入门到精通)
非常详细的" 鸿蒙HarmonyOS Next应用开发学习路线!
介绍
本示例主要介绍如何在聊天信息中加入表情图片。通过变量控制表情键盘的显示与否,使用RichEdit接收所选表情的ImageSpan。在发送信息时将图片和文字消息分别通过ImageSpan、Span加入到消息列表中,显示的时候将消息列表中的ImageSpan、Span包裹在Text中进行显示,同时实现了最近使用表情和长按表情图片弹窗显示表情明细的效果。
效果图预览
使用说明
- 进入页面,在底部输入框中输入文字,点击输入框右边的表情图片按钮弹出表情键盘,长按表情图片弹窗显示表情明细。在表情键盘中选择表情图片后点击发送,在聊天对话框中会显示聊天文字以及表情。
实现思路
-
将实现了Popup效果的单个表情功能封装成EmojiDetail自定义组件。选中表情图片后,将表情通过imageSpan的方式加到RichEditor输入框中,同时加入到最近使用的表情数据列表中。源码参考EmojiDetail.ets
Image(this.EmojiItem!.imgSrc) .width(FaceGridConstants.EMOJI_IMAGE_SIZE) .height(FaceGridConstants.EMOJI_IMAGE_SIZE) .onClick(() => { // TODO 知识点:将表情添加到输入框中 this.controllerRich!.addImageSpan(this.EmojiItem!.imgSrc, { imageStyle: { size: [this.msgFontSize / 3.2, this.msgFontSize / 3.2], // 3.2 调整表情在输入框中的尺寸 verticalAlign: ImageSpanAlignment.CENTER } }); // TODO 知识点:将表情添加到最近使用表情中 this.lastEmojiData.pushData(this.EmojiItem!); }) .draggable(false) .gesture( // TODO 知识点:长按弹出表情明细 LongPressGesture() .onAction(() => { this.isPopup = true; }) .onActionEnd(() => { this.isPopup = false; }) ) .bindPopup(this.isPopup, { // TODO 知识点:使用bindPopup弹出表情明细 builder: this.popupBuilder, onStateChange: (e) => { if (!e.isVisible) { this.isPopup = false; } } })
-
使用Grid和EmojiDetail自定义组件创建表情键盘,源码参考EmojiKeyboard.ets
Grid() { // TODO: 性能知识点:使用ForEach组件循环渲染数据 ForEach(this.emojiList, (item: EmojiModel) => { GridItem() { // 表情明细组件 EmojiDetail({ controllerRich: this.controllerRich, EmojiItem: item, msgFontSize: this.msgFontSize, lastEmojiData: this.lastEmojiData }) } }) } .maxCount(FaceGridConstants.GRID_MAX_COUNT) .columnsTemplate("1fr 1fr 1fr 1fr 1fr 1fr") // 分成6份 .rowsGap(FaceGridConstants.ROWS_GAP)
-
点击表情按钮时弹出最近表情和全部表情键盘,源码参考ChatWithExpression.ets
// TODO 知识点:通过变量控制表情键盘的显示 if (this.isFaceDlgOpen) { Column() { // 最近使用的表情 if (this.lastEmojiData.totalCount() > 0) { Text($r('app.string.chat_with_expression_last_emoji')).alignSelf(ItemAlign.Start) .margin($r('app.integer.chat_with_expression_chat_margin_top')) // 表情键盘组件 Emojikeyboard({ controllerRich: this.controllerRich, msgFontSize: this.msgFontSize, lastEmojiData: this.lastEmojiData, emojiList: this.lastEmojiData.emojiList }) } // 全部表情 Text($r('app.string.chat_with_expression_all_emoji')).alignSelf(ItemAlign.Start) .margin($r('app.integer.chat_with_expression_chat_margin_top')) // 表情键盘组件 Emojikeyboard({ controllerRich: this.controllerRich, msgFontSize: this.msgFontSize, lastEmojiData: this.lastEmojiData, emojiList: EmojiData }) } }
-
点击发送时,通过RichEditorController的getSpans方法,将聊天信息中ImageSpan、Span分别push到要发送的信息的spanItems中。源码参考ChatWithExpression.ets的sendChatMsg方法
let msgBase = new MessageBase(true, USER_NAME_MYSELF, HEAD_IMAGE_MYSELF, this.msgMaxWidth); // 获取发送信息 this.controllerRich.getSpans({ start: this.start, end: this.end }).forEach(item => { if (typeof (item as RichEditorImageSpanResult)['imageStyle'] !== 'undefined') { // TODO 知识点:处理imagespan信息 const imageMsg: ResourceStr | undefined = (item as RichEditorImageSpanResult).valueResourceStr; if (imageMsg !== undefined) { const spanItem: SpanItem = new SpanItem(SpanType.IMAGE, '', imageMsg.toString().substring(EMOJI_SRC_POS)); msgBase.spanItems.push(spanItem); } } else { // TODO 知识点:处理文字span信息 const textMsg: string = (item as RichEditorTextSpanResult).value; const spanItem: SpanItem = new SpanItem(SpanType.TEXT, textMsg, ''); msgBase.spanItems.push(spanItem); } }) logger.info(TAG, 'sendChatMsg spanItems:' + msgBase.spanItems.length.toString()); // 发送 if (msgBase.spanItems.length !== 0) { this.textDetailData.pushData(msgBase); this.msgNums = this.textDetailData.totalCount(); this.scroller.scrollToIndex(this.msgNums - 1); this.controllerRich.deleteSpans(); this.controllerRich.setCaretOffset(-1); } this.customFaceDialogCtl.close(); this.isFaceDlgOpen = false; this.marginBottomInput = 0; focusControl.requestFocus(this.focusKey);
-
在聊天对话框中通过LazyForEach循环加载聊天信息。
// 聊天对话框 List({ scroller: this.scroller, initialIndex: this.msgNums - 1 }) { // 性能知识点:使用懒加载组件渲染数据。参考资料:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-rendering-control-lazyforeach-0000001820879609 LazyForEach(this.textDetailData, (msg: MessageBase) => { ListItem() { if (msg.isSelf) { MessageItemSelfView({ msg: msg }); } else { MessageItemView({ msg: msg }); } } }) }
-
将聊天信息的SpanItems根据spanType在Text中分别包裹为ImageSpan跟Span。
// 聊天信息 Row() { Text(undefined) { // TODO: 性能知识点:使用ForEach组件循环渲染数据 ForEach(this.msg.spanItems, (item: SpanItem) => { // TODO 知识点:分别使用ImageSpan、Span渲染图片、文字信息 if (item.spanType === SpanType.IMAGE) { ImageSpan($rawfile(item.imgSrc as string)) .width($r('app.integer.chat_font_size')) .height($r('app.integer.chat_font_size')) .verticalAlign(ImageSpanAlignment.BOTTOM).objectFit(ImageFit.Cover) } else if (item.spanType === SpanType.TEXT) { Span(item.text) } }) }.constraintSize({ minHeight: $r('app.integer.chat_inline_height'), maxWidth: this.msg.maxWidth }) .textAlign(TextAlign.Start) }
高性能知识点
本示例使用了LazyForEach进行数据懒加载,同时搭配组件复用能力以达到性能最优效果。
工程结构&模块类型
chatwithexpression // har类型
|---view
| |---ChatWithExpression.ets // 视图层-表情聊天界面
|---constants
| |---ChatConstants.ets // 常量
|---model
| |---Emoji.ets // 表情资源
| |---Message.ets // 消息结构
| |---BasicDataSource.ets // 数据类型文件
最后
小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为体系杂乱无章,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)路线、视频、文档用来跟着学习是非常有必要的。
如果你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感兴趣以及转行人员
鸿蒙 NEXT 全栈开发学习笔记希望这一份鸿蒙学习文档能够给大家带来帮助~
这份鸿蒙(HarmonyOS NEXT)包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、(南向驱动、嵌入式等)鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
鸿蒙(HarmonyOS NEXT)最新学习路线
该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案
路线图适合人群:
IT开发人员: 想要拓展职业边界
零基础小白: 鸿蒙爱好者,希望从0到1学习,增加一项技能。
**技术提升/进阶跳槽:**发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术
2.视频教程+学习PDF文档
(鸿蒙语法ArkTS、TypeScript、ArkUI教程......) 全球开发者的开源社区,开源代码
纯血版鸿蒙全套学习文档(面试、文档、全套视频等) 全球开发者的开源社区,开源代码
鸿蒙APP开发必备
总结
参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线