1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > android qq聊天动态表情的实现

android qq聊天动态表情的实现

时间:2024-01-14 23:07:04

相关推荐

android qq聊天动态表情的实现

公司的任务要求实现类似qq聊天动态表情,在网上找了不少资料,基本都是TextView加SpannableString来显示gif,通过自定义一个TextView,启动线程更新图片。由于聊天时可输入多个表情,gif图片各帧时间的不同,就导致了显示多个gif图片很别扭的问题,而且每个TextView就开一个线程也不实际。研究了一下qq聊天,发现所有表情相同的gif图片显示的都是相同的帧。由此我认为qq是通过一个线程来控制所有的TextView更新gif的。

在这里我使用的GifView的源码来解析gif图片,并修改了其中的GifView,使其继承TextView来达到要求,其他源码不变。源码忘了是在哪下载的,还带有demo,非常感谢好心人。

上代码 GifTextView

import java.io.InputStream;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import com.mcds.app.ponent.MySpan;

import com.mcds.app.android.estar.util.FaceUtility;

import android.content.Context;

import android.content.res.Resources;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.drawable.BitmapDrawable;

import android.text.Spannable;

import android.text.SpannableString;

import android.text.style.ImageSpan;

import android.util.AttributeSet;

import android.widget.TextView;

public class GifTextView extends TextView implements GifAction {

/** gif解码器 */

private GifDecoder gifDecoder = null;

private String input="";//需要显示的文字

private SpannableString spannable;

BitmapDrawable drawable;//需要添加的图片资源

private long time;//记录post到主线程的时间

private class Info {//保存input中需要添加的SpannableString的信息

String decodername;//gif图片名称,根据次名称去bitmapMap去图片

int start;//开始位置

int end;//结束位置

}

//保存需要更新的GifTextView,简单的检查者模式,退出activity时清空,新建GifTextView时添加到此list中

public static ArrayList<GifTextView> textViewList = new ArrayList<GifTextView>();

//保存gifDecoder的信息,使用gif图片的名称作key,只添加,不删除,

public static HashMap<String, DecoderInfo> decodermap = new HashMap<String, DecoderInfo>();

//保存更新后drawable的信息,使用gif图片的名称作key,GifTextView更新时,根据key取drawable

public static HashMap<String, Bitmap> bitmapMap = new HashMap<String, Bitmap>();

//保存需要添加的SpannableString的list

private ArrayList<Info> infoList = new ArrayList<GifTextView.Info>();

private Info info ;

private ImageSpan span;

public GifTextView(Context context) {

super(context);

textViewList.add(this);

}

public GifTextView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

textViewList.add(this);

}

public GifTextView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

textViewList.add(this);

}

/**

* 设置图片,记录开始结束信息,开始解码

*

* @param is

* 要设置的图片

*/

private void setGifDecoderImage(String resId, int start, int end) {

Info info = new Info();

info.decodername = resId;

info.start = start;

info.end = end;

infoList.add(info);

if (!decodermap.containsKey(resId)) {//主要是为了节省资源,如果已经包含,不在开启线程

Resources r = getContext().getResources();

//FaceUtility内主要是一个hash表,key是gif图片的名字,value是图片的资源id

InputStream is = r.openRawResource(FaceUtility.getInstance().getBigItem(resId));

gifDecoder = new GifDecoder(is, this);

gifDecoder.start();

DecoderInfo decoderInfo = new DecoderInfo();

decoderInfo.decoder = gifDecoder;

decodermap.put(resId, decoderInfo);

//这里是为了防止gifdecoder解析失败所保存,如果解析失败,则直接显示静态图片

Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), FaceUtility.getInstance().getBigItem(resId));

bitmapMap.put(resId, bitmap);

}

}

/**

* 设置文字

*/

public void setSpanText(String input) {

infoList.clear();

this.input = input;

if (input == null) {

input = "";

}

String contentinfo = input;

int staindex = contentinfo.indexOf("[");

List<Integer> arraysta = new ArrayList<Integer>();

while (staindex != -1) {

arraysta.add(staindex);

staindex = contentinfo.indexOf("[", staindex + 1);

}

int endindex = contentinfo.indexOf("]");

List<Integer> arrayend = new ArrayList<Integer>();

while (endindex != -1) {

arrayend.add(endindex + 1);

endindex = contentinfo.indexOf("]", endindex + 1);

}

String[] array_start = input.split("\\[");

for (int i = 0; i < array_start.length - 1; i++) {

String[] array_end = array_start[i + 1].split("\\]");

if (FaceUtility.getInstance().getBigItem("["+array_end[0]+"]") != null) {

if (arraysta.size() - 1 >= i && arrayend.size() - 1 >= i) {

setGifDecoderImage("["+array_end[0]+"]", arraysta.get(i), arrayend.get(i));

}

}

}

show1();//第一次显示

}

