React Native自定義View解析Emoji

一、需求準備

在react native的類中實現可以解析多種字符格式的內容并放入到指定文本中。效果圖如下:

emoji_example_1

二、Emoji封裝

將需要對應好的Emoji表情圖片放到指定文件夾,然后寫一個公共的Const.js封裝成一個對象,實現變量與圖片資源的關系映射:

export const emojiReflection = {
    // emoji表情對應關系
    "[微笑]": require('../emojiImage/emoji_1.png'),
    "[呲牙]": require('../emojiImage/emoji_2.png'),
    "[偷笑]": require('../emojiImage/emoji_3.png'),
    ……
}

三、生命周期方法的簡單處理

這里只針對于子文本的展示,實際效果是將該文本放到一個ListView的item中,其功能實現無大差異,只需在外層包一個ListView即可。所以這里只需將這個文本放入一個Text標簽中。
  這里加入了對文本高度的判斷,超過了4行只展示4行,并展示出“更多”按鈕,由于設置了行高為20,只需在onLayout( )方法中對高度設置監聽處理即可。

return (
  <View>
    <Text key={'textMore'} style={{ lineHeight: 20, maxHeight: this.state.rowHeight }}
      onLayout={this.state.textState == 1 ? (e) => {
        let {x, y, width, height} = e.nativeEvent.layout;
        if (Math.ceil(height / 20) > 4 && this.state.textState == 1) {
          this.setState({ textState: 2, rowHeight: 80 });
        } else {
          this.setState({ rowHeight: height });
        }
      } : null}>
      {this.state.Views}
    </Text>
    {showMore}
  </View>
);

其中,這里的this.state.Views是一個數組,用于存放截取處理后的各個子View。可在constructor( )或componentWillMount( )方法中定義:

constructor() {
    super();
    this.state = {
        rowHeight: 10000,
        textState: 1,
        Views: [],
    }
}

這里,在componentWillMount( )方法中接收指定字符串textContent(當然你完全可以自定義一個你想要的String):

componentWillMount() {
    let textContent = this.props.textContent;
    this.matchContentString(textContent);
}

匹配的邏輯與思路

這里用到的是正則匹配的方法,先定義出三種匹配規則,即匹配[微笑]格式的emoji表情、匹配@...格式的文本、匹配http://www.baidu.com 格式的網址。
  先使用正則定義好匹配方法:

let emojiReg = '\\[[^\\]]+\\]';
let nameReg = '@([^\\s@]+)';
let httpReg = '((https://|http://|www\.|ftp://)[A-Za-z0-9\._\?%&;:+\-=/#]*)';
let emojiAt = new RegExp(emojiReg);
let nameAt = new RegExp(nameReg);
let httpAt = new RegExp(httpReg);

然后開始寫一個方法,實現自己的思路:將一個字符串按三種匹配規則匹配,得到3個index,取最小者,將這個字符串拆分成三部分,即0-index,index,index-end。然后0-index部分直接返回一個文本,index部分再分別處理,然后將最后的index-end部分實現遞歸,直到最終的index為-1,即只剩下純文本為止。

matchContentString(textContent) {
    // 匹配得到3個index并放入數組中
    let emojiIndex = textContent.search(emojiAt);
    let nameIndex = textContent.search(nameAt);
    let httpIndex = textContent.search(httpAt);
    let checkIndexArray = [];
    // 若匹配不到,則直接返回一個全文本
    if (emojiIndex === -1 && nameIndex === -1 && httpIndex === -1) {
        let emptyTextView = (<Text key ={'emptyTextView'+(Math.random()*100)}>{textContent}</Text>);
        this.state.Views.push(emptyTextView);
    } else {
      if (emojiIndex !== -1) checkIndexArray.push(emojiIndex);
      if (nameIndex !== -1) checkIndexArray.push(nameIndex);
      if (httpIndex !== -1) checkIndexArray.push(httpIndex);
      // 取index最小者
      let minIndex = Math.min.apply(Math, checkIndexArray);
      // 將0-index部分返回文本
      let firstTextView = (<Text key ={'firstTextView'+(Math.random()*100)}>{textContent.substring(0, minIndex)}</Text>);
      this.state.Views.push(firstTextView);
      // 將index部分作分別處理
      switch (minIndex) {
        case emojiIndex:
          this.matchEmojiString(textContent.substring(minIndex));
          break;
        case nameIndex:
          this.matchNameString(this.props.ats, this.props.atDeparts, textContent.substring(minIndex));
          break;
        case httpIndex:
          this.matchHttpString(textContent.substring(minIndex));
          break;
        default:
          break;
      }
    }
}

如上代碼,可看到,每執行一次比較處理index的思路就會將原文本拆分成三部分,然后再分別處理。這樣的定位index會比較明確。下面就再實現一下具體的三種處理方式:

【處理帶emoji表情的】

matchEmojiString(emojiStr) {

    let castStr = emojiStr.match(emojiAt);
    let emojiLength = castStr[0].length;
    let imageView = (<Image key={emojiStr} style={{ width: 15, height: 14 }} source={emojiReflection[castStr]} />);
    this.state.Views.push(imageView);
    this.matchContentString(emojiStr.substring(emojiLength));

}

【處理帶@...文本的】

matchNameString(ats, atDeparts, nameStr) {

    let castStr = nameStr.match(nameAt);
    let nameString = castStr[0];
    let atUser = null;
    let atMyDeparts = null;
    if (ats && ats.length > 0) {
      atUser = ats[0];
    }
    if (atUser == null && atDeparts && atDeparts.length > 0) {
      atMyDeparts = atDeparts[0];
    }

    let nameView = (<CustomTextView key={'name' + nameStr} textContent={nameString} contentType={'name'} atUser={atUser} atDeparts={atMyDeparts} />);
    this.state.Views.push(nameView);
    this.matchContentString(nameStr.substring(nameString.length));

}

【處理帶http:...網址的】

matchHttpString(httpStr) {

    let castStr = httpStr.match(httpAt);
    let httpString = castStr[0];
    let httpView = (<CustomTextView key={'http'+httpString} textContent={httpString} contentType={'http'} />);
    this.state.Views.push(httpView);
    this.matchContentString(httpStr.substring(httpString.length));

}

其中CustomTextView只是一個簡單的自定義View,只實現如跳轉鏈接,改變字體顏色等功能,完全可以自定義,在這里就不貼代碼了。
  最終,一個滿足三種匹配的自定義View就實現啦,看下效果咯~


emoji_example_2
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,710評論 25 708
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,818評論 18 139
  • 概述 iOS系統相機、相冊功能全部依托于圖像選取控制器UIImagePickerController,在使用該控制...
    蚊香醬閱讀 4,831評論 3 48
  • 表白就是冒著以后連朋友都不能做的危險去賭以后能正大光明牽你手擁抱你愛著你的機會 ...
    劉皓永遠都是劉皓閱讀 258評論 0 0
  • 2.28 火 早上起床還蠻早,結果還是8點多還在匆匆準備出門,去到公司對桌的同事看到我說,你忘記畫眉了,馬上想著向...
    ancilapple閱讀 215評論 0 0