前言
在IM通訊軟件中,基本上都會有emoji表情功能。聊天氣泡中要顯示文字和emoji表情的混排(下圖所示),在原生iOS開發時,可以用富文本NSAttributedString實現,安卓中用SpannableString實現。當用到React-Native來開發這個功能的時候,貌似沒有直接的現成的實現方案。經過一番努力,這個功能已經在項目中實現
,在此記錄。
思路
假設有一條信息在輸入框是這樣的:“假設[微笑]有一條信息[齜牙]是這樣的”
[微笑],[齜牙]都是emoji表情的名稱.
這條消息發送出去之後,要顯示成如上圖形式的樣式。也就是要把emoji表情名稱替換成相應圖片。
這條消息發送出去之後,要顯示成如上圖形式的樣式。也就是要把emoji表情名稱替換成相應圖片。
處理步驟如下:
1.運用正則表達式將表情名稱識別出來,然後把這條文本截成數組:
[
{content:假設},
{resource:[微笑]},
{content:有一條信息},
{resource:[齜牙]},
{content:是這樣的}
]
2.根據這個數組進行組件排列,key是content的話放置Text,key是resources的話放置Image,一字排開。
字符串轉成數組
1.編寫正確的正則表達式;
2.根據正則表達式匹配出所有的表情名稱,把他們在字符串中的位置用數組記錄;
3.根據記錄位置的數組,截斷字符串;
4.組成相應的數組。
代碼:
形如“[微笑]”的字符串形式的正則表達式爲:
代碼中的正則方法:
注意後面有第二個參數‘g’,它表示全局匹配,把字符串中所有能匹配的都匹配一遍。
如果沒有第二個參數,則會找到第一個匹配的字符串之後就停止。
export function stringToContentArray(text) {
//text = "wwww[微笑]eeee[鬼臉]asdfasfasd[大笑]w222";
var regex = new RegExp('\\[[a-zA-Z0-9\\/\\u4e00-\\u9fa5]+\\]', 'g');
var contentArray = [];
var regArray = text.match(regex);
console.log(regArray);
if (regArray === null) {
contentArray.push({"Content" : text});
return contentArray;
}
var indexArray = [];
var pos = text.indexOf(regArray[0]);//頭
for (let i = 1; i < regArray.length; i++) {
indexArray.push(pos);
pos = text.indexOf(regArray[i],pos + 1);
}
indexArray.push(pos);//尾
console.log("indexArray = ",indexArray);
for (let i=0; i<indexArray.length; i++) {
if (indexArray[i] === 0) {//一開始就是表情
contentArray.push({"Resources" : EMOTION_GIF_NAME[regArray[i]],attr: {Type:"0"}});
} else {
if (i === 0) {
contentArray.push({"Content" : text.substr(0,indexArray[i])});
} else {
if (indexArray[i] - indexArray[i-1] - regArray[i-1].length > 0) {//兩個表情相鄰,中間不加content
contentArray.push({"Content" : text.substr(indexArray[i-1] + regArray[i-1].length,indexArray[i] - indexArray[i-1] - regArray[i-1].length)});
}
}
contentArray.push({"Resources" : EMOTION_GIF_NAME[regArray[i]],attr: {Type:"0"}});
}
}
let lastLocation = indexArray[indexArray.length - 1] + regArray[regArray.length - 1].length;
if (text.length > lastLocation) {
contentArray.push({"Content": text.substr(lastLocation,text.length - lastLocation)});
}
return contentArray;
}
形如“[微笑]”的字符串形式的正則表達式爲:
'\\[[a-zA-Z0-9\\/\\u4e00-\\u9fa5]+\\]'
var regex =new RegExp('\\[[a-zA-Z0-9\\/\\u4e00-\\u9fa5]+\\]','g');
如果沒有第二個參數,則會找到第一個匹配的字符串之後就停止。
根據數組排列組件
此步驟方法較多,可以用數組的map實現。貼上一段代碼
render() {
return (
<View style={[styles[this.props.position].container, this.props.containerStyle[this.props.position]]}>
{
this.props.currentMessage.contentArray.map((content, i) => {
if (content["Content"] != null || !EMOTION_PATH[content["Resources"].toLowerCase()]) {//文本
return (
<Text
key={i}
style={[styles[this.props.position].text, this.props.textStyle[this.props.position],{textAlign:'left'}]}
adjustsFontSizeToFit={true}
minimumFontScale={1.0}
>
{content["Content"] != null? content["Content"] : content["Resources"]}
</Text>
);
} else if (content["Resources"] != null) {//emoji
let w = this._emojiWidth;
return (
<Image
key={i}
style={[customStyle.emoji, { "width": w, "height": w }]}
source={EMOTION_PATH[content["Resources"].toLowerCase()]}
>
</Image>
);
}
})
}
</View>
);
}
上面代碼中有EMOTION_PATH[content["Resources"],
這句是把表情名稱轉換成表情的圖片路徑,EMOTION_PATH是一個對象,形如:
總結
這個方法存在一個問題,一個表情用一個Image控件顯示,如果表情太多則會影響性能。經過實測,50個表情顯示,對ListView的滑動流程性不會產生影響,只是渲染的時候稍微有點慢。可接受。
以上是現在的解決方案,方法略顯笨拙。如果哪位童鞋有更好的方法,請在下面留言。