一、需求準備
在react native的類中實現可以解析多種字符格式的內容并放入到指定文本中。效果圖如下:
二、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就實現啦,看下效果咯~