public void parseOk(boolean parseStatus, int frameIndex) {

}

public void show() {

//设置两次post的时间,post主线程过快相当耗资源,这里自己设置

//如果没有需要设置的spannablestring或者需要设置太多,直接返回

if(System.currentTimeMillis()-time<500||infoList.size()==0||infoList.size()>7){

return;

}

time = System.currentTimeMillis();

spannable = new SpannableString(input);

int length = infoList.size();

for (int i = 0; i < length; i++) {

info = infoList.get(i);

drawable = new BitmapDrawable(getContext().getResources(), bitmapMap.get(info.decodername));

drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

span = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);

spannable.setSpan(span, info.start, info.end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

}

//spannable = MySpan.URLSpan(spannable, getContext(), true);

//post到主线程,只有在主线程中才可对ui进行操作

GifTextView.this.post(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

GifTextView.this.setText(spannable);

}

});

}

private void show1(){

spannable = new SpannableString(input);

for (int i = 0; i < infoList.size(); i++) {

info = infoList.get(i);

drawable = new BitmapDrawable(getContext().getResources(), bitmapMap.get(info.decodername));

drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());

span = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);

spannable.setSpan(span, info.start, info.end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);

}

spannable = MySpan.URLSpan(spannable, getContext(), true);

GifTextView.this.post(new Runnable() {

@Override

public void run() {

// TODO Auto-generated method stub

GifTextView.this.setText(spannable);

}

});

}

}

线程MyThread 此线程我是设置为静态的,只要app启动,就会运行,虽然有点耗资源,现在没啥好办法。

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import com.mcds.app.android.estar.MainApplication;

import android.graphics.Bitmap;

import android.os.SystemClock;

import android.util.Log;

public class MyThread extends Thread {

public boolean stop = true;//线程暂停

public boolean destroy = false;//线程结束

private boolean animation = false;//是否有gif更新

private HashMap<String, DecoderInfo> map = new HashMap<String, DecoderInfo>();//decodermap的拷贝,解决同步错误

private ArrayList<GifTextView> textViewlist = new ArrayList<GifTextView>();//textViewList的拷贝,解决同步错误

String key;

DecoderInfo val;

@Override

public void run() {

// TODO Auto-generated method stub

super.run();

while (!destroy) {

if (!stop) {

if (!map.equals(GifTextView.decodermap)) {//如果decodermap变化,重新拷贝

map.clear();

map.putAll(GifTextView.decodermap);

}

Iterator iter = map.entrySet().iterator();

while (iter.hasNext()) {

Map.Entry entry = (Map.Entry) iter.next();

key = (String) entry.getKey();

val = (DecoderInfo) entry.getValue();

if (val.decoder != null && val.decoder.getFrameCount() > 1) {

if (System.currentTimeMillis() - val.time > val.delay) {

GifFrame frame = val.decoder.next();

val.time = System.currentTimeMillis();

val.delay = frame.delay;

putBitmap(key, frame.image);

}

}

}

if (animation) {

if(!textViewlist.equals(GifTextView.textViewList)){//textViewList,重新拷贝

textViewlist.clear();

textViewlist.addAll( GifTextView.textViewList);

}

for (GifTextView view :textViewlist) {//监察者模式,出现变化,通知GifTextView更新

view.show();

}

animation = false;

}

SystemClock.sleep(100);//设置循环时间,这里不要设置太长,会打乱gif图的效果

} else {

SystemClock.sleep(2000);//设置线程睡眠时间,

}

}

}

private void putBitmap(String name, Bitmap bitmap) {

animation = true;

//bitmapMap更新drawable,以便GifTextView调用

if (GifTextView.bitmapMap.containsKey(name)) {

GifTextView.bitmapMap.remove(name);

}

GifTextView.bitmapMap.put(name, bitmap);

}

}

public class DecoderInfo {//保存decoder信息

GifDecoder decoder;

long time = 0;//记录上次图片更新时间

long delay = 0;//本次帧间隔时间

}

在activity pause和resume时,分别把MyThread的stop值设置为true和false,退出时把destroy设置为true,节省资源。还有在列表进行滚动时,最好设置MyThread的stop值,不会太卡。如果感觉卡,调节GifTextView的post间隔值。

这是我的一点见解,如果有大神有更好的实现方式,请告知,不胜感激。

作者:qiangainannan(csdn)

如需转载 请注明转载出处/qiangainannan/article/details/38349129

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。