HarmonyOS开发场景案例(一)——文本高亮搜索示例

305 阅读4分钟

文本高亮搜索示例

功能介绍

1. 功能概述

本示例展示了一个搜索组件的实现,包含以下核心功能:

  • 实时搜索建议:根据用户输入实时过滤并展示搜索建议
  • 关键词高亮:在搜索建议中高亮显示匹配的关键词部分
  • 搜索历史:记录用户的搜索历史,支持快速重新搜索

2. 效果展示

效果.gif

实现原理

1. 整体架构

graph LR
    A[用户输入] --> B[状态管理<br>AppStorageV2]
    B --> C[数据处理]
    C --> D[UI渲染]
    D --> E[结果点击]
    E --> F[搜索记录更新]
    F --> B
    B --> G[历史记录存储]
    G --> B

整体采用单向数据流设计:

  1. 用户输入触发搜索状态更新
  2. 状态变化驱动搜索建议过滤
  3. 数据处理层处理高亮显示
  4. UI层响应状态变化并更新显示

2. 核心功能实现原理

文本高亮处理流程
graph LR
    A["原始文本<br>Hello"] --> B["大小写转换<br>hello"]
    B --> C["位置匹配<br>(0, 2)"]
    C --> D["文本分割<br>He,l,lo"]
    D --> E["高亮渲染<br>He[l]lo"]

工作原理:

  1. 将原文本和关键词转换为小写进行匹配
  2. 记录所有匹配位置
  3. 根据匹配位置分割文本
  4. 对匹配部分应用高亮样式
状态管理流程
graph LR
    A["状态定义<br>SearchState"] --> B["状态更新<br>ObservedV2"]
    B --> C["持久化<br>AppStorageV2"]
    C --> D["UI响应"]
    D --> E["用户交互"]
    E --> F["本地存储"]
    F --> C
    F --> G["历史记录读取"]
    G --> A

数据流转:

  1. 统一的状态管理类
  2. 响应式状态更新
  3. 自动持久化存储
  4. UI组件自动响应变化

开发步骤

1. 定义数据模型

首先定义核心数据结构,确保类型安全和代码可维护性:

// 搜索建议项
interface SearchSuggestion {
  id: string        // 唯一标识
  keyword: string   // 搜索建议文本
  tags: string[]    // 标签列表
}

// 高亮文本片段
interface HighlightPart {
  text: string      // 文本内容
  isHighlight: boolean  // 是否高亮
}

2. 实现高亮文本组件

首先实现文本高亮的核心组件,这是整个搜索功能的基础:

// src/main/ets/components/Common/HighlightText.ets
@ComponentV2
export struct HighlightText {
  @Require @Param content: string    // 原始文本
  @Param keyword: string = ''        // 搜索关键词

  private getHighlightParts(): Array<HighlightPart> {
    // 1. 空关键词处理
    if (!this.keyword) {
      return [{ text: this.content, isHighlight: false }]
    }
    
    // 2. 初始化变量
    const parts: Array<HighlightPart> = []
    const lowerText = this.content.toLowerCase()
    const lowerKeyword = this.keyword.toLowerCase()
    
    // 3. 查找并分割文本
    let lastIndex = 0
    let index = lowerText.indexOf(lowerKeyword)
    
    while (index !== -1) {
      // 添加非高亮部分
      if (index > lastIndex) {
        parts.push({
          text: this.content.substring(lastIndex, index),
          isHighlight: false
        })
      }
      
      // 添加高亮部分
      parts.push({
        text: this.content.substring(index, index + this.keyword.length),
        isHighlight: true
      })
      
      lastIndex = index + this.keyword.length
      index = lowerText.indexOf(lowerKeyword, lastIndex)
    }
    
    // 4. 添加剩余文本
    if (lastIndex < this.content.length) {
      parts.push({
        text: this.content.substring(lastIndex),
        isHighlight: false
      })
    }
    
    return parts
  }

  build() {
    Row() {
      ForEach(this.getHighlightParts(), (part: HighlightPart) => {
        Text(part.text)
          .fontSize(16)
          .fontColor(part.isHighlight ? '#FF7500' : '#333333')
      })
    }
  }
}

3. 状态管理实现

接下来实现状态管理,使用 AppStorageV2 管理搜索状态和历史记录:

