我們在進行qq聊天的時候發送表情,但這些表情都是并不是靜态的,更多的是動态圖,gif圖,那麼如何在android用戶端顯示動态gif圖呢。 在github上找到了這樣一種方
我們在進行qq聊天的時候發送表情,,但這些表情都是并不是靜态的,更多的是動态圖,gif圖,那麼如何在android用戶端顯示動态gif圖呢。
在github上找到了這樣一種方法,Github位址https://github.com/TracyZhangLei/android-gif-demo
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SOkNGOkRGM2gzYiFjNyEzYjFGMjBzMxQDOhRmM3Q2Ym9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
由于我是截圖,是以看不到動态效果,大家可以自己下載下傳看一下。
我們首先來看一下該開源項目的代碼。該開源項目主要是通過自定義一個Adapter-------chatAdapter,在ChatAdapter每一條的setText屬性中使用了自定義的方法convertNormalStringToSpannableString
convertNormalStringToSpannableString方法的傳回值是SpannableString
我們首先來了解一下什麼是SpannableString
TextView通常用來顯示普通文本,但是有時候需要對其中某些文本進行樣式、事件方面的設定。Android系統通過SpannableString類來對指定文本進行相關處理,也就是說我們想要實作文字加動态表情的實作就要通過SpannableString這個類來實作。
private SpannableString convertNormalStringToSpannableString(String message , final TextView tv) {
SpannableString value = SpannableString.valueOf(message);
Matcher localMatcher = EMOTION_URL.matcher(value);
while (localMatcher.find()) {
String str2 = localMatcher.group(0);
int k = localMatcher.start();
int m = localMatcher.end();
if (m - k < 8) {
int face = fm.getFaceId(str2);
if(-1!=face){//wrapping with weakReference
WeakReference localImageSpanRef = new WeakReference(new AnimatedImageSpan(new AnimatedGifDrawable(cxt.getResources().openRawResource(face), new AnimatedGifDrawable.UpdateListener() {
@Override
public void update() {//update the textview
tv.postInvalidate();
}
})));
value.setSpan(localImageSpanRef.get(), k, m, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
return value;
}
首先将我們傳入的message轉化成SpannableString類,然後看一下傳入的值是否符合我們一開始寫好的正規表達式EMOTION_URL
private Pattern EMOTION_URL = Pattern.compile("\\[(\\S+?)\\]");
如果符合的話 我們取group(0)
附:group是針對()來說的,group(0)就是指的整個串,group(1)指的是第一個括号裡的東西,group(2)指的第二個括号裡的東西。
子表達式和起始位置和結束位置的差小于8,也就是符合我們的要求。調用FaceManager中的getFaceId方法
public int getFaceId(String faceStr){
if(mFaceMap.containsKey(faceStr)){
return mFaceMap.get(faceStr);
}
return -1;
}
找到我們用Map進行存儲的表情
如果表情存在的話利用一個弱引用(WeakReference)把自定義的AnimatedImageSpan進行處理,使AnimatedImageSpan不那麼的消耗記憶體,在UpdateListener中利用postInvalidate重新整理界面。最後把SpannableString的setSpan方法,三個參數分别是要放進去的span ,起始位置,結束位置,flag标志。
關于flag:
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, 這是在 setSpan 時需要指定的 flag,它是用來辨別在 Span 範圍内的文本前後輸入新的字元時是否把它們也應用這個效果。分别有 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE(前後都不包括)、 Spanned.SPAN_INCLUSIVE_EXCLUSIVE(前面包括,後面不包括)、 Spanned.SPAN_EXCLUSIVE_INCLUSIVE(前面不包括,後面包括)、
Spanned.SPAN_INCLUSIVE_INCLUSIVE(前後都包括)。
最後将SpannableString傳回,實作動态圖文混排。
關于自定義的AnimatedImageSpan如下:
public class AnimatedImageSpan extends DynamicDrawableSpan {
private Drawable mDrawable;
public AnimatedImageSpan(Drawable d) {
super();
mDrawable = d;
// Use handler for 'ticks' to proceed to next frame
final Handler mHandler = new Handler();
mHandler.post(new Runnable() {
public void run() {
((AnimatedGifDrawable)mDrawable).nextFrame();
// Set next with a delay depending on the duration for this frame
mHandler.postDelayed(this, ((AnimatedGifDrawable)mDrawable).getFrameDuration());
}
});
}
@Override
public Drawable getDrawable() {
return ((AnimatedGifDrawable)mDrawable).getDrawable();
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
Drawable d = getDrawable();
Rect rect = d.getBounds();
if (fm != null) {
fm.ascent = -rect.bottom;
fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
}
return rect.right;
}
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
Drawable b = getDrawable();
canvas.save();
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
}
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
}
如果大家有疑問,歡迎加入QQ群: (452379712),與傑瑞教育進階工程師線上互動