前言
在IM通讯软件中,基本上都会有emoji表情功能。聊天气泡中要显示文字和emoji表情的混排(下图所示),在原生iOS开发时,可以用富文本NSAttributedString实现,安卓中用SpannableString实现。当用到React-Native来开发这个功能的时候,貌似没有直接的现成的实现方案。经过一番努力,这个功能已经在项目中实现 ,在此记录。
思路
假设有一条信息在输入框是这样的:“假设[微笑]有一条信息[龇牙]是这样的”
[微笑],[龇牙]都是emoji表情的名称
处理步骤如下:
1.运用正则表达式将表情名称识别出来,然后把这条文本截成数组:
[
{content:假设},
{resource:[微笑]},
{content:有一条信息},
{resource:[龇牙]},
{content:是这样的}
]
2.根据这个数组进行组件排列,key是content的话放置Text,key是resources的话放置Image,一字排开。
字符串转成数组
1.编写正确的正则表达式;
2.根据正则表达式匹配出所有的表情名称,把他们在字符串中的位置用数组记录;
3.根据记录位置的数组,截断字符串;
4.组成相应的数组。
代码:
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; }
根据数组排列组件
analyseComponent() { if (!this.props.currentMessage.hasOwnProperty("contentArray")) { this.props.currentMessage.contentArray = MessageProvider.stringToContentArray(this.props.currentMessage.text); } let objArray = []; for (let subObj of this.props.currentMessage.contentArray) { objArray.push(this.renderSubObj(subObj)); } return( <View style = {styles[this.props.position].container}> {objArray} </View> ); } renderSubObj(subObj) { if (subObj["Content"] != null) {//文本 return ( <ParsedText style={[styles[this.props.position].text,this.props.textStyle[this.props.position]]} parse={[ {type: 'url',style: StyleSheet.flatten([styles[this.props.position].link,this.props.linkStyle[this.props.position]]),onPress: this.onUrlPress},{type: 'phone',onPress: this.onPhonePress},{type: 'email',onPress: this.onEmailPress},]} > {subObj["Content"]} </ParsedText> ); } else if (subObj["Resources"] != null) {//emoji return ( <Image style = {styleEmoji.emoji} source={EMOTION_PATH[subObj["Resources"]]} > </Image> ); } } render() { console.log("----------contentArray:" + this.props.currentMessage.contentArray); return ( <View style={[styles[this.props.position].container,this.props.containerStyle[this.props.position]]}> {this.analyseComponent()} </View> ); } }