在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

鴻蒙OS開發(fā)實(shí)戰(zhàn):【打造自己的搜索入口】

jf_46214456 ? 2024-03-29 20:17 ? 次閱讀

背景

幾乎每家應(yīng)用中都帶有搜索功能,關(guān)于這個(gè)功能的頁面不是特別復(fù)雜,但如果要追究其背后的一系列邏輯,可能是整個(gè)應(yīng)用中最復(fù)雜的一個(gè)功能。今天主要實(shí)踐目標(biāo),會拋開復(fù)雜的邏輯,嘗試純粹實(shí)現(xiàn)一個(gè)“搜索主頁”,主要包含,輸入框文字輸入,熱門詞展示,熱門帖子展示。全篇主要使用到的控件是TextInput, Flex, Swiper。為了貼近實(shí)戰(zhàn),文字輸入過程中,也增加了聯(lián)想詞功能。整個(gè)示例將在模擬狀態(tài)下完成,不做任何網(wǎng)絡(luò)請求。

功能清單

  1. 輸入框 - TextInput用法
  2. 按鈕搜索詞刪除 - 觸摸事件透傳用法
  3. 搜索按鈕 - 頁面返回用法
  4. 聯(lián)想詞 - Span用法,if...else 渲染用法
  5. 歷史搜索詞 - 行數(shù)限制,排序
  6. 熱門搜索詞 - 換行布局,行為識別(打開鏈接,發(fā)起搜索)
  7. 熱門帖子 - Swiper用法,Span用法

效果

Screenshot_20231219153940743.pngScreenshot_20231219152615992.png

布局結(jié)構(gòu)

整體頁面分為上下布局兩大部分:

  1. 搜索欄
  2. 可滾動內(nèi)容區(qū)域

開始前熟悉鴻蒙文檔

鴻蒙OS開發(fā)更多內(nèi)容↓點(diǎn)擊HarmonyOSOpenHarmony技術(shù)
鴻蒙技術(shù)文檔《鴻蒙NEXT星河版開發(fā)學(xué)習(xí)文檔》

搜狗高速瀏覽器截圖20240326151344.png

搜索框

HarmonyOS 提供了Search控件, 這種樣式不太滿足今天要做的需求,所以我還是準(zhǔn)備采用TextInput控件重新定制

0000000000011111111.20231116092649.47082174539092627589057824940312.gif

預(yù)期的搜索框需要包含基礎(chǔ)的三個(gè)功能

  1. 文字輸入
  2. 文字刪除
  3. 提交已輸入的文字(即:準(zhǔn)備發(fā)起搜索)

這個(gè)樣式的實(shí)現(xiàn)方式,我采用了左右布局,左布局采用疊加布局方式,翻譯為代碼,表現(xiàn)形式如下

