TextView中显示gif表情文件

来源:互联网 发布:工可网软件 编辑:程序博客网 时间:2024/05/16 19:13

首先,我以下代码,关键部分是借鉴到Stack Overflow中的一个牛人的回复,所以这里并不属于原创,只是自己整合写了一个demo

刚开始时借鉴GifView.jar 包里面的内容,可是,我想以后如果要做成类似QQ那种随意插入表情那种,这里就用不上,所以想借鉴那里面的Gifdecoder类的解析器,可是最后在

GifDecoder decoder = new GifDecoder(source, action);

报出GifDecoder decoderinvocationtargetexception,我自己确实无法解决这个问题。所以就还是借鉴网上的那种gif解码器,代码比较多,我会在后面贴出

这里有两个比较重要的自定义类,GifDrawable.java和AnimatedImageSpan.java,代码如下

package com.example.android_widget_byself_widget;import java.io.InputStream;import com.ant.liao.GifAction;import com.ant.liao.GifDecoder;import com.example.android_helper.GifHelper;import android.content.Context;import android.graphics.Bitmap;import android.graphics.drawable.AnimationDrawable;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.util.Log;public class GifDrawable extends AnimationDrawable {private int mCurrentIndex = 0;private UpdateListener mListener;public GifDrawable(Context context, InputStream source,UpdateListener listener) {mListener = listener;//class MyGifAction implements GifAction {////@Override//public void parseOk(boolean parseStatus, int frameIndex) {//// TODO Auto-generated method stub//if (parseStatus && frameIndex == -1) {//Log.d("GifDrawable", "解码成功");//}//}////}////MyGifAction action = new MyGifAction();//GifDecoder decoder = new GifDecoder(source, action);GifHelper decoder=new GifHelper();decoder.read(source);// 遍历gif图像里面的每一帧,放进animation frame中for (int i = 0; i < decoder.getFrameCount(); i++) {Bitmap bitmap = decoder.getFrame(i);BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);// 为每一帧设置边界drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());addFrame(drawable, decoder.getDelay(i));if (i == 0) {// 为容器设置一个边界setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());}}}/** * 继续下一帧,同时通知监听器 */public void nextFrame() {// 当循环到最后一帧时,索引就会为0,注意索引比帧的数量小1mCurrentIndex = (mCurrentIndex + 1) % getNumberOfFrames();if (mListener != null) {mListener.update();}}/** * 返回当前帧的显示时间 *  * @return */public int getFrameDuration() {return getDuration(mCurrentIndex);}/** * 返回当前帧的图片 *  * @return */public Drawable getDrawable() {return getFrame(mCurrentIndex);}/** * 该接口通知监听器更新/重绘界面 *  * @author moon *  */public interface UpdateListener {void update();}public void setUpdateListener(UpdateListener listener) {this.mListener = listener;}}
AnimatedImageSpan.java

package com.example.android_widget_byself_widget;import android.graphics.Canvas;import android.graphics.Paint;import android.graphics.Rect;import android.graphics.Paint.FontMetricsInt;import android.graphics.drawable.Drawable;import android.os.Handler;import android.text.style.DynamicDrawableSpan;public class AnimatedImageSpan extends DynamicDrawableSpan {private Drawable mDrawable;public AnimatedImageSpan(Drawable d) {super();mDrawable = d;// 用handler 通知继续下一帧final Handler mHandler = new Handler();mHandler.post(new Runnable() {@Overridepublic void run() {((GifDrawable) mDrawable).nextFrame();// 设置下一个的延迟取决于当前帧的持续时间mHandler.postDelayed(this,((GifDrawable) mDrawable).getFrameDuration());}});}@Overridepublic Drawable getDrawable() {return ((GifDrawable) mDrawable).getDrawable();}/** * 代码跟父类代码相似,就是getCachedDrawable()替换成getDrawable(),因为前者里面的图片是WeakReference, * 容易被gc回收,所以这里要避免这个问题 */@Overridepublic int getSize(Paint paint, CharSequence text, int start, int end,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;}/** * 代码跟父类代码相似,就是getCachedDrawable()替换成getDrawable(),因为前者里面的图片是WeakReference, * 容易被gc回收,所以这里要避免这个问题 */@Overridepublic void draw(Canvas canvas, CharSequence text, int start, int end,float x, int top, int y, int bottom, Paint paint) {// TODO Auto-generated method stubDrawable 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();}}
ExpressionUtil.java 类里面是通过正则表达式来匹配表情的,然后根据反射机制获得gif的资源id,然后在spannableString中进行替换成相应的gif文件

这里用到的是dealExpression_gif()方法,dealExpression()是匹配静态图片的。这里用Hashtable<Integer, GifDrawable> cache储存已经解析过的

gif文件,提高效率。

package com.example.android_helper;import java.lang.reflect.Field;import java.util.Hashtable;import java.util.regex.Matcher;import java.util.regex.Pattern;import com.example.android_widget_byself.R;import com.example.android_widget_byself_widget.AnimatedImageSpan;import com.example.android_widget_byself_widget.GifDrawable;import com.example.android_widget_byself_widget.GifDrawable.UpdateListener;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.text.Spannable;import android.text.SpannableString;import android.text.style.ImageSpan;import android.util.Log;public class ExpressionUtil {/** * 对spannableString经行正则判断,如果匹配成功,就用表情图片替代字符串 *  * @param context * @param spannableString * @param patten * @param start * @throws NoSuchFieldException * @throws NumberFormatException * @throws IllegalAccessException * @throws IllegalArgumentException */public static void dealExpression(Context context,SpannableString spannableString, Pattern patten, int start)throws NoSuchFieldException, NumberFormatException,IllegalAccessException, IllegalArgumentException {Matcher matcher = patten.matcher(spannableString);while (matcher.find()) {String key = matcher.group();if (matcher.start() < start) {continue;}// String imagekey = "b"// + key.substring(key.indexOf("#") + 1, key.lastIndexOf("#"));String imagekey = "f"+ key.substring(key.indexOf("#") + 1, key.lastIndexOf("#"));Field field = R.drawable.class.getDeclaredField(imagekey);int resId = Integer.parseInt(field.get(null).toString());if (resId != 0) {Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId);ImageSpan imageSpan = new ImageSpan(context, bitmap);int end = matcher.start() + key.length();spannableString.setSpan(imageSpan, matcher.start(), end,Spannable.SPAN_INCLUSIVE_EXCLUSIVE);if (end < matcher.end()) {}}}}/** * 得到一个SpanableString对象,通过传入的字符串,并进行正则判断 *  * @param context * @param string * @param zhengze * @return */public static SpannableString getExpressionString(Context context,String string, String zhengze,Hashtable<Integer, GifDrawable> cache, UpdateListener listener) {SpannableString spannableString = new SpannableString(string);Pattern pattern = Pattern.compile(zhengze, Pattern.CASE_INSENSITIVE);try {// dealExpression(context, spannableString, pattern, 0);dealExpression_gif(context, spannableString, pattern, 0, cache,listener);} catch (Exception e) {Log.e("dealExpression", e.toString());}return spannableString;}public static void dealExpression_gif(Context context,SpannableString spannableString, Pattern patten, int start,Hashtable<Integer, GifDrawable> cache, UpdateListener listener)throws NoSuchFieldException, NumberFormatException,IllegalAccessException, IllegalArgumentException {Matcher matcher = patten.matcher(spannableString);while (matcher.find()) {String key = matcher.group();String imagkey = "f"+ key.substring(key.indexOf("#") + 1, key.lastIndexOf("#"));Field field = R.drawable.class.getDeclaredField(imagkey);int resId = Integer.parseInt(field.get(null).toString());if (resId != 0) {GifDrawable gifDrawable = null;if (cache.containsKey(resId)) {gifDrawable = cache.get(resId);} else {gifDrawable = new GifDrawable(context, context.getResources().openRawResource(resId), listener);cache.put(resId, gifDrawable);}AnimatedImageSpan imageSpan = new AnimatedImageSpan(gifDrawable);spannableString.setSpan(imageSpan, matcher.start(),matcher.start() + key.length(),Spannable.SPAN_INCLUSIVE_EXCLUSIVE);}}}}
Constant.java 里面的正则表达式

public static final String Reg_expression_gif = "\\{53f\\#[0][0-1][0-9]\\#\\}";
MainActivity.java

package com.example.android_widget_byself;import java.util.Hashtable;import com.example.android_helper.Constant;import com.example.android_helper.ExpressionUtil;import com.example.android_widget_byself_widget.DialogByme;import com.example.android_widget_byself_widget.EdittextCanClear;import com.example.android_widget_byself_widget.GifDrawable;import com.example.android_widget_byself_widget.GifDrawable.UpdateListener;import com.example.android_widget_byself_widget.ListViewCanRefresh;import com.example.android_widget_byself_widget.ListViewCanRefresh.OnLastItemVisibleListener;import com.example.android_widget_byself_widget.ListViewCanRefresh.OnRefreshListener;import android.os.Bundle;import android.os.Message;import android.app.Activity;import android.text.SpannableString;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.ImageView;import android.widget.TextView;public class MainActivity extends Activity {String[] emoj_gif = new String[] { "{53f#001#}", "{53f#010#}","{53f#004#}", "{53f#007#}", "{53f#008#}" };Hashtable<Integer, GifDrawable> cache = new Hashtable<Integer, GifDrawable>();MyUpdateListener listener = new MyUpdateListener();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);expression_text = (TextView) findViewById(R.id.expression_text);// String test = "您好," + emoj[0] + ",呵呵," + emoj[1] + emoj[2] + emoj[4];String test_gif = "您好," + emoj_gif[0] + ",呵呵," + emoj_gif[1]+ emoj_gif[2] + emoj_gif[4];// SpannableString spannableString = ExpressionUtil.getExpressionString(// MainActivity.this, test_gif, Constant.Reg_expression_gif);SpannableString spannableString = ExpressionUtil.getExpressionString(this, test_gif, Constant.Reg_expression_gif, cache, listener);expression_text.setText(spannableString);class MyUpdateListener implements UpdateListener {@Overridepublic void update() {// TODO Auto-generated method stubexpression_text.postInvalidate();}}}

最后贴出Gifhelper.java这个解析类的代码

package com.example.android_helper;import java.io.InputStream;import java.util.Vector;import android.graphics.Bitmap;import android.graphics.Bitmap.Config;//Handler for read & extract Bitmap from *.gifpublic class GifHelper {// to define some error typepublic static final int STATUS_OK = 0;public static final int STATUS_FORMAT_ERROR = 1;public static final int STATUS_OPEN_ERROR = 2;protected int status;protected InputStream in;protected int width; // full image widthprotected int height; // full image heightprotected boolean gctFlag; // global color table usedprotected int gctSize; // size of global color tableprotected int loopCount = 1; // iterations; 0 = repeat foreverprotected int[] gct; // global color tableprotected int[] lct; // local color tableprotected int[] act; // active color tableprotected int bgIndex; // background color indexprotected int bgColor; // background colorprotected int lastBgColor; // previous bg colorprotected int pixelAspect; // pixel aspect ratioprotected boolean lctFlag; // local color table flagprotected boolean interlace; // interlace flagprotected int lctSize; // local color table sizeprotected int ix, iy, iw, ih; // current image rectangleprotected int lrx, lry, lrw, lrh;protected Bitmap image; // current frameprotected Bitmap lastImage; // previous frameprotected int frameindex = 0;public int getFrameindex() {return frameindex;}public void setFrameindex(int frameindex) {this.frameindex = frameindex;if (frameindex > frames.size() - 1) {frameindex = 0;}}protected byte[] block = new byte[256]; // current data blockprotected int blockSize = 0; // block size// last graphic control extension infoprotected int dispose = 0;// 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prevprotected int lastDispose = 0;protected boolean transparency = false; // use transparent colorprotected int delay = 0; // delay in millisecondsprotected int transIndex; // transparent color indexprotected static final int MaxStackSize = 4096;// max decoder pixel stack size// LZW decoder working arraysprotected short[] prefix;protected byte[] suffix;protected byte[] pixelStack;protected byte[] pixels;protected Vector<GifFrame> frames; // frames read from current fileprotected int frameCount;// to get its Width / Heightpublic int getWidth() {return width;}public int getHeigh() {return height;}/** * Gets display duration for specified frame. * 获得当前帧的延迟时间 * @param n *            int index of frame * @return delay in milliseconds */public int getDelay(int n) {delay = -1;if ((n >= 0) && (n < frameCount)) {delay = ((GifFrame) frames.elementAt(n)).delay;}return delay;}public int getFrameCount() {return frameCount;}/** * 获取首帧的图片 */public Bitmap getImage() {return getFrame(0);}public int getLoopCount() {return loopCount;}protected void setPixels() {int[] dest = new int[width * height];// fill in starting image contents based on last image's dispose codeif (lastDispose > 0) {if (lastDispose == 3) {// use image before lastint n = frameCount - 2;if (n > 0) {lastImage = getFrame(n - 1);} else {lastImage = null;}}if (lastImage != null) {lastImage.getPixels(dest, 0, width, 0, 0, width, height);// copy pixelsif (lastDispose == 2) {// fill last image rect area with background colorint c = 0;if (!transparency) {c = lastBgColor;}for (int i = 0; i < lrh; i++) {int n1 = (lry + i) * width + lrx;int n2 = n1 + lrw;for (int k = n1; k < n2; k++) {dest[k] = c;}}}}}// copy each source line to the appropriate place in the destinationint pass = 1;int inc = 8;int iline = 0;for (int i = 0; i < ih; i++) {int line = i;if (interlace) {if (iline >= ih) {pass++;switch (pass) {case 2:iline = 4;break;case 3:iline = 2;inc = 4;break;case 4:iline = 1;inc = 2;}}line = iline;iline += inc;}line += iy;if (line < height) {int k = line * width;int dx = k + ix; // start of line in destint dlim = dx + iw; // end of dest lineif ((k + width) < dlim) {dlim = k + width; // past dest edge}int sx = i * iw; // start of line in sourcewhile (dx < dlim) {// map color and insert in destinationint index = ((int) pixels[sx++]) & 0xff;int c = act[index];if (c != 0) {dest[dx] = c;}dx++;}}}image = Bitmap.createBitmap(dest, width, height, Config.ARGB_4444);}/** * 获得索引帧的图片 *  * @param n * @return */public Bitmap getFrame(int n) {Bitmap im = null;if ((n >= 0) && (n < frameCount)) {im = ((GifFrame) frames.elementAt(n)).image;}return im;}/** * 获得下一帧的图片 *  * @return */public Bitmap nextBitmap() {frameindex++;if (frameindex > frames.size() - 1) {frameindex = 0;}return ((GifFrame) frames.elementAt(frameindex)).image;}public int nextDelay() {return ((GifFrame) frames.elementAt(frameindex)).delay;}// to read & parse all *.gif stream/** * 这里开始读取输入流,后面进行解析 *  * @param is * @return */public int read(InputStream is) {init();if (is != null) {in = is;readHeader();if (!err()) {readContents();if (frameCount < 0) {status = STATUS_FORMAT_ERROR;}}} else {status = STATUS_OPEN_ERROR;}try {is.close();} catch (Exception e) {e.printStackTrace();}return status;}protected void decodeImageData() {int NullCode = -1;int npix = iw * ih;int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;if ((pixels == null) || (pixels.length < npix)) {pixels = new byte[npix]; // allocate new pixel array}if (prefix == null) {prefix = new short[MaxStackSize];}if (suffix == null) {suffix = new byte[MaxStackSize];}if (pixelStack == null) {pixelStack = new byte[MaxStackSize + 1];}// Initialize GIF data stream decoder.data_size = read();clear = 1 << data_size;end_of_information = clear + 1;available = clear + 2;old_code = NullCode;code_size = data_size + 1;code_mask = (1 << code_size) - 1;for (code = 0; code < clear; code++) {prefix[code] = 0;suffix[code] = (byte) code;}// Decode GIF pixel stream.datum = bits = count = first = top = pi = bi = 0;for (i = 0; i < npix;) {if (top == 0) {if (bits < code_size) {// Load bytes until there are enough bits for a code.if (count == 0) {// Read a new data block.count = readBlock();if (count <= 0) {break;}bi = 0;}datum += (((int) block[bi]) & 0xff) << bits;bits += 8;bi++;count--;continue;}// Get the next code.code = datum & code_mask;datum >>= code_size;bits -= code_size;// Interpret the codeif ((code > available) || (code == end_of_information)) {break;}if (code == clear) {// Reset decoder.code_size = data_size + 1;code_mask = (1 << code_size) - 1;available = clear + 2;old_code = NullCode;continue;}if (old_code == NullCode) {pixelStack[top++] = suffix[code];old_code = code;first = code;continue;}in_code = code;if (code == available) {pixelStack[top++] = (byte) first;code = old_code;}while (code > clear) {pixelStack[top++] = suffix[code];code = prefix[code];}first = ((int) suffix[code]) & 0xff;// Add a new string to the string table,if (available >= MaxStackSize) {break;}pixelStack[top++] = (byte) first;prefix[available] = (short) old_code;suffix[available] = (byte) first;available++;if (((available & code_mask) == 0)&& (available < MaxStackSize)) {code_size++;code_mask += available;}old_code = in_code;}// Pop a pixel off the pixel stack.top--;pixels[pi++] = pixelStack[top];i++;}for (i = pi; i < npix; i++) {pixels[i] = 0; // clear missing pixels}}protected boolean err() {return status != STATUS_OK;}// to initia variablepublic void init() {status = STATUS_OK;frameCount = 0;frames = new Vector<GifFrame>();gct = null;lct = null;}protected int read() {int curByte = 0;try {curByte = in.read();} catch (Exception e) {status = STATUS_FORMAT_ERROR;}return curByte;}protected int readBlock() {blockSize = read();int n = 0;if (blockSize > 0) {try {int count = 0;while (n < blockSize) {count = in.read(block, n, blockSize - n);if (count == -1) {break;}n += count;}} catch (Exception e) {e.printStackTrace();}if (n < blockSize) {status = STATUS_FORMAT_ERROR;}}return n;}// Global Color Tableprotected int[] readColorTable(int ncolors) {int nbytes = 3 * ncolors;int[] tab = null;byte[] c = new byte[nbytes];int n = 0;try {n = in.read(c);} catch (Exception e) {e.printStackTrace();}if (n < nbytes) {status = STATUS_FORMAT_ERROR;} else {tab = new int[256]; // max size to avoid bounds checksint i = 0;int j = 0;while (i < ncolors) {int r = ((int) c[j++]) & 0xff;int g = ((int) c[j++]) & 0xff;int b = ((int) c[j++]) & 0xff;tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;}}return tab;}// Image Descriptorprotected void readContents() {// read GIF file content blocksboolean done = false;while (!(done || err())) {int code = read();switch (code) {case 0x2C: // image separatorreadImage();break;case 0x21: // extensioncode = read();switch (code) {case 0xf9: // graphics control extensionreadGraphicControlExt();break;case 0xff: // application extensionreadBlock();String app = "";for (int i = 0; i < 11; i++) {app += (char) block[i];}if (app.equals("NETSCAPE2.0")) {readNetscapeExt();} else {skip(); // don't care}break;default: // uninteresting extensionskip();}break;case 0x3b: // terminatordone = true;break;case 0x00: // bad byte, but keep going and see what happensbreak;default:status = STATUS_FORMAT_ERROR;}}}protected void readGraphicControlExt() {read(); // block sizeint packed = read(); // packed fieldsdispose = (packed & 0x1c) >> 2; // disposal methodif (dispose == 0) {dispose = 1; // elect to keep old image if discretionary}transparency = (packed & 1) != 0;delay = readShort() * 10; // delay in millisecondstransIndex = read(); // transparent color indexread(); // block terminator}// to get Stream - Headprotected void readHeader() {StringBuffer id = new StringBuffer();for (int i = 0; i < 6; i++) {id.append((char) read());}if (!id.toString().startsWith("GIF")) {status = STATUS_FORMAT_ERROR;return;}readLSD();if (gctFlag && !err()) {gct = readColorTable(gctSize);bgColor = gct[bgIndex];}}protected void readImage() {// offset of Xix = readShort(); // (sub)image position & size// offset of Yiy = readShort();// width of bitmapiw = readShort();// height of bitmapih = readShort();// Local Color Table Flagint packed = read();lctFlag = (packed & 0x80) != 0; // 1 - local color table flag// Interlace Flag, to array with interwoven if ENABLE, with order// otherwiseinterlace = (packed & 0x40) != 0; // 2 - interlace flag// 3 - sort flag// 4-5 - reservedlctSize = 2 << (packed & 7); // 6-8 - local color table sizeif (lctFlag) {lct = readColorTable(lctSize); // read tableact = lct; // make local table active} else {act = gct; // make global table activeif (bgIndex == transIndex) {bgColor = 0;}}int save = 0;if (transparency) {save = act[transIndex];act[transIndex] = 0; // set transparent color if specified}if (act == null) {status = STATUS_FORMAT_ERROR; // no color table defined}if (err()) {return;}decodeImageData(); // decode pixel dataskip();if (err()) {return;}frameCount++;// create new image to receive frame dataimage = Bitmap.createBitmap(width, height, Config.ARGB_4444);// createImage(width, height);setPixels(); // transfer pixel data to imageframes.addElement(new GifFrame(image, delay)); // add image to frame// listif (transparency) {act[transIndex] = save;}resetFrame();}// Logical Screen Descriptorprotected void readLSD() {// logical screen sizewidth = readShort();height = readShort();// packed fieldsint packed = read();gctFlag = (packed & 0x80) != 0; // 1 : global color table flag// 2-4 : color resolution// 5 : gct sort flaggctSize = 2 << (packed & 7); // 6-8 : gct sizebgIndex = read(); // background color indexpixelAspect = read(); // pixel aspect ratio}protected void readNetscapeExt() {do {readBlock();if (block[0] == 1) {// loop count sub-blockint b1 = ((int) block[1]) & 0xff;int b2 = ((int) block[2]) & 0xff;loopCount = (b2 << 8) | b1;}} while ((blockSize > 0) && !err());}// read 8 bit dataprotected int readShort() {// read 16-bit value, LSB firstreturn read() | (read() << 8);}protected void resetFrame() {lastDispose = dispose;lrx = ix;lry = iy;lrw = iw;lrh = ih;lastImage = image;lastBgColor = bgColor;dispose = 0;transparency = false;delay = 0;lct = null;}/** * Skips variable length blocks up to and including next zero length block. */protected void skip() {do {readBlock();} while ((blockSize > 0) && !err());}// to store *.gif data, Bitmap & delay/** * 帧对象,包含图片和延迟时间 *  * @author moon *  */private class GifFrame {// to access image & delay w/o interfacepublic Bitmap image;public int delay;public GifFrame(Bitmap im, int del) {image = im;delay = del;}}}

我的gig图片的命名,都是f000.gif-f0010.gif  注意不要匹配不出来

本人在textview中测试效果很好,可是在edittex它里面就会失效,或者动态效果不明显,这里还不知道原因,希望知道的能告诉下。

有不足的位置希望大家指出。谢谢!!





0 0
原创粉丝点击