第47篇搜索组件——考题搜索功能实现一、引言搜索功能是驾考应用中帮助用户快速定位特定考题的重要工具。当用户遇到不会的题目想查找相关知识点或者想针对某个特定主题进行练习时搜索功能就派上了用场。DriverLicenseExam 项目的search组件封装了搜索入口、搜索控制器和搜索结果跳转的完整流程。本文将深入解析其实现。二、搜索组件结构2.1 组件目录search/ ├──src/main/ets/ │ ├── components/ │ │ └── SearchComponents.ets← 搜索 UI 组件 │ └── controller/ │ └── SearchController.ets← 搜索控制器 └── Index.ets← 模块导出2.2 搜索流程用户点击搜索框 │ ▼ 进入 SearchPage │ ▼ 输入关键词 │ ▼ SearchController.search(keyword) │ ▼ 在 ExamDetail 中匹配题目文本 │ ├── 匹配成功 → 跳转到练习页展示搜索结果 │ └── 匹配失败 → 显示未找到相关题目提示三、搜索入口实现3.1 主页面搜索框搜索入口位于 HomeView 顶部与城市选择并列// HomeView.ets - 顶部搜索框Row(){// 城市选择Row(){Text(this.guideService.getGuideData().city) .fontSize(14).fontColor($r(sys.color.font_primary)) .fontWeight(FontWeight.Medium).margin({ right:4}) .maxFontScale(1);Image($r(app.media.city_triangle)) .width(12).height(12); } .onClick(() { this.vm.navStack.pushPathByName(selectCityView,true); }) .margin({ left:12, right:8});// 搜索框Row({space: 8 }){Image($r(app.media.ic_glass)) .width(16) .fillColor($r(sys.color.icon_primary));Text(请输入关键字搜索).fontSize(14).fontColor($r(sys.color.font_secondary)) .maxFontScale(1); } .backgroundColor($r(app.color.search_background)) .height(40) .layoutWeight(1).margin({ right:12}) .padding(12) .borderRadius(8).onClick(() { this.vm.navStack.pushPathByName(SearchPage,undefined); }); }这个搜索框的 UI 设计有几个值得注意的点搜索图标使用$r(app.media.ic_glass)引用本地资源占位文本使用Text组件而非TextInput的 placeholder因为搜索框实际上是点击跳转的入口**layoutWeight(1)**搜索框占据城市选择后的剩余空间**maxFontScale(1)**限制搜索框文字不跟随系统字体缩放3.2 搜索页面// SearchPage.etsComponentV2export struct SearchPage {Localkeyword: string ;LocalsearchResults: ExamDetail[] [];privatesearchController: SearchController new SearchController(); build() { NavDestination() { Column() {// 搜索输入框TextInput({ placeholder:输入考题关键词..., text:this.keyword }) .height(48) .padding({ left:16}) .borderRadius(24) .backgroundColor($r(sys.color.comp_background_primary)) .onChange((value: string) {this.keyword value;this.performSearch(); });// 搜索结果列表if(this.searchResults.length 0) { List() { ForEach(this.searchResults, (item: ExamDetail) { ListItem() {this.searchResultItem(item); } }); } .layoutWeight(1); }else{// 空状态Column() { Image($r(app.media.ic_search_empty)).width(80).height(80); Text(未找到相关题目).fontSize(14).fontColor(#999); } .layoutWeight(1) .justifyContent(FlexAlign.Center); } } .padding(16); } .title(搜索考题); } performSearch() {if(this.keyword.trim().length 0) {this.searchResults [];return; }// 通过 SearchController 执行搜索this.searchResults this.searchController.search(this.keyword); } }四、搜索控制器实现4.1 SearchController// SearchController.etsexportclassSearchController{privateexamDetails:ExamDetail[] [];constructor() {// 初始化时获取所有考题数据this.examDetailsExamService.instance(null).getAllExamDetails(); }search(keyword:string):ExamDetail[] {if(!keyword || keyword.trim().length0) {return[]; }constlowerKeyword keyword.toLowerCase();// 在题目文本和章节名称中匹配returnthis.examDetails.filter(itemitem.question.toLowerCase().includes(lowerKeyword) || item.chapterName.toLowerCase().includes(lowerKeyword) ); }// 按题型筛选搜索结果searchByType(keyword:string,type:QuestionTypeEnum):ExamDetail[] {constresults this.search(keyword);returnresults.filter(itemitem.questionTypetype); } }4.2 搜索结果的跳转搜索结果的点击跳转到练习页面使用EXAM_MANAGER_TYPE.search类型// 搜索项点击事件Builder searchResultItem(item: ExamDetail){Row(){Column({space: 4 }){Text(item.question).fontSize(14).maxLines(2).textOverflow({overflow: TextOverflow.Ellipsis });Text(item.chapterName).fontSize(12).fontColor(#999); } .layoutWeight(1); } .padding(12) .width(100%) .onClick(() { const param: ROUTE_PARAM { title: 搜索结果,type:EXAM_MANAGER_TYPE.search, keyword: this.keyword, }; this.vm.navStack.pushPathByName(practiceView,param); }); }五、搜索数据流5.1 搜索与 ExamService 的集成搜索功能与 ExamService 的getExamQuestionList方法集成// ExamService.ets - 搜索数据过滤case EXAM_MANAGER_TYPE.search: examData originData.filter(itemitem.question.includes(searchCondition));break;5.2 搜索参数传递// 搜索跳转时传递 keyword 参数constparam: ROUTE_PARAM { title:搜索结果, type: EXAM_MANAGER_TYPE.search, keyword:交通标志,// 搜索关键词};// PracticeView 接收参数.onReady((ctx: NavDestinationContext) {constparam this.vm.navStack.getParamByIndex(index -1)asROUTE_PARAM;if(param.keyword) {this.keyword param.keyword; }this.getExamManger(); });六、搜索功能优化6.1 防抖处理为了避免用户每次输入都触发搜索可以添加防抖机制privatesearchTimer: number -1; performSearch() {if(this.searchTimer ! -1) { clearTimeout(this.searchTimer); }this.searchTimer setTimeout(() {if(this.keyword.trim().length 0) {this.searchResults [];return; }this.searchResults this.searchController.search(this.keyword); },300);// 300ms 防抖}6.2 搜索历史可以扩展搜索功能记录用户的历史搜索关键词通过 Preferences 持久化saveSearchHistory(keyword:string){ const preferencesUtil PreferencesUtil.getInstance(); const preference preferencesUtil.getPreferences(context);lethistory preferencesUtil.getPreferencesValue(preference, searchHistory)asstring[];if(!history) history [];if(!history.includes(keyword)) { history.unshift(keyword);if(history.length 10) history.pop();// 只保留最近10条preferencesUtil.preferencesPut(preference, searchHistory,history); } }七、总结搜索组件通过 SearchController 和 SearchComponents 的组合实现了完整的搜索功能搜索入口主页面的搜索框点击跳转到搜索页面搜索逻辑关键词匹配题目文本和章节名称结果展示列表展示搜索结果支持点击跳转数据集成与 ExamService 的 EXAM_MANAGER_TYPE.search 集成扩展性支持防抖、搜索历史等优化关键源码文件components/search/src/main/ets/components/SearchComponents.ets— 搜索组件components/search/src/main/ets/controller/SearchController.ets— 搜索控制器products/entry/src/main/ets/pages/home/HomeView.ets— 搜索入口products/entry/src/main/ets/pages/home/SearchPage.ets— 搜索页面commons/datasource/src/main/ets/ExamService.ets— 搜索数据源