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.android.estar.component.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)
如需转载 请注明转载出处 http://blog.csdn.net/qiangainannan/article/details/38349129
android qq聊天动态表情的实现相关推荐
- android qq聊天背景图片,手机qq聊天背景图片【突破指南】
很多小伙伴都遇到过手机qq聊天背景图片的困惑吧,一些朋友看过网上零散的手机qq聊天背景图片的处理方法,并没有完完全全明白手机qq聊天背景图片是如何解决的,今天小编准备了简单的解决办法,只需要按照1:打 ...
- WPF仿QQ聊天框表情文字混排实现
这个需求前后也历经了多版,上两张图给各位同学感受一下,第一张是初版,第二张是稳定不最终版~ 图中分别有文件.文本+表情.纯文本的展示,对于同一个list不同的展示形式,很明显,应该用多个DataTem ...
- php聊天动图,如何制作微信动态表情 真人qq聊天搞笑动态图片 多人摇头娃娃在线制作...
呼呼,总算是完成了哦,最后就是保存摇头娃娃啦,单击"另存为"按钮-"保存为单独文件",在随即弹出的保存页面里面,设置图片的保存路径并为图片命名,最后选择&quo ...
- android仿qq聊天项目点评,android 实现qq聊天对话界面效果
[实例简介] Android UI[android 仿微信.QQ聊天,带表情,可翻页,带翻页拖动缓冲] 博客介绍http://blog.csdn.net/lnb333666/article/detai ...
- 怎么制作gif动态图 QQ动态表情包怎么制作
在平时的聊天中经常会使用到GIF动图,不仅仅可以缓解气氛,还很有趣,那这些动态图是如何制作的呢?没有想象的那么难,今天来看看怎么制作的吧! 1.先准备好素材,要制作什么样的动图,可以是图片也可以是视频 ...
- Android特效专辑(六)——仿QQ聊天撒花特效,无形装逼,最为致命
Android特效专辑(六)--仿QQ聊天撒花特效,无形装逼,最为致命 我的关于特效的专辑已经在CSDN上申请了一个专栏--http://blog.csdn.net/column/details/li ...
- 类似qq聊天表情实现
android qq上有这样的功能,点击表情,然后输入框EditText上显示表情,博客,论坛上也有这样的功能.有些是显示qq表情的代表符号洳:":()"这样的符号 先是从网上搜索 ...
- android气泡聊天消息背景,Android使用贝塞尔曲线仿QQ聊天消息气泡拖拽效果
本文实例为大家分享了Android仿QQ聊天消息气泡拖拽效果展示的具体代码,供大家参考,具体内容如下 先画圆,都会吧.代码如下: public class Bezier extends View { ...
- 微信android版发布动态图片,太“炸”了!微信重大玩法更新!微信表情会动了!附安卓用户更新入口→...
在我爱大乐昌微信对话框 发生"新版"二字 昨天(1月21日) 微信十周年之日 8.0.0 iOS版重磅上线 一口气更新了多个重要功能 包括动态表情.个人状态. 浮窗调整.创作音乐视 ...
- Android源码解析--SwipeMenuListView仿QQ聊天左滑
版权声明:本文为博主原创文章,转载请标明出处. https://blog.csdn.net/lyhhj/article/details/50612714 绪论: 好久没写博客了,最近比较懒,不想写博客 ...
最新文章
- Linux的概念与体系 2. Linux文件管理(转载)
- 支持向量机:Numerical Optimization
- mysql not in优化_MySQL性能优化 — 实践篇2
- 数据分析、机器学习必读书,李航《统计学习方法》发布算法推导视频啦!(附作业讲解)...
- C++求三位数的水仙花数
- Splunk学习心得
- MongoDB+java+spirng+morphia
- c语言中的数组二分法排序程序,#C语言#二分法查找有序数组
- linux进程控制-exit()
- ajax请求后台php数据时查看报错parse error
- 从零开始配置 vim(4)——键盘映射的一些技巧
- RCC BUCK变压器设计
- python——获取矩形四个角点的坐标
- navicat 导入dmp文件
- php接入北斗定位,手机如何连接北斗卫星?
- 滴滴跨端框架 Chameleon 正式支持快应用
- Java调用python脚本,进程长时间卡住问题
- Vue中图片实现毛玻璃效果
- 支付宝存漏洞?这10招保护个人信息赶紧保存起来!
- python 操作微信_利用 Python 实现微信半自动化操作