// src/main/ets/model/SearchState.ets
@ObservedV2
export class SearchState {
  @Trace searchText: string = ''           // 搜索文本
  @Trace searchHistory: string[] = []      // 搜索历史
  
  constructor() {
    // 初始化时加载历史记录
    this.loadSearchHistory()
  }

  // 添加搜索历史
  addSearchHistory(keyword: string) {
    if (!keyword) return
    
    // 去重处理
    const index = this.searchHistory.indexOf(keyword)
    if (index > -1) {
      this.searchHistory.splice(index, 1)
    }
    
    // 添加到开头并限制数量
    this.searchHistory.unshift(keyword)
    if (this.searchHistory.length > 10) {
      this.searchHistory.pop()
    }
    
    this.saveSearchHistory()
  }

  // 其他状态管理方法...
}

4. 搜索建议列表实现

实现搜索建议列表的数据源和渲染:

  1. 自定义数据源
// src/main/ets/model/SearchDataSource.ets
export class SuggestionsDataSource extends BasicDataSource<SearchSuggestion> {
  constructor(suggestions: SearchSuggestion[]) {
    super(suggestions)
  }
}
  1. 搜索建议项组件
// src/main/ets/components/Search/SearchItem.ets
@ComponentV2
export struct SearchItem {
  @Require @Param suggestion: SearchSuggestion
  @Require @Param keyword: string
  @EventV2('click') onClick: () => void = () => {}

  build() {
    Row() {
      // 搜索图标
      SymbolGlyph($r('sys.symbol.search'))
        .size({ width: 20, height: 20 })
        .margin({ right: 8 })

      // 高亮文本
      HighlightText({
        content: this.suggestion.keyword,
        keyword: this.keyword
      })

      // 标签展示
      if (this.suggestion.tags.length > 0) {
        ForEach(this.suggestion.tags, (tag: string) => {
          Text(tag)
            .fontSize(12)
            .fontColor($r('sys.color.alert'))
            // 其他样式...
        })
      }
    }
    .width('100%')
    .padding(16)
    .onClick(() => this.onClick())
  }
}

5. 功能整合

最后在主页面中整合所有功能:

// src/main/ets/components/MainPage.ets
@ComponentV2
export struct TextHighlight {
  @Local searchState: SearchState = AppStorageV2.connect(SearchState, () => new SearchState())!
  private suggestionsDataSource: SuggestionsDataSource = new SuggestionsDataSource([])

  build() {
    Column() {
      // 搜索栏
      SearchBar({
        searchText: this.searchState.searchText,
        onSearchChange: (value: string) => {
          this.searchState.searchText = value
          this.updateSuggestions(value)
        },
        onClear: () => {
          this.searchState.searchText = ''
          this.suggestionsDataSource = new SuggestionsDataSource([])
        }
      })

      // 搜索建议或历史记录
      if (this.searchState.searchText) {
        this.buildSuggestionsList()
      } else {
        this.buildSearchHistory()
      }
    }
  }

  // 其他辅助方法...
}

技术总结

1. 核心技术点

文本处理算法
  • 大小写不敏感匹配:通过 toLowerCase() 实现
  • 文本分割策略:使用 indexOf 和 substring 进行精确分割
  • 高亮渲染:使用数组映射实现文本片段的动态渲染
状态管理方案
  • 使用 AppStorageV2 实现应用级状态管理
  • @ObservedV2 装饰器实现响应式更新
  • @Trace 实现细粒度的状态追踪
  • 状态持久化确保数据可靠性
性能优化策略
  • LazyForEach 实现列表懒加载
  • 合理的组件拆分避免不必要的重渲染
  • 数据源抽象实现高效的列表更新

2. 实现难点及解决方案

1. 文本高亮实现

难点:需要准确分割文本并保持原始大小写

解决方案:

  • 转换为小写进行匹配
  • 使用原始文本进行显示
  • 记录匹配位置确保准确分割
2. 状态管理

难点:状态更新可能导致循环引用

解决方案:

  • 单向数据流设计
  • 状态隔离
  • 合理的更新时机
3. 列表性能

难点:大量数据的渲染性能

解决方案:

  • 使用 BasicDataSource 实现数据源
  • LazyForEach 实现懒加载
  • 精确的更新通知

通过以上技术点的实现,我们成功构建了一个高性能、可维护的搜索组件。该组件不仅实现了基础的搜索功能,还通过合理的架构设计和性能优化,确保了良好的用户体验和开发体验。