【引言】

本文将介绍一个名为“颜文字搜索器”的开发案例,该应用是基于鸿蒙NEXT平台构建的,旨在帮助用户快速查找和使用各种风格的表情符号。通过本案例的学习,读者可以了解如何在鸿蒙平台上进行数据处理、UI设计以及交互逻辑的实现。

【环境准备】

• 操作系统:Windows 10

• 开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806

• 目标设备:华为Mate60 Pro

• 开发语言:ArkTS

• 框架:ArkUI

• API版本:API 12

【开发思路】

1. 数据模型设计

为了表示单个表情符号的信息,我们定义了一个 EmoticonBean 类,它包含了表情符号的风格(style)、类型(type)、表情符号本身(emoticon)及其含义(meaning)。此外,还添加了一个布尔属性 isShown 来追踪表情符号是否应该显示给用户,这有助于在搜索时动态更新列表。

2. UI 组件与布局

应用的主界面由一个 Index 组件构成,它负责整体布局的设计。界面上部是一个搜索框,允许用户输入关键词来过滤表情符号列表。下方则以表格形式展示了所有符合条件的表情符号,每一行包括四个部分:风格、类型、表情符号和含义。为了提升用户体验,当用户点击某个表情符号或其含义中的高亮文本时,会触发相应的点击事件,并输出日志信息。

3. 数据加载与处理

表情符号的数据来源于一个本地 JSON 文件 (emoticons.json),该文件在组件初次渲染之前被读取并解析为 EmoticonBean 对象数组。每次用户修改搜索框中的内容时,都会调用 splitAndHighlight 方法对每个表情符号的含义进行分割,并检查是否存在匹配的关键字。如果存在,则设置 isShown 属性为 true,否则为 false,以此控制表情符号是否在界面上显示。

4. 搜索与高亮

splitAndHighlight 函数用于将表情符号的含义按关键字分割成多个片段,并返回这些片段组成的数组。对于包含关键字的片段,会在界面上以不同的颜色高亮显示,从而直观地指出匹配的部分。此外,此函数还会根据是否有匹配项来决定表情符号是否可见,确保只有相关的表情符号才会展示给用户。

5. 用户交互

为了让用户有更好的操作体验,我们在界面上实现了触摸事件监听,当用户点击非输入区域时,自动关闭键盘。这样既保证了界面整洁,又简化了用户的操作流程。

【完整代码】

数据源:src/main/resources/rawfile/emoticons.json

https://download.csdn.net/download/zhongcongxu01/90126325

代码

// 引入必要的工具库 util 用于文本解码等操作
import { util } from '@kit.ArkTS'
// 引入 BusinessError 类用于处理业务逻辑错误
import { BusinessError } from '@kit.BasicServicesKit'
// 引入 inputMethod 模块用于管理输入法行为
import { inputMethod } from '@kit.IMEKit' // 定义一个可以被观察的数据模型 EmoticonBean 表示单个表情符号的信息
@ObservedV2
class EmoticonBean {
// 定义风格属性,并初始化为空字符串
style: string = ""
// 定义类型属性,并初始化为空字符串
type: string = ""
// 定义表情符号本身,并初始化为空字符串
emoticon: string = ""
// 定义含义属性,并初始化为空字符串
meaning: string = "" // 构造函数,允许在创建对象时设置上述属性
constructor(style: string, type: string, emoticon: string, meaning: string) {
this.style = style
this.type = type
this.emoticon = emoticon
this.meaning = meaning
} // 定义是否显示的表情符号状态标记,默认为 true,使用 @Trace 装饰器使其可追踪变化
@Trace isShown: boolean = true
} // 使用 @Entry 和 @Component 装饰器定义 Index 组件作为应用入口
@Entry
@Component
struct Index {
// 定义一个状态变量 textInput 用于存储搜索框中的文本内容,默认为空字符串
@State private textInput: string = ''
// 定义一个状态变量 emoticonList 用于存储表情符号列表,默认为空数组
@State private emoticonList: EmoticonBean[] = [] // 定义线条颜色属性
private lineColor: string = "#e6e6e6"
// 定义标题背景色属性
private titleBackground: string = "#f8f8f8"
// 定义文本颜色属性
private textColor: string = "#333333"
// 定义基础填充大小
private basePadding: number = 4
// 定义线条宽度
private lineWidth: number = 2
// 定义单元格高度
private cellHeight: number = 50
// 定义列权重比例
private weightRatio: number[] = [1, 1, 5, 4]
// 定义基础字体大小
private baseFontSize: number = 14 // 定义一个方法 splitAndHighlight 用于分割并高亮表情符号含义中的关键词
private splitAndHighlight(item: EmoticonBean, keyword: string): string[] {
let text = item.meaning // 获取表情符号的含义文本
if (!keyword) { // 如果没有关键词,则直接返回整个文本,并显示该表情符号
item.isShown = true
return [text]
}
let segments: string[] = []; // 用于存储分割后的文本片段
let lastMatchEnd: number = 0; // 记录上一次匹配结束的位置
while (true) { // 循环查找关键词在文本中的位置
const matchIndex = text.indexOf(keyword, lastMatchEnd); // 查找关键词出现的位置
if (matchIndex === -1) { // 如果找不到关键词,将剩余文本加入到segments中并退出循环
segments.push(text.slice(lastMatchEnd));
break;
} else { // 如果找到关键词,将非关键词部分和关键词部分分别加入到segments中
segments.push(text.slice(lastMatchEnd, matchIndex)); // 非关键词部分
segments.push(text.slice(matchIndex, matchIndex + keyword.length)); // 关键词部分
lastMatchEnd = matchIndex + keyword.length;
}
}
// 如果有关键词出现,则设置表情符号为显示状态
item.isShown = (segments.indexOf(keyword) != -1)
return segments;
} // 当组件即将出现在屏幕上时调用此方法,用于加载表情符号数据
aboutToAppear() {
// 从资源管理器中读取本地文件 emoticons.json 的内容
getContext().resourceManager.getRawFileContent("emoticons.json", (err: BusinessError, data) => {
if (err) { // 如果读取失败,打印错误信息
console.error('getRawFileContent error: ' + JSON.stringify(err))
return
}
// 创建一个文本解码器来将二进制数据转换为字符串
let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })
let jsonString = textDecoder.decodeToString(data, { stream: false })
let jsonObjectArray: object[] = JSON.parse(jsonString) // 将 JSON 字符串解析为对象数组
for (let i = 0; i < jsonObjectArray.length; i++) { // 遍历对象数组,填充 emoticonList
let item = jsonObjectArray[i]
this.emoticonList.push(new EmoticonBean(item['s'], item['t'], item['e'], item['m']))
}
try {
// 打印 emoticonList 到控制台以供调试
console.info(`this.emoticonList:${JSON.stringify(this.emoticonList, null, '\u00a0\u00a0')}`)
} catch (err) {
console.error('parse error: ' + JSON.stringify(err))
}
})
} // 定义 build 方法构建组件的UI结构
build() {
Column({ space: 0 }) { // 创建一个列容器,内部元素之间没有间距
// 搜索框组件,绑定到 textInput 状态变量
Search({ value: $$this.textInput })
.margin(this.basePadding) // 设置外边距
.fontFeature("\"ss01\" on") // 设置字体特征
// 创建一个列容器用于表头
Column() {
Row() { // 创建一行用于放置表头
// 表头文字:风格
Text('风格')
.height('100%') // 设置高度为父容器的100%
.layoutWeight(this.weightRatio[0]) // 根据权重分配宽度
.textAlign(TextAlign.Center) // 文本居中对齐
.fontSize(this.baseFontSize) // 设置字体大小
.fontWeight(600) // 设置字体粗细
.fontColor(this.textColor) // 设置文本颜色
// 分割线
Line().height('100%').width(this.lineWidth).backgroundColor(this.lineColor)
// 表头文字:类型
Text('类型')
.height('100%')
.layoutWeight(this.weightRatio[1])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontWeight(600)
.fontColor(this.textColor)
// 分割线
Line().height('100%').width(this.lineWidth).backgroundColor(this.lineColor)
// 表头文字:表情
Text('表情')
.height('100%')
.layoutWeight(this.weightRatio[2])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontWeight(600)
.fontColor(this.textColor)
// 分割线
Line().height('100%').width(this.lineWidth).backgroundColor(this.lineColor)
// 表头文字:含义
Text('含义')
.height('100%')
.layoutWeight(this.weightRatio[3])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontWeight(600)
.fontColor(this.textColor)
}.height(this.cellHeight).borderWidth(this.lineWidth).borderColor(this.lineColor)
.backgroundColor(this.titleBackground) // 设置背景颜色
}.width(`100%`).padding({ left: this.basePadding, right: this.basePadding }) // 创建一个滚动容器 Scroll 包含表情符号列表
Scroll() {
Column() {
// ForEach 循环遍历 emoticonList 数组,创建每一行代表一个表情符号条目
ForEach(this.emoticonList, (item: EmoticonBean) => {
Row() {
// 显示表情符号的风格
Text(item.style)
.height('100%')
.layoutWeight(this.weightRatio[0])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontColor(this.textColor)
// 分割线
Line().height('100%').width(this.lineWidth).backgroundColor(this.lineColor)
// 显示表情符号的类型
Text(item.type)
.height('100%')
.layoutWeight(this.weightRatio[1])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontColor(this.textColor)
// 分割线
Line().height('100%').width(this.lineWidth).backgroundColor(this.lineColor)
// 显示表情符号
Text(item.emoticon)
.height('100%')
.layoutWeight(this.weightRatio[2])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontColor(this.textColor)
.copyOption(CopyOptions.LocalDevice) // 允许复制到剪贴板
// 分割线
Line().height('100%').width(this.lineWidth).backgroundColor(this.lineColor)
// 显示表情符号的含义,支持关键字高亮
Text() {
ForEach(this.splitAndHighlight(item, this.textInput), (segment: string, index: number) => {
ContainerSpan() {
Span(segment)
.fontColor(segment === this.textInput ? Color.White : Color.Black) // 根据是否是关键词设置字体颜色
.onClick(() => { // 设置点击事件监听器
console.info(`Highlighted text clicked: ${segment}`); // 打印点击的文本信息
console.info(`Click index: ${index}`); // 打印点击的索引信息
});
}.textBackgroundStyle({
color: segment === this.textInput ? Color.Red : Color.Transparent // 根据是否是关键词设置背景颜色
});
});
}
.height('100%')
.layoutWeight(this.weightRatio[3])
.textAlign(TextAlign.Center)
.fontSize(this.baseFontSize)
.fontColor(this.textColor)
.padding({ left: this.basePadding, right: this.basePadding })
}
.height(this.cellHeight)
.borderWidth({ left: this.lineWidth, right: this.lineWidth, bottom: this.lineWidth })
.borderColor(this.lineColor)
// 根据表情符号的状态(是否显示)来决定其可见性
.visibility(item.isShown ? Visibility.Visible : Visibility.None)
})
}.width(`100%`).padding({ left: this.basePadding, right: this.basePadding })
}.width('100%').layoutWeight(1).align(Alignment.Top)
// 触摸事件处理,当用户点击空白区域时,关闭键盘输入
.onTouch((event) => {
if (event.type == TouchType.Down) { // 如果是按下事件
inputMethod.getController().stopInputSession() // 停止当前的输入会话
}
})
}.width('100%').height('100%').backgroundColor(Color.White); // 设置容器的宽高和背景颜色
}
}

  

鸿蒙NEXT开发案例:颜文字搜索器的更多相关文章

  1. SpringBoot开发案例之拦截器注入Bean

    前言 由于业务需要,需要在拦截器中操作Redis缓存,按照 controller,service层配置发现无法注入,一直报空指针异常. 解决方案 @Configuration public class ...

  2. 最全华为鸿蒙 HarmonyOS 开发资料汇总

    开发 本示例基于 OpenHarmony 下的 JavaScript UI 框架,进行项目目录解读,JS FA.常用和自定义组件.用户交互.JS 动画的实现,通过本示例可以基本了解和学习到 JavaS ...

  3. 《实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)》

    <实战突击:PHP项目开发案例整合(第2版)(含DVD光盘1张)> 基本信息 作者: 徐康明    辛洪郁 出版社:电子工业出版社 ISBN:9787121221378 上架时间:2014 ...

  4. [源代码] - C#代码搜索器 - 续

    在前文 [源代码] - C#代码搜索器 中我开发了一个代码搜索器. 我对其做的最后改动是将索引保存到磁盘中, 以备今后使用. 如今, 我在工作中又接到一项新任务: 有一个大项目, 其中10个负责数据访 ...

  5. 百度UEditor开发案例(JSP)

    本案例的开发环境:MyEclipse+tomcat+jdk     本案例的开发内容: 用百度编辑器发布新闻(UEditor的初始化开发部署) 编辑已发过的新闻(UEditor的应用——编辑旧文章) ...

  6. FocusBI: SSIS 开发案例(原创)

    关注微信公众号:FocusBI 查看更多文章:加QQ群:808774277 获取学习资料和一起探讨问题. <商业智能教程>pdf下载地址 链接:https://pan.baidu.com/ ...

  7. JAVA WEB项目开发案例精粹

    http://www.blogjava.net/zongbao/archive/2012/07/24/383884.htmlJAVA WEB项目开发案例精粹.pdf main Alt + / => ...

  8. CozyRSS开发记录0-RSS阅读器开坑

    CozyRSS开发记录0-RSS阅读器开坑 1.RSS RSS,全名是Really Simple Syndication,简易信息聚合. 关于RSS相关的介绍,网上可以很容易的找到.RSS阅读器是我几 ...

  9. 超级文件搜索器(SuperSearch)

    写了个工具,各个分享渠道上传太难了,在这里分享给大家吧! 中文名称:超级文件搜索器 英文名称:  SuperSearch 适用对象:硬盘里文件达到数万至数千万级的用户 运行环境:WindowXP,Wi ...

  10. 使用Jquery+EasyUI 进行框架项目开发案例讲解之五 模块(菜单)管理源码分享

    http://www.cnblogs.com/huyong/p/3454012.html 使用Jquery+EasyUI 进行框架项目开发案例讲解之五  模块(菜单)管理源码分享    在上四篇文章 ...