//一. (左布局)輸入框 + (右布局)搜索按鈕
Row() {
  Stack() {
    // 輸入框
    TextInput()
   // 放大鏡圖片 + 刪除按鈕圖片 
    Row() {
      Image(放大鏡圖片)
      if (this.currentInputBoxContent.length != 0) {
         Image(刪除按鈕圖片)
     }
  }
  //搜索按鈕 / 返回按鈕
  Text(this.searchButtonText)
}

這里的Stack布局方式,實(shí)際中會引發(fā)一個(gè)問題:點(diǎn)擊TextInput控件時(shí)非常不靈敏,實(shí)際情況是“放大鏡圖片+刪除按鈕圖解片”Row布局,消耗了輸入框的觸摸事件。 解決這個(gè)問題,可以使用系統(tǒng)提供的hitTestBehavior(HitTestMode.None)這個(gè)接口,這個(gè)接口的參數(shù)提供了4種響應(yīng)觸摸事件的功能

所以解決此問題只需要添加完這個(gè)接口即可恢復(fù)正常觸摸事件:見代碼中的 NOTE:父組件不消耗觸摸事件

//一. (左布局)輸入框 + (右布局)搜索按鈕
Row() {
  Stack() {
    // 輸入框
    TextInput()
   // 放大鏡圖片 + 刪除按鈕圖片 
    Row() {
      Image(放大鏡圖片)
      if (this.currentInputBoxContent.length != 0) {
         Image(刪除按鈕圖片)
     }
     .hitTestBehavior(HitTestMode.None) // NOTE:父組件不消耗觸摸事件

  }
  //搜索按鈕 / 返回按鈕
  Text(this.searchButtonText)
}

由于采用的是Stack疊加布局方式,所以要解決的第二個(gè)問題是如何將Row布局兩邊對齊Stack即,處于TextInput控件的兩端,根據(jù)[Row容器內(nèi)子元素在水平方向上的排列]可知,在Row布局上添加justifyContent(FlexAlign.SpaceBetween)這句代碼即可。

官方指導(dǎo)示意圖:

0000000000011111111.20231211142810.46416226973241546619287224558714.png

變更后的代碼

//一. (左布局)輸入框 + (右布局)搜索按鈕
Row() {
  Stack() {
    // 輸入框
    TextInput()
   // 放大鏡圖片 + 刪除按鈕圖片 
    Row() {
      Image(放大鏡圖片)
      if (this.currentInputBoxContent.length != 0) {
         Image(刪除按鈕圖片)
     }
     .hitTestBehavior(HitTestMode.None) // NOTE:父組件不消耗觸摸事件
     .justifyContent(FlexAlign.SpaceBetween) // NOTE: 兩端對齊

  }
  //搜索按鈕 / 返回按鈕
  Text(this.searchButtonText)
}

TextInput的構(gòu)造函數(shù)參數(shù)說明

  • placeholder: 俗稱:按提示,提示詞,引導(dǎo)詞
  • text: 輸入框已輸入的文字內(nèi)容

TextInput的屬性方法onChange

用來監(jiān)聽最新已輸入的文字

  • 這個(gè)方法中,我們可以通過判斷內(nèi)容長度,來設(shè)置控制中的搜索按鈕文字,如果有內(nèi)容,按鈕文案將變?yōu)?搜索",反之,按鈕文案變?yōu)椤叭∠保袋c(diǎn)擊之后將關(guān)閉當(dāng)前頁面; 同時(shí)請求遠(yuǎn)端聯(lián)想詞的功能也是在這里觸發(fā),注意:本篇文章中的聯(lián)想詞僅僅是本地模擬的數(shù)據(jù),沒有進(jìn)行網(wǎng)絡(luò)請求,也沒有模擬網(wǎng)絡(luò)延時(shí)加載,在真實(shí)場景中一定要注意用戶的操作行為應(yīng)該中斷本次聯(lián)想詞的網(wǎng)絡(luò)請求(即使網(wǎng)絡(luò)請求已經(jīng)發(fā)出去,回來之后,也要扔掉拿到的聯(lián)想詞數(shù)據(jù))。

TextInput的屬性方法enterKeyType

這個(gè)用來修改軟件盤上的回車鍵文字提示,這個(gè)設(shè)置的值為EnterKeyType.Search,所以在中文模式下,你會發(fā)現(xiàn)鍵盤上的文字為:搜索

TextInput({ placeholder: '熱詞搜索', text: this.currentInputBoxContent })
  .height('40vp')
  .fontSize('20fp')
  .enterKeyType(EnterKeyType.Search)
  .placeholderColor(Color.Grey)
  .placeholderFont({ size: '14vp', weight: 400 })
  .width('100%')
  .padding({ left: '35vp', right: '35vp' })
  .borderStyle(BorderStyle.Solid)
  .borderWidth('1vp')
  .borderColor(Color.Red)
  .onChange((currentContent) = > {
    this.currentInputBoxContent = currentContent
    if (this.currentInputBoxContent.length != 0) {
      this.searchButtonText = '搜索'
      this.showThinkWord = true
      this.simulatorThinkWord()
    } else {
      this.searchButtonText = '取消'
      this.showThinkWord = false
    }
  })
  .onSubmit((enterKey: EnterKeyType) = > {
    this.submitData(new HistoryWordModel(0, this.currentInputBoxContent));
  })

至此,一個(gè)完整的輸入框已完美的完成布局。

歷史搜索詞

一個(gè)搜索的新手產(chǎn)品,在講解這部分需求時(shí),會使用簡短的話術(shù):把搜索過的內(nèi)容顯示出來。

實(shí)際情況是比較嚴(yán)謹(jǐn)復(fù)雜的,最多多展示多少行? 每個(gè)歷史詞最多展示多少個(gè)字符? 要不要識別詞性?......`, 針對這些嚴(yán)格的邏輯,研發(fā)人員需要優(yōu)先解決動態(tài)布局的問題,剩下的僅僅是堆積代碼。

Android系統(tǒng)中,針對這種布局場景,需要代碼動態(tài)實(shí)現(xiàn),即采用Java方式布局,不幸的是HarmonyOS 中沒有這個(gè)說法。

解決方案:

給歷史詞變量添加 @State 修飾,根據(jù)視圖高度動態(tài)計(jì)算行數(shù),然后動態(tài)刪除多余關(guān)鍵詞記錄

注意:@State 修飾的Array無法對sort方法生效,結(jié)合場景描述即:最新搜索的關(guān)鍵詞,都要排在第一個(gè)位置,所以每發(fā)起一次搜索,都要對Array類型的變量進(jìn)行一次排序,由于@State的限制,我們需要在中間中轉(zhuǎn)一次。

既然已經(jīng)知道問題,那么先看一下布局代碼,然后繼續(xù)完成需求

首先,對動態(tài)布局的需求來講,HarmonyOS中,貌似只能用Flex容器來解決,因?yàn)樗粌H可以包含子組件,也有自動換行功能,所這里我采用的是Flex容器,如果你要更好的方案,歡迎留言交流

通過視圖高度動態(tài)計(jì)算行數(shù),可以依賴onAreaChange接口,在其回調(diào)中,通過每次的新值結(jié)構(gòu)體(即Area),獲取當(dāng)前布局高度,然后除以第一次獲取到的高度,這樣即可完成行數(shù)的測算

關(guān)于Flex自動換行功能,這個(gè)要依賴于一個(gè)參數(shù)wrap: FlexWrap.Wrap

if (this.historyWords.length != 0) {

  Row() {

    Text('歷史搜索')
      .fontSize('20fp')
      .fontWeight(FontWeight.Bold)

    Image($r('app.media.ic_public_delete')).width('20vp').height('20vp')
      .onClick(() = > {
        this.dialogController.open()
      })

  }.width('100%')
  .margin({ top: '20vp' })
  .padding({ left: '10vp', right: '10vp' })
  .justifyContent(FlexAlign.SpaceBetween)

  Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
    ForEach(this.historyWords, (item: HistoryWordModel, index) = > {

      Text(item.word)
        .fontSize(15)
        .margin(5)
        .fontColor('#5d5d5d')
        .maxLines(1)
        .backgroundColor('#f6f6f6')
        .padding({ left: 20, right: 20, top: 5, bottom: 5 })
        .borderRadius('30vp')
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .onClick(()= >{
          this.submitData(item);
        })
    })
  }.width('100%')
  .margin({ top: '12vp' })
  .onAreaChange((oldValue: Area, newValue: Area) = > {

    let newHeight = newValue.height as number

    //全局聲明一個(gè)歷史詞單行高度變量,初始值設(shè)置為0,一旦產(chǎn)生歷史詞,將行高設(shè)置為此值
    //后續(xù)將以此值為標(biāo)準(zhǔn)來計(jì)算歷史詞行數(shù)
    if(this.currentHistoryHeight == 0){
       this.currentHistoryHeight = newHeight
    }

    //這里僅僅取整
    this.currentLineNumbs = newHeight / this.currentHistoryHeight

    //MAX_LINES 代表最大行數(shù)
    if (this.currentLineNumbs >= MAX_LINES) {
    
      //刪除一個(gè)歷史詞,由于historyWords添加了@State修飾,所以數(shù)據(jù)發(fā)生變化后,頁面會刷新
      //頁面刷新后,又會重新觸發(fā)此方法
      this.historyWords = this.historyWords.slice(0, this.historyWords.length-1)
    }

  })

}

剛剛提到過一個(gè)問題,@State 修飾的Array變量是無法進(jìn)行排序的。應(yīng)對這個(gè)問題,可以在中間中轉(zhuǎn)一下,即聲明一個(gè)局部Array,先將歷史記錄賦值給它,讓這個(gè)局部Array參與sort,然后清空@State修飾的Array變量,最終將局部Array賦值給@State修飾的Array變量,描述有點(diǎn)繁瑣,直接看代碼。

只要發(fā)起搜索行為,都會使用到此方法, 另外注意閱讀代碼注釋有NOTE的文字

submitData(wordModel: HistoryWordModel) {
  if (wordModel.word.length != 0) {

    //標(biāo)識本次搜索的關(guān)鍵詞是否存在
    let exist: boolean = false
    
    //如果搜索關(guān)鍵詞存在,記錄其位置,如果發(fā)現(xiàn)其已經(jīng)是第一個(gè)位置,則不進(jìn)行排序刷新動作
    let existIndex: number = -1

     //判斷搜索關(guān)鍵是否存在于歷史搜索詞列表中
    this.historyWords.forEach((item, index) = > {
         if(item.word === wordModel.word){
           //如果本次搜索關(guān)鍵詞已經(jīng)處于歷史搜索詞的第一個(gè)位置,不做刪除動作
           if(index != 0){
             //如果存在,先刪除歷史詞列表中的這個(gè)關(guān)鍵詞
             this.historyWords.splice(index, 1)
           }
           exist = true
           existIndex = index
         }
    });

    //本次搜索關(guān)鍵詞在歷史搜索詞列表中處于第一個(gè)位置,因此不做任何額外處理
    //NOTE:真實(shí)場景中,這里除了重置狀態(tài),應(yīng)該發(fā)起網(wǎng)絡(luò)請求
    if(existIndex == 0){
      console.log('不需要刷新頁面')
      this.currentInputBoxContent = ''
      this.searchButtonText = '取消'
      this.showThinkWord = false
      return
    }

    if(!exist){
      //如果本次搜索關(guān)鍵詞在歷史詞列表中不存在,則將其加入其中
      wordModel.index = this.historyWordIndex++
      this.historyWords.push(wordModel)
    } else {
      //如果本次搜索關(guān)鍵詞已存在于歷史詞列表中,將其對應(yīng)的下標(biāo)加1,因?yàn)楹罄m(xù)會用下表排序
      //下標(biāo)越大代表離發(fā)生過的搜索行為離當(dāng)前越近
      this.historyWordIndex++
      this.historyWords.push(new HistoryWordModel(this.historyWordIndex, wordModel.word, wordModel.link))
    }

    //NOTE:這個(gè)就是中轉(zhuǎn)排序的起始代碼
    let Test: Array< HistoryWordModel > = []

    this.historyWords.forEach((item, index) = > {
      Test.push(item)
    })

    Test.sort((a:HistoryWordModel, b:HistoryWordModel) = > {
       return b.index - a.index
    })

    this.historyWords.length = 0

    Test.forEach((item, index) = > {
      this.historyWords.push(item)
    })
    //NOTE:這個(gè)就是中轉(zhuǎn)排序的結(jié)束代碼

    this.currentInputBoxContent = ''
    this.searchButtonText = '取消'
    this.showThinkWord = false
  } else {
    Prompt.showToast({
      message: '請輸入關(guān)鍵詞',
      bottom: px2vp(this.toastBottom)
    })
  }
}

至此,歷史記錄也實(shí)現(xiàn)完成。

聯(lián)想詞實(shí)現(xiàn)

在已有的搜索場景中,我們都知道,當(dāng)發(fā)起聯(lián)想詞時(shí),歷史搜索記錄,熱詞等等,均不會出現(xiàn)在當(dāng)前屏幕中,為了實(shí)現(xiàn)此種效果,我采用了Stack控件疊加覆蓋機(jī)制和if...else渲染機(jī)制,最終實(shí)現(xiàn)完成之后,發(fā)現(xiàn)沒必要使用Stack控件,因?yàn)橛昧薸f...else布局后,像當(dāng)于會動態(tài)掛載和卸載試圖。

聯(lián)想詞實(shí)現(xiàn)還會碰到的一個(gè)問題:高亮關(guān)鍵詞, 按照HarmonyOS 的布局機(jī)制,一切布局都應(yīng)該提前計(jì)算好,全量布局多種場景樣式,以if...else機(jī)制為基礎(chǔ),完整最終的業(yè)務(wù)場景效果。

那么,如何提前計(jì)算好數(shù)據(jù)呢?高亮詞在數(shù)據(jù)結(jié)構(gòu)上我們可以分為三段:前,中,后。如何理解呢?比如搜索關(guān)鍵詞“1”,那我的聯(lián)想詞無非就幾種情況123,213,1,231,那么,我聲明三個(gè)變量s, m, e, 分別代表前,中,后,此時(shí)你會發(fā)現(xiàn)三個(gè)變量是完全可以覆蓋所有的匹配場景的。這種方式暫且命名為:分割,“分割”后,在最終展示時(shí),由于需要高亮文字,所以,我們還需要知曉已“分割”的文字中,到底哪一段應(yīng)該高亮,基于此種考慮,需要額外聲明高亮下標(biāo),參考“前,中,后”,將下標(biāo)分別定義為0,1,2

具體實(shí)現(xiàn),看代碼吧

Stack() {
  //聯(lián)想詞需要展示時(shí)
  if (this.showThinkWord) {
    Column() {
      //遍歷聯(lián)想詞
      ForEach(this.thinkWords, (item: ThinkWordModel, index) = > {

        //NOTE: Span控件可以實(shí)現(xiàn)文字分段多種樣式
        Text() {
          //判斷一條聯(lián)想詞數(shù)據(jù)的“前”
          if (item.wordStart && item.wordStart.length != 0) {
            Span(item.wordStart)
              .fontSize(18)
              .fontColor(item.highLightIndex == 0 ? item.highLightColor : item.normalColor)
          }
          //判斷一條聯(lián)想詞數(shù)據(jù)的“中”
          if (item.wordMid && item.wordMid.length != 0) {
            Span(item.wordMid)
              .fontSize(18)
              .fontColor(item.highLightIndex == 1 ? item.highLightColor : item.normalColor)
          }
          //判斷一條聯(lián)想詞數(shù)據(jù)的“后”
          if (item.wordEnd && item.wordEnd.length != 0) {
            Span(item.wordEnd)
              .fontSize(18)
              .fontColor(item.highLightIndex == 2 ? item.highLightColor : item.normalColor)
          }
        }......

      })

    }......

  } else {
  
// 沒有聯(lián)想詞時(shí),系統(tǒng)機(jī)制會講聯(lián)想詞視圖卸載掉,即if中的視圖會完全從視圖節(jié)點(diǎn)中拿掉
    Column() {
      //二. 搜索歷史
      if (this.historyWords.length != 0) {
        ......
      }

      //三. 熱門搜索
      Text('熱門搜索')......

      Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
        ForEach(this.hotWords, (item: HotWordsModel, index) = > {
          Text(item.word)......
        })
      }
   

      //四. 熱門帖子
      Text('熱門帖子')
      Swiper(this.swiperController) {

        LazyForEach(this.data, (item: string, index: number) = > {
           ......
        }, item = > item)

      }
     
    }

  }

}

熱門帖子實(shí)現(xiàn)

在整個(gè)搜索主頁中,這個(gè)功能可能算比較簡單的,在Scroll控件中放置Swiper控件,然后按照官方文檔,循環(huán)塞入數(shù)據(jù),整個(gè)效果即可實(shí)現(xiàn)。 這個(gè)里邊用到了Span,由于我們在聯(lián)想詞實(shí)現(xiàn)時(shí)已經(jīng)實(shí)踐過了Span, 這里就不再描述。

NOTE:為了迎合需求,將滑動指示隱藏掉,indicator(false)false代表隱藏滑動指示

//四. 熱門帖子
Text('熱門帖子')
  .fontSize('20fp')
  .width('100%')
  .fontWeight(FontWeight.Bold)
  .margin({ left: '10vp', top: '20vp' })

Swiper(this.swiperController) {

  //data僅僅是為了循環(huán),數(shù)據(jù)總個(gè)數(shù)是3  
  LazyForEach(this.data, (item: string, index: number) = > {

    //每一頁Swiper內(nèi)容視圖,通過 @Builder 修飾的方法進(jìn)行一次封裝
    if (index == 0) {
      this.swiperList(this.hotTopicList1)
    } else if (index == 1) {
      this.swiperList(this.hotTopicList2)
    } else if (index == 2) {
      this.swiperList(this.hotTopicList3)
    }

  }, item = > item)

}
.padding({ bottom: '50vp' })
.displayMode(SwiperDisplayMode.AutoLinear)
.margin({ top: '12vp' })
.cachedCount(2)
.index(1)
.indicator(false)
.loop(true)
.itemSpace(0)
.curve(Curve.Linear)

完整代碼

主頁面代碼 SearchUI.ets

import common from '@ohos.app.ability.common';
import Prompt from '@system.prompt';
import router from '@ohos.router';
import dataPreferences from '@ohos.data.preferences';

import { CommonConstants } from '../../common/CommonConstants';
import HotWordsModel from '../../viewmodel/HotWordsModel';
import mediaquery from '@ohos.mediaquery';
import ThinkWordModel from '../../viewmodel/ThinkWordModel';
import HistoryWordModel from '../../viewmodel/HistoryWordModel';

const MAX_LINES: number = 3;

@Entry
@Component
struct SearchUIIndex {
  private hotTopicList1: Array< string > = [
    '四種醉駕可從寬處理',
    '冰面摔倒至腹腔出血',
    '董宇輝復(fù)播',
    '朱一龍拍戲受傷送醫(yī)',
    '音樂節(jié)求婚觀眾退票',
    '周杰倫新歌歌名',
    '用好“改革開放”這關(guān)鍵一招',
    '男子冬釣失聯(lián) 遺體在冰縫中被發(fā)現(xiàn)',
    '女孩用科目三跳繩 獲省級比賽第1名',
    '美麗鄉(xiāng)村 幸福生活',
  ]
  private hotTopicList2: Array< string > = [
    '醉駕輕微可不起訴',
    '狄龍被驅(qū)逐',
    '勞榮枝希望還清花唄',
    '周海媚告別儀式完成',
    '董宇輝兼任副總裁',
    '小米智能鎖自動開門',
    '李家超:基本法第23條明年內(nèi)實(shí)施',
    '山東兩幼師出租房內(nèi)遇害',
    '南京同曦老總大鬧裁判休息室',
    '女子出車禍鯊魚夾插入后腦勺',
    '官方辟謠南京過江隧道連環(huán)追尾',
    '上海地鐵開啟瘋狂動物城模式',
  ]
  private hotTopicList3: Array< string > = [
    '朱丹好友起訴朱丹',
    '"中年大叔"自拍刷屏',
    '西方臻選回應(yīng)被封號',
    '草莓價(jià)格大跳水',
    '庫里三分球8中0',
    '國足開啟亞洲杯備戰(zhàn)',
  ]

  private currentHistoryHeight: number = 0

  @State toastBottom: number = 0;
  @State currentInputBoxContent: string = ''
  private controller = new TextInputController()
  private hotWords: Array< HotWordsModel > = []
  @State historyWords: Array< HistoryWordModel > = []
  @State inputBoxFocus: boolean = false;
  @State hotwordLines: number = 0
  @State searchButtonText: string = '取消'
  private swiperController: SwiperController = new SwiperController()
  private data: MyDataSource = new MyDataSource([])
  private currentLineNumbs: number = 0
  private context = getContext(this) as common.UIAbilityContext;
  @State screenDirection: number = this.context.config.direction
  @State showThinkWord: boolean = false
  @State thinkWords: Array< ThinkWordModel > = []

  // 當(dāng)設(shè)備橫屏?xí)r條件成立
  listener = mediaquery.matchMediaSync('(orientation: landscape)');
  dialogController: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample({
      historyWords: $historyWords,
      title: '確認(rèn)全部刪除?',
      cancel: this.onCancel,
      confirm: this.onAccept,
    }),
    alignment: DialogAlignment.Default, // 可設(shè)置dialog的對齊方式,設(shè)定顯示在底部或中間等,默認(rèn)為底部顯示
  })

  onCancel() {

  }

  onAccept() {
    console.log('當(dāng)前數(shù)組長度:' + this.historyWords.length)
  }

  configureParamsByScreenDirection() {
    if (this.screenDirection == 0) {
      this.toastBottom = (AppStorage.Get(CommonConstants.ScreenHeight) as number) / 2
    } else {
      this.toastBottom = (AppStorage.Get(CommonConstants.ScreenWidth) as number) / 2
    }
  }

  DATASOURCE: string[] = [
    '聯(lián)想詞測試',
    '測試聯(lián)想詞',
    '全城尋找測試在哪里',
    '找不到人',
    '哈爾濱的啤酒好喝',
    'HarmonyOS版權(quán)歸屬華為'
  ]

  simulatorThinkWord() {

    this.thinkWords = []

    this.DATASOURCE.forEach((value: string, index: number) = > {
      let s: string = ''
      let m: string = ''
      let e: string = ''
      let hIndex: number = -1

      let position = value.indexOf(this.currentInputBoxContent)
      if (position != -1) {

        if (position == 0) {
          s = value.substr(0, this.currentInputBoxContent.length)
        } else {
          s = value.substr(0, position)
        }

        if (s.length < value.length) {

          position = value.substr(s.length).indexOf(this.currentInputBoxContent)

          if (position == -1) {
            m = value.substr(s.length)
          } else {
            m = value.substr(s.length, this.currentInputBoxContent.length)
          }

          if (s.length + m.length < value.length) {
            e = value.substr(s.length + m.length)
          }

        }

        if (s === this.currentInputBoxContent) {
          hIndex = 0
        } else if (m === this.currentInputBoxContent) {
          hIndex = 1
        } else if (e === this.currentInputBoxContent) {
          hIndex = 2
        }

        this.thinkWords.push(new ThinkWordModel('#000000', '#ff0000', hIndex, s, m, e))
      }

    })

  }

  onPortrait(mediaQueryResult) {
    if (mediaQueryResult.matches) {
      //橫屏
      this.screenDirection = 1
    } else {
      //豎屏
      this.screenDirection = 0
    }

    setTimeout(() = > {
      this.configureParamsByScreenDirection()
    }, 300)
  }

  aboutToAppear() {
    this.searchButtonText = '取消'

    let list = []
    for (var i = 1; i <= 3; i++) {
      list.push(i.toString());
    }
    this.data = new MyDataSource(list)

    this.hotWords.push(new HotWordsModel('HarmonyOS', '#E84026', 'https://developer.harmonyos.com/'))
    this.hotWords.push(new HotWordsModel('實(shí)名認(rèn)證', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('HMS Core', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('Serverless', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('生態(tài)市場', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('應(yīng)用上架', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('倉頡', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('HUAWEI HiAI', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('表盤', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('推送', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('主題', '#5d5d5d'))
    this.hotWords.push(new HotWordsModel('公測', '#5d5d5d'))

    let portraitFunc = this.onPortrait.bind(this)
    this.listener.on('change', portraitFunc)

    this.toastBottom = (AppStorage.Get(CommonConstants.ScreenHeight) as number) / 2

    dataPreferences.getPreferences(getContext(this), 'HistoryWord', (err, preferences) = > {
      if (err) {
        console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
        return;
      }
      console.info('Succeeded in getting preferences.');
      // 進(jìn)行相關(guān)數(shù)據(jù)操作
    })

  }

  historyWordIndex: number = 1

  submitData(wordModel: HistoryWordModel) {
    if (wordModel.word.length != 0) {

      let exist: boolean = false
      let existIndex: number = -1

      this.historyWords.forEach((item, index) = > {
           if(item.word === wordModel.word){
             if(index != 0){
               this.historyWords.splice(index, 1)
             }
             exist = true
             existIndex = index
           }
      });

      if(existIndex == 0){
        console.log('不需要刷新頁面')
        this.currentInputBoxContent = ''
        this.searchButtonText = '取消'
        this.showThinkWord = false
        return
      }

      if(!exist){
        wordModel.index = this.historyWordIndex++
        this.historyWords.push(wordModel)
      } else {
        this.historyWordIndex++
        this.historyWords.push(new HistoryWordModel(this.historyWordIndex, wordModel.word, wordModel.link))
      }

      let Test: Array< HistoryWordModel > = []

      this.historyWords.forEach((item, index) = > {
        Test.push(item)
      })

      Test.sort((a:HistoryWordModel, b:HistoryWordModel) = > {
         return b.index - a.index
      })

      this.historyWords.length = 0

      Test.forEach((item, index) = > {
        this.historyWords.push(item)
      })

      this.currentInputBoxContent = ''
      this.searchButtonText = '取消'
      this.showThinkWord = false
    } else {
      Prompt.showToast({
        message: '請輸入關(guān)鍵詞',
        bottom: px2vp(this.toastBottom)
      })
    }
  }

  build() {

    Column() {

      //一. 輸入框 + 搜索按鈕
      Row() {
        Stack() {
          TextInput({ placeholder: '熱詞搜索', controller: this.controller, text: this.currentInputBoxContent })
            .height('40vp')
            .fontSize('20fp')
            .enterKeyType(EnterKeyType.Search)
            .placeholderColor(Color.Grey)
            .placeholderFont({ size: '14vp', weight: 400 })
            .width('100%')
            .padding({ left: '35vp', right: '35vp' })
            .borderStyle(BorderStyle.Solid)
            .borderWidth('1vp')
            .borderColor(Color.Red)
            .onChange((currentContent) = > {
              this.currentInputBoxContent = currentContent
              if (this.currentInputBoxContent.length != 0) {
                this.searchButtonText = '搜索'
                this.showThinkWord = true
                this.simulatorThinkWord()
              } else {
                this.searchButtonText = '取消'
                this.showThinkWord = false
              }
            })
            .onSubmit((enterKey: EnterKeyType) = > {
              this.submitData(new HistoryWordModel(0, this.currentInputBoxContent));
            })

          Row() {
            Image($r('app.media.ic_public_input_search')).width('20vp').height('20vp')
            if (this.currentInputBoxContent.length != 0) {
              Image($r('app.media.ic_public_cancel_filled')).width('20vp').height('20vp')
                .onClick(() = > {
                  this.currentInputBoxContent = ''
                })
            }
          }.width('100%')
          .hitTestBehavior(HitTestMode.None)
          .justifyContent(FlexAlign.SpaceBetween)
          .padding({ left: '10vp', right: '10vp' })

        }.alignContent(Alignment.Start)
        .width('83%')

        Text(this.searchButtonText)
          .fontSize('15fp')
          .borderRadius('10vp')
          .padding('5vp')
          .backgroundColor(Color.Red)
          .fontColor(Color.White)
          .width('15%')
          .textAlign(TextAlign.Center)
          .onClick(() = > {
            if ('搜索' === this.searchButtonText) {
              this.submitData(new HistoryWordModel(0, this.currentInputBoxContent));
            } else {
              if ("1" === router.getLength()) {
                this.context.terminateSelf()
              } else {
                router.back()
              }
            }
          })
          .stateStyles({
            focused: {
              .backgroundColor(Color.Orange)
            },
            pressed: {
              .backgroundColor(Color.Orange)
            },
            normal: {
              .backgroundColor(Color.Red)
            }
          })

      }.justifyContent(FlexAlign.SpaceBetween)
      .padding({ left: '10vp', right: '10vp' })
      .width('100%')

      Scroll() {
        Stack() {

          if (this.showThinkWord) {
            Column() {
              ForEach(this.thinkWords, (item: ThinkWordModel, index) = > {

                Text() {
                  if (item.wordStart && item.wordStart.length != 0) {
                    Span(item.wordStart)
                      .fontSize(18)
                      .fontColor(item.highLightIndex == 0 ? item.highLightColor : item.normalColor)
                  }
                  if (item.wordMid && item.wordMid.length != 0) {
                    Span(item.wordMid)
                      .fontSize(18)
                      .fontColor(item.highLightIndex == 1 ? item.highLightColor : item.normalColor)
                  }
                  if (item.wordEnd && item.wordEnd.length != 0) {
                    Span(item.wordEnd)
                      .fontSize(18)
                      .fontColor(item.highLightIndex == 2 ? item.highLightColor : item.normalColor)
                  }
                }
                .width('100%')
                .height(50)
                .textAlign(TextAlign.Center)
                .fontSize(18)
                .textAlign(TextAlign.Start)
                .maxLines(1)
                .textOverflow({ overflow: TextOverflow.Ellipsis })

                Divider().width('100%').height(1).color(Color.Grey)
              })

            }
            .width('100%').height('100%')
            .padding({ left: '12vp', right: '12vp' })
            .backgroundColor(Color.White)

          } else {

            Column() {
              //二. 搜索歷史
              if (this.historyWords.length != 0) {

                Row() {

                  Text('歷史搜索')
                    .fontSize('20fp')
                    .fontWeight(FontWeight.Bold)

                  Image($r('app.media.ic_public_delete')).width('20vp').height('20vp')
                    .onClick(() = > {
                      this.dialogController.open()
                    })

                }.width('100%')
                .margin({ top: '20vp' })
                .padding({ left: '10vp', right: '10vp' })
                .justifyContent(FlexAlign.SpaceBetween)

                Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
                  ForEach(this.historyWords, (item: HistoryWordModel, index) = > {

                    Text(item.word)
                      .fontSize(15)
                      .margin(5)
                      .fontColor('#5d5d5d')
                      .maxLines(1)
                      .backgroundColor('#f6f6f6')
                      .padding({ left: 20, right: 20, top: 5, bottom: 5 })
                      .borderRadius('30vp')
                      .textOverflow({ overflow: TextOverflow.Ellipsis })
                      .onClick(()= >{
                        this.submitData(item);
                      })
                  })
                }.width('100%')
                .margin({ top: '12vp' })
                .onAreaChange((oldValue: Area, newValue: Area) = > {

                  let newHeight = newValue.height as number

                  if(this.currentHistoryHeight == 0){
                     this.currentHistoryHeight = newHeight
                  }

                  this.currentLineNumbs = newHeight / this.currentHistoryHeight

                  console.log('當(dāng)前行數(shù): ' + this.currentLineNumbs)

                  if (this.currentLineNumbs >= MAX_LINES) {
                    this.historyWords = this.historyWords.slice(0, this.historyWords.length-1)
                  }

                })

              }

              //三. 熱門搜索
              Text('熱門搜索')
                .fontSize('20fp')
                .width('100%')
                .fontWeight(FontWeight.Bold)
                .margin({ left: '10vp', top: '20vp' })

              Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
                ForEach(this.hotWords, (item: HotWordsModel, index) = > {

                  Text(item.word)
                    .fontSize(15)
                    .margin(5)
                    .fontColor(item.wordColor)
                    .backgroundColor('#f6f6f6')
                    .padding({ left: 20, right: 20, top: 5, bottom: 5 })
                    .borderRadius('30vp')
                    .onClick(() = > {
                      if (this.hotWords[index].wordLink && this.hotWords[index].wordLink.length != 0) {

                        router.pushUrl({ url: 'custompages/WebView', params: {
                          "targetUrl": this.hotWords[index].wordLink,
                        } })
                          .then(() = > {
                            console.info('Succeeded in jumping to the second page.')
                          }).catch((error) = > {
                          console.log(error)
                        })
                      } else if(this.hotWords[index].word){
                        this.submitData(new HistoryWordModel(0, this.hotWords[index].word));
                      }
                    })
                })
              }
              .width('100%')
              .margin({ top: '12vp' })
              .onAreaChange((oldValue: Area, newValue: Area) = > {
                console.log('熱詞高度:' + newValue.height + '')
              })

              //四. 熱門帖子
              Text('熱門帖子')
                .fontSize('20fp')
                .width('100%')
                .fontWeight(FontWeight.Bold)
                .margin({ left: '10vp', top: '20vp' })

              Swiper(this.swiperController) {

                LazyForEach(this.data, (item: string, index: number) = > {

                  if (index == 0) {
                    this.swiperList(this.hotTopicList1)
                  } else if (index == 1) {
                    this.swiperList(this.hotTopicList2)
                  } else if (index == 2) {
                    this.swiperList(this.hotTopicList3)
                  }

                }, item = > item)

              }
              .padding({ bottom: '50vp' })
              .displayMode(SwiperDisplayMode.AutoLinear)
              .margin({ top: '12vp' })
              .cachedCount(2)
              .index(1)
              .indicator(false)
              .loop(true)
              .itemSpace(0)
              .curve(Curve.Linear)
            }

          }

        }
      }.scrollBar(BarState.Off)

    }.padding({ top: px2vp(AppStorage.Get(CommonConstants.StatusBarHeight)) })

  }

  @Builder swiperList(data: string[]){
    Column() {
      ForEach(data, (da, i) = > {

        if(i == 0){
          Text(){
            Span((i+1)+'. ').fontColor('#E84026').fontSize(20)
            Span(da).fontColor('#5d5d5d').fontSize(18)
          }.width('100%').height(50)
        } else if(i == 1){
          Text(){
            Span((i+1)+'. ').fontColor('#ED6F21').fontSize(20)
            Span(da).fontColor('#5d5d5d').fontSize(18)
          }.width('100%').height(50)
        } else if(i == 2){
          Text(){
            Span((i+1)+'. ').fontColor('#F9A01E').fontSize(20)
            Span(da).fontColor('#5d5d5d').fontSize(18)
          }.width('100%').height(50)
        } else {
          Text((i + 1) + '. '+ da)
            .fontColor('#5d5d5d')
            .width('100%')
            .height(50)
            .textAlign(TextAlign.Center)
            .fontSize(18)
            .textAlign(TextAlign.Start)
        }

        if (i != this.hotTopicList1.length - 1) {
          Divider().width('100%').vertical(false)
        }

      })
    }.borderRadius('10vp')
    .margin({ left: '10vp', right: '10vp', bottom: '25vp' })
    .backgroundColor('#f6f6f6')
    .padding('10vp')

  }
}

@CustomDialog
struct CustomDialogExample {
  controller: CustomDialogController
  title: string = ''
  @Link historyWords: Array< string >
  cancel: () = > void
  confirm: () = > void

  build() {
    Column() {
      Text(this.title).fontSize(20).margin({ top: 10, bottom: 10 })
      Flex({ justifyContent: FlexAlign.SpaceAround }) {
        Button('取消')
          .onClick(() = > {
            this.controller.close()
            this.cancel()
          }).backgroundColor(0xffffff).fontColor(Color.Black)
        Button('確認(rèn)')
          .onClick(() = > {
            this.controller.close()
            this.confirm()
            this.historyWords = []
          }).backgroundColor(0xffffff).fontColor(Color.Red)
      }.margin({ bottom: 10 })
    }
  }
}

class MyDataSource implements IDataSource {
  private list: number[] = []
  private listener: DataChangeListener

  constructor(list: number[]) {
    this.list = list
  }

  totalCount(): number {
    return this.list.length
  }

  getData(index: number): any {
    return this.list[index]
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    this.listener = listener
  }

  unregisterDataChangeListener() {
  }
}

歷史詞數(shù)據(jù)結(jié)構(gòu) HistoryWordModel.ets

export default class HistoryWordModel {
  public index: number
  public link: string
  public word: string

  constructor(index, word, link?) {
    this.index = index
    this.link = link
    this.word = word
  }
}

熱詞數(shù)據(jù)結(jié)構(gòu) HotWordModel.ets

export default class HotWordModel {
  public word: string //詞語
  public wordColor: string //文字顏色
  public wordLink?: string //文字超鏈接

  constructor(word, wordColor, wordLink?) {
    this.word = word
    this.wordColor = wordColor
    this.wordLink = wordLink
  }
}

聯(lián)想詞數(shù)據(jù)結(jié)構(gòu) ThinkWordModel.ets

export default class ThinkWordModel {
   public normalColor: string
   public highLightColor: string
   public wordStart: string //詞語
   public wordMid: string //文字顏色
   public wordEnd: string //文字超鏈接
   public highLightIndex: number

   constructor(normalColor: string, highLightColor: string, highLightIndex: number,wordStart?: string, wordMid?: string,
               wordEnd?: string) {
      this.normalColor = normalColor
      this.highLightColor = highLightColor
      this.highLightIndex = highLightIndex
      this.wordStart = wordStart
      this.wordMid = wordMid
      this.wordEnd = wordEnd
   }

}
鴻蒙開發(fā)技術(shù):mau123789記住是v喔

總結(jié)

  1. 對于Android&iOS開發(fā)者來講,在HarmonyOS中實(shí)現(xiàn)動態(tài)布局,還是非常容易陷入之前的開發(fā)思路中
  2. 新的平臺,熟悉API很重要
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 移動開發(fā)
    +關(guān)注

    關(guān)注

    0

    文章

    52

    瀏覽量

    9762
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2365

    瀏覽量

    42894
  • HarmonyOS
    +關(guān)注

    關(guān)注

    79

    文章

    1978

    瀏覽量

    30273
  • OpenHarmony
    +關(guān)注

    關(guān)注

    25

    文章

    3727

    瀏覽量

    16382
  • 鴻蒙OS
    +關(guān)注

    關(guān)注

    0

    文章

    189

    瀏覽量

    4452
收藏 人收藏

    評論

    相關(guān)推薦

    鴻蒙實(shí)戰(zhàn)項(xiàng)目開發(fā):【短信服務(wù)】

    兩位前阿里高級研發(fā)工程師聯(lián)合打造的 《鴻蒙NEXT星河版OpenHarmony開發(fā)文檔》 里面內(nèi)容包含了(ArkTS、ArkUI開發(fā)組件、Stage模型、多端部署、分布式應(yīng)用
    發(fā)表于 03-03 21:29

    鴻蒙OS崛起,鴻蒙應(yīng)用開發(fā)工程師成市場新寵

    應(yīng)用的形態(tài)也在發(fā)生著翻天覆地的變化。作為全球領(lǐng)先的移動操作系統(tǒng)和智能終端制造商,華為公司自主研發(fā)的鴻蒙OS應(yīng)運(yùn)而生,致力于構(gòu)建一個(gè)統(tǒng)一的分布式操作系統(tǒng),為各行各業(yè)的應(yīng)用開發(fā)帶來全新的可能性。 一、
    發(fā)表于 04-29 17:32

    谷歌全新推出的Fuchsia OS,對鴻蒙有什么影響?

    正式公測,搭載的設(shè)備也沒有亮相。回想安卓系統(tǒng)對于塞班、Windows Phone的超越,鴻蒙OS也可以說是占得了先機(jī),能夠吸引更多的開發(fā)者和優(yōu)質(zhì)軟件,才能夠取得更大的優(yōu)勢。在未來,能夠在不同形態(tài)的硬件設(shè)備中,都搭建起
    發(fā)表于 09-08 16:12

    初識鴻蒙OS

    的元?dú)饨凶?b class='flag-5'>鴻蒙。華為HarmonyOS,中文名字取做鴻蒙,寓意將帶領(lǐng)國人在操作系統(tǒng)領(lǐng)域開天辟地,擺脫對IOS和Android兩大生態(tài)、兩座大山的依賴,走出國人自己的軟件生態(tài)之路。鴻蒙
    發(fā)表于 09-10 15:28

    鴻蒙OS適用的全場景到底什么意思?

    鴻蒙系統(tǒng)(HarmonyOS),第一款基于微內(nèi)核的全場景分布式OS,是華為自主研發(fā)的操作系統(tǒng)。華為在開發(fā)者大會HDC.2019上正式發(fā)布了鴻蒙系統(tǒng),該系統(tǒng)將率先部署在智慧屏、車載終端、
    發(fā)表于 09-25 09:25

    #HarmonyOS征文#—鴻蒙OS開發(fā)流程及DevEco Studio安裝

    鴻蒙OS的完整開發(fā)流程1. 注冊并實(shí)名認(rèn)證華為開發(fā)者賬號鴻蒙官網(wǎng):www.harmonyos.com注冊登錄華為賬號后,進(jìn)行實(shí)名認(rèn)證登錄之后
    發(fā)表于 07-22 11:43

    鴻蒙 OS 應(yīng)用開發(fā)初體驗(yàn)

    的操作系統(tǒng)平臺和開發(fā)框架。HarmonyOS 的目標(biāo)是實(shí)現(xiàn)跨設(shè)備的無縫協(xié)同和高性能。 DevEco Studio 對標(biāo) Android Studio,開發(fā)鴻蒙 OS 應(yīng)用的 IDE。
    發(fā)表于 11-02 19:38

    如何尋找鴻蒙源碼入口

    因?yàn)?b class='flag-5'>鴻蒙源碼剛開源,所以網(wǎng)上是不會搜到源碼講解的,搜到的基本都是鴻蒙OS應(yīng)用開發(fā)教程,這個(gè)和鴻蒙源碼是兩回事哈。
    的頭像 發(fā)表于 10-14 14:22 ?4093次閱讀
    如何尋找<b class='flag-5'>鴻蒙</b>源碼<b class='flag-5'>入口</b>

    華為鴻蒙os2.0系統(tǒng)官網(wǎng)報(bào)名入口

    之前華為鴻蒙os2.0系統(tǒng)的公測推進(jìn)迎來更多的內(nèi)容,在適配機(jī)型上也有所增加。另外,在今晚8點(diǎn)華為鴻蒙os2.0系統(tǒng)將會正式發(fā)布,但是有不少用戶仍然不清楚在哪里報(bào)名申請,下面我們一起來看
    的頭像 發(fā)表于 06-02 15:43 ?3.2w次閱讀

    華為鴻蒙系統(tǒng)申請入口 如何升級鴻蒙系統(tǒng)

    。 一.華為鴻蒙系統(tǒng)申請入口 1.打開“我的華為”APP 2.找到首頁的“升級嘗鮮” 3.點(diǎn)擊“立即嘗鮮” 4.選擇自己的機(jī)型,并且點(diǎn)擊“報(bào)名公測” 5.下載描述文件后就可以接到系統(tǒng)推送了。 二.華為
    的頭像 發(fā)表于 06-08 11:32 ?2.3w次閱讀

    鴻蒙os怎么升級

    6月2日,華為正式發(fā)布了鴻蒙armonyOS 2系統(tǒng),那么鴻蒙os如何升級?現(xiàn)將鴻蒙os升級方式告知如下。
    的頭像 發(fā)表于 06-08 16:26 ?2747次閱讀

    華為鴻蒙開發(fā)者官網(wǎng)申請入口

    華為鴻蒙開發(fā)者官網(wǎng)申請入口介紹,6 月 2 日,華為正式發(fā)布了鴻蒙系統(tǒng),肯定許多小伙伴想嘗鮮體驗(yàn),那么華為鴻蒙
    的頭像 發(fā)表于 06-16 09:29 ?3.6w次閱讀

    華為開發(fā)者大會2021鴻蒙os在哪場

    華為開發(fā)者大會2021將在10月22日-24日舉辦,地點(diǎn)為東莞松山湖,鴻蒙os 3.0或?qū)⑴c我們見面,那么華為開發(fā)者大會2021鴻蒙
    的頭像 發(fā)表于 10-22 15:24 ?1920次閱讀

    RISC-V MCU開發(fā)實(shí)戰(zhàn) (三):移植鴻蒙OS項(xiàng)目

    移植鴻蒙OS項(xiàng)目
    的頭像 發(fā)表于 11-01 11:08 ?2950次閱讀
    RISC-V MCU<b class='flag-5'>開發(fā)</b><b class='flag-5'>實(shí)戰(zhàn)</b> (三):移植<b class='flag-5'>鴻蒙</b><b class='flag-5'>OS</b>項(xiàng)目

    鴻蒙OS開發(fā)之 融合搜索概述

    HarmonyOS 融合搜索開發(fā)者提供搜索引擎級的全文搜索能力,可支持應(yīng)用內(nèi)搜索和系統(tǒng)全局搜索
    的頭像 發(fā)表于 01-29 16:24 ?585次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>OS</b><b class='flag-5'>開發(fā)</b>之  融合<b class='flag-5'>搜索</b>概述
    主站蜘蛛池模板: 欧美性a欧美在线| 亚洲视频1| 天天干天天操天天射| 午夜寂寞视频在线观看| 天天干狠狠插| 国产视频综合| h国产在线观看| 日本在线观看一区| 国产一区二区在线视频播放| 亚洲三级免费观看| 四虎最新网址入口| 欧美freesex交| 国产亚洲美女| 97人人人人| 欧美啊片| 日本xxxxxxx69xx| 涩狠狠狠狠色| 亚洲毛片大全| 日本国产中文字幕| 国产一级aaa全黄毛片| 香蕉操| 国内久久精品| 亚洲欧美综合一区二区三区四区| 在线观看网址你懂得| 三级黄色录像| 国产欧美精品一区二区色综合 | 欧美人与zoxxxx另类9| 欧美成网站| 色香视频首页| 黄色a级免费| 五月天婷婷基地| 91大神大战高跟丝袜美女| 欧美视频不卡一区二区三区 | 亚洲啪啪| 二区视频在线| 欧美午夜在线播放| cum4k在线| 国产一级αv片免费观看| 免费视频不卡| 日韩一级高清| 逼逼视频网站|