随机推荐

  1. 浏览器原生组件 shadow DOM(已完成)

    https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_shadow_DOM Web components 的一个重要属性是 ...

  2. SpringMVC —— SpringMVC简介

    SpringMVC SpringMVC技术 与 Servlet技术功能等同,均属于web层开发技术 是一种基于java实现MVC模型的轻量级Web框架 SpringMVC 入门案例          ...

  3. Filter——过滤器

    Filter       Filter 快速入门    Filter 执行流程           1.放行前,对 request 数据进行处理     2.放行后,对 response 数据进行处理 ...

  4. Linux操作系统和文件系统、常见命令(下)

    C语言的绝大部分内容应该记录在以.c作为拓展名的文件里,这种文件叫做C语言的源文件 C语言程序里还包括以.h作为拓展名的文件,这种文件叫头文件(只有极少数的内容可以记录在头文件里) C语言程序里可以使 ...

  5. 简单上手 Vue Router

    Vue Router 也随着 Vue3 的更新来到了 4 版本,来看一下怎么使用吧!(这里使用的是 composition API 和 TypeScript 模式) 安装 vue-router4 np ...

  6. wpf之样式

    在Window.Resources中书写样式 : <Window.Resources> <Style TargetType="Button" > </ ...

  7. 前端 vue.config.js 处理跨域问题 proxy 代理

    问: 业务中的跨域问题是如何解决的? 这个的话我们公司⼤概分了俩种环境,⼀种是开发的时候,⼀种是上⽣产的时候,开发的时候因为要对 接的后端可能会⽐较多,他们配置不太⽅便,这个时候采取的是⽐较⽅便的前端 ...

  8. NOI 2024

    Day1 T1 集合(set) 容易发现两个序列等价当且仅当,所有数字在序列中出现位置的集合构成集族相等. 考虑哈希,对于一个集合 \(S\),令它的哈希值为 \(f(S) = (\sum\limit ...

  9. .NET 7+Vue 3 开源仓库管理系统 ModernWMS

    前言 本系统的设计目标是帮助中小企业乃至大型企业实现仓库操作的自动化与数字化,从而提升工作效率,降低成本,并最终实现业务增长.项目采用 Vue 3 + TS + .NET 7 等前沿框架进行开发,为企 ...

  10. Nuxt.js 应用中的 server:devHandler 事件钩子详解

    title: Nuxt.js 应用中的 server:devHandler 事件钩子详解 date: 2024/10/26 updated: 2024/10/26 author: cmdragon e ...