Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發系列(十七)QQ聊天之Android顯示Gif ——在TextView中添加動態表情

Android開發系列(十七)QQ聊天之Android顯示Gif ——在TextView中添加動態表情

編輯:關於Android編程

谷歌官方並沒有給出Gif的顯示控件(雖然我想在高霸上的谷歌眼中這不過是隨便敲敲鍵盤的事兒),網絡上給的解決思路,基本上可以分為兩個方向:        一、使用Movie類,將Gif當成視頻來播放。 經驗證之後發現這種方法並不靠譜,播放的Gif不是花屏就是完全黑屏,而且另外一個缺點是可操作性不強,比如就沒有辦法把它嵌在TextView中。因此,不推薦用此方法,各位所有為此困擾的建議果斷拋棄這種思路。        二、將Gif文件 分解成多幀圖片,然後由一個線程控制ImageView或者ImageSpan按照一定的時間間隔循環加載這幾幀圖片。             而對於如何將Gif圖片分解成多幀圖片又有兩種思路:一個是人工采用一個軟件將gif圖片分解成多個幀,然後把這每一幀都放進資源文件夾中,然後按照上面的方法進行加載,這種方法雖然看起來很笨,但是效果最流暢,我估計較早版本的手機QQ可能這樣干過==    另外一種思路就是采用工具類進行即時解碼,這種方法看起來比較高端,但對內存資源的消耗比較多,還有一個關鍵的問題是現在很難找到一個完美的工具類可以對Gif圖片有較好的解碼,我想騰訊肯定會有,但是不見得會開源..現在code.google.com上一個gifView.jar的工具包,有興趣可以去網上搜一下,這個工具包裡面對於gif的軟解碼還是比較全,當然也不完美。        在本項目中采用的也是第二種思路,即采用一個工具類GifOpenHelper.java對gif進行解碼,將生成的一組Bitmap對象存儲在一個ArrayList<Bitmap>中,然後由線程控制加載。這裡首先聲明這個工具類不是我原創的,也有一些缺陷,首先給出這個類:   復制代碼 package com.example.textactivity;   import java.io.InputStream; import java.util.Vector;   import android.graphics.Bitmap; import android.graphics.Bitmap.Config;   //Handler for read & extract Bitmap from *.gif public class GifOpenHelper {       // to store *.gif data, Bitmap & delay     class GifFrame {         // to access image & delay w/o interface         public Bitmap image;         public int delay;           public GifFrame(Bitmap im, int del) {             image = im;             delay = del;         }       }       // to define some error type     public 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 width     protected int height; // full image height     protected boolean gctFlag; // global color table used     protected int gctSize; // size of global color table     protected int loopCount = 1; // iterations; 0 = repeat forever       protected int[] gct; // global color table     protected int[] lct; // local color table     protected int[] act; // active color table       protected int bgIndex; // background color index     protected int bgColor; // background color     protected int lastBgColor; // previous bg color     protected int pixelAspect; // pixel aspect ratio       protected boolean lctFlag; // local color table flag     protected boolean interlace; // interlace flag     protected int lctSize; // local color table size       protected int ix, iy, iw, ih; // current image rectangle     protected int lrx, lry, lrw, lrh;     protected Bitmap image; // current frame     protected Bitmap lastImage; // previous frame     protected 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 block     protected int blockSize = 0; // block size       // last graphic control extension info     protected int dispose = 0;     // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev     protected int lastDispose = 0;     protected boolean transparency = false; // use transparent color     protected int delay = 0; // delay in milliseconds     protected int transIndex; // transparent color index       protected static final int MaxStackSize = 4096;     // max decoder pixel stack size       // LZW decoder working arrays     protected short[] prefix;     protected byte[] suffix;     protected byte[] pixelStack;     protected byte[] pixels;       protected Vector<GifFrame> frames; // frames read from current file     protected int frameCount;       // to get its Width / Height     public 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 code         if (lastDispose > 0) {             if (lastDispose == 3) {                 // use image before last                 int 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 pixels                 if (lastDispose == 2) {                     // fill last image rect area with background color                     int 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 destination         int 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 dest                 int dlim = dx + iw; // end of dest line                 if ((k + width) < dlim) {                     dlim = k + width; // past dest edge                 }                 int sx = i * iw; // start of line in source                 while (dx < dlim) {                     // map color and insert in destination                     int index = ((int) pixels[sx++]) & 0xff;                     int c = act[index];                     if (c != 0) {                         dest[dx] = c;                     }                     dx++;                 }             }         }         image = Bitmap.createBitmap(dest, width, height, Config.RGB_565);     }       public Bitmap getFrame(int n) {         Bitmap im = null;         if ((n >= 0) && (n < frameCount)) {             im = ((GifFrame) frames.elementAt(n)).image;         }         return im;     }       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     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 code                 if ((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 variable     public 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 Table     protected 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 checks             int 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 Descriptor     protected void readContents() {         // read GIF file content blocks         boolean done = false;         while (!(done || err())) {             int code = read();             switch (code) {             case 0x2C: // image separator                 readImage();                 break;             case 0x21: // extension                 code = read();                 switch (code) {                 case 0xf9: // graphics control extension                     readGraphicControlExt();                     break;                   case 0xff: // application extension                     readBlock();                     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 extension                     skip();                 }                 break;               case 0x3b: // terminator                 done = true;                 break;               case 0x00: // bad byte, but keep going and see what happens                 break;             default:                 status = STATUS_FORMAT_ERROR;             }         }     }       protected void readGraphicControlExt() {         read(); // block size         int packed = read(); // packed fields         dispose = (packed & 0x1c) >> 2; // disposal method         if (dispose == 0) {             dispose = 1; // elect to keep old image if discretionary         }         transparency = (packed & 1) != 0;         delay = readShort() * 10; // delay in milliseconds         transIndex = read(); // transparent color index         read(); // block terminator     }       // to get Stream - Head     protected void readHeader() {         String id = "";         for (int i = 0; i < 6; i++) {             id += (char) read();         }         if (!id.startsWith("GIF")) {             status = STATUS_FORMAT_ERROR;             return;         }         readLSD();         if (gctFlag && !err()) {             gct = readColorTable(gctSize);             bgColor = gct[bgIndex];         }     }       protected void readImage() {         // offset of X         ix = readShort(); // (sub)image position & size         // offset of Y         iy = readShort();         // width of bitmap         iw = readShort();         // height of bitmap         ih = readShort();           // Local Color Table Flag         int packed = read();         lctFlag = (packed & 0x80) != 0; // 1 - local color table flag           // Interlace Flag, to array with interwoven if ENABLE, with order         // otherwise         interlace = (packed & 0x40) != 0; // 2 - interlace flag         // 3 - sort flag         // 4-5 - reserved         lctSize = 2 << (packed & 7); // 6-8 - local color table size         if (lctFlag) {             lct = readColorTable(lctSize); // read table             act = lct; // make local table active         } else {             act = gct; // make global table active             if (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 data         skip();         if (err()) {             return;         }         frameCount++;         // create new image to receive frame data         image = Bitmap.createBitmap(width, height, Config.RGB_565);         // createImage(width, height);         setPixels(); // transfer pixel data to image         frames.addElement(new GifFrame(image, delay)); // add image to frame         // list         if (transparency) {             act[transIndex] = save;         }         resetFrame();     }       // Logical Screen Descriptor     protected void readLSD() {         // logical screen size         width = readShort();         height = readShort();         // packed fields         int packed = read();         gctFlag = (packed & 0x80) != 0; // 1 : global color table flag         // 2-4 : color resolution         // 5 : gct sort flag         gctSize = 2 << (packed & 7); // 6-8 : gct size         bgIndex = read(); // background color index         pixelAspect = read(); // pixel aspect ratio     }       protected void readNetscapeExt() {         do {             readBlock();             if (block[0] == 1) {                 // loop count sub-block                 int b1 = ((int) block[1]) & 0xff;                 int b2 = ((int) block[2]) & 0xff;                 loopCount = (b2 << 8) | b1;             }         } while ((blockSize > 0) && !err());     }       // read 8 bit data     protected int readShort() {         // read 16-bit value, LSB first         return 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());     } } 復制代碼   該類中用到的幾個基本方法是:         read(InputStream Is)    讀取gif圖片流,注意只能是Gif!  具體調用方法參考下面的MyTextView.java類         getImage();    獲取第一幀圖片;         nextBitmap()   獲取下一幀圖片;         getFrameCount();   獲取分解後的總幀數;         nextDelay()     獲取幀與幀之間的時間間隔,用於在線程中控制播放效果   對於這個類還想說兩句的是不建議把它搞得明明白白,我們的目的是利用一些工具做出想要的控件。當然如果你想做一個制作Gif的應用那反而要好好研究一下算法了。         下面給出來利用GifOpenHelper類設計的TextView升級版本,這個TextView在原有功能的基礎上還可以實現靜態圖片顯示、動態圖片顯示,以及動態圖片、靜態圖片和文字混合顯示。 這個類是我自己寫的,所以注釋給的比較全面~   復制代碼 package com.example.android_qqfix;   import java.util.ArrayList;   import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern;   import com.example.utils.FaceData;   import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ImageSpan; import android.util.AttributeSet; import android.widget.TextView;   public class MyTextView extends TextView{         /**      * @author Dragon      * SpanInfo 類用於存儲一個要顯示的圖片(動態或靜態)的信息,包括分解後的每一幀mapList、替代文字的起始位置、終止位置      * 、幀的總數、當前需要顯示的幀、幀與幀之間的時間間隔       */     private class SpanInfo{         ArrayList<Bitmap> mapList;         int start,end,frameCount,currentFrameIndex,delay;         public SpanInfo(){             mapList=new ArrayList<Bitmap>();             start=end=frameCount=currentFrameIndex=delay=0;             }     }          /**      * spanInfoList 是一個SpanInfo的list ,用於處理一個TextView中出現多個要匹配的圖片的情況      */     private ArrayList<SpanInfo> spanInfoList=null;     private Handler handler;           //用於處理從子線程TextView傳來的消息     private String myText;             //存儲textView應該顯示的文本          /**      * 這三個構造方法一個也不要少,否則會產生CastException,注意在這三個構造函數中都為spanInfoList實例化,可能有些浪費      * ,但保證不會有空指針異常      * @param context      * @param attrs      * @param defStyle      */     public MyTextView(Context context, AttributeSet attrs, int defStyle) {         super(context, attrs, defStyle);         // TODO Auto-generated constructor stub         spanInfoList=new ArrayList<SpanInfo>();     }       public MyTextView(Context context, AttributeSet attrs) {         super(context, attrs);         // TODO Auto-generated constructor stub         spanInfoList=new ArrayList<SpanInfo>();     }       public MyTextView(Context context) {         super(context);         // TODO Auto-generated constructor stub         spanInfoList=new ArrayList<SpanInfo>();     }                    /**      * 對要顯示在textView上的文本進行解析,看看是否文本中有需要與Gif或者靜態圖片匹配的文本      * 若有,那麼調用parseGif 對該文本對應的Gif圖片進行解析 或者嗲用parseBmp解析靜態圖片      * @param inputStr      */     private void parseText(String inputStr){         myText=inputStr;         Pattern mPattern=Pattern.compile("\\\\..");         Matcher mMatcher=mPattern.matcher(inputStr);         while(mMatcher.find()){             String faceName=mMatcher.group();             Integer faceId=null;             /**              * 這裡匹配時用到了圖片庫,即一個專門存放圖片id和其匹配的名稱的靜態對象,這兩個靜態對象放在了FaceData.java              * 中,並采用了靜態塊的方法進行了初始化,不會有空指針異常              */                          if((faceId=FaceData.gifFaceInfo.get(faceName))!=null){                 parseGif(faceId, mMatcher.start(), mMatcher.end());             }             else if((faceId=FaceData.staticFaceInfo.get(faceName))!=null){                 parseBmp(faceId, mMatcher.start(), mMatcher.end());             }                      }                       }          /**      * 對靜態圖片進行解析:      * 創建一個SpanInfo對象,幀數設為1,按照下面的參數設置,最後不要忘記將SpanInfo對象添加進spanInfoList中,      * 否則不會顯示      * @param resourceId      * @param start      * @param end      */     private void parseBmp(int resourceId,int start, int end){         Bitmap bitmap=BitmapFactory.decodeResource(getContext().getResources(), resourceId);         ImageSpan imageSpan=new ImageSpan(getContext(),bitmap);         SpanInfo spanInfo=new SpanInfo();         spanInfo.currentFrameIndex=0;         spanInfo.frameCount=1;         spanInfo.start=start;         spanInfo.end=end;         spanInfo.delay=100;         spanInfo.mapList.add(bitmap);         spanInfoList.add(spanInfo);              }          /**      * 解析Gif圖片,與靜態圖片唯一的不同是這裡需要調用GifOpenHelper類讀取Gif返回一系一組bitmap(用for 循環把這一      * 組的bitmap存儲在SpanInfo.mapList中,此時的frameCount參數也大於1了)      * @param resourceId      * @param start      * @param end      */     private void parseGif(int resourceId ,int start, int end){                 GifOpenHelper helper=new GifOpenHelper();         helper.read(getContext().getResources().openRawResource(resourceId));         SpanInfo spanInfo=new SpanInfo();         spanInfo.currentFrameIndex=0;         spanInfo.frameCount=helper.getFrameCount();         spanInfo.start=start;         spanInfo.end=end;         spanInfo.mapList.add(helper.getImage());         for(int i=1; i<helper.getFrameCount(); i++){             spanInfo.mapList.add(helper.nextBitmap());         }         spanInfo.delay=helper.nextDelay();        //獲得每一幀之間的延遲         spanInfoList.add(spanInfo);       }          /**      * MyTextView 與外部對象的接口,以後設置文本內容時使用setSpanText() 而不再是setText();      * @param handler      * @param text      */     public void setSpanText(Handler handler, String text){         this.handler=handler;      //獲得UI的Handler         parseText(text);           //對String對象進行解析         TextRunnable r=new TextRunnable();   //生成Runnable對象         handler.post(r);       //利用UI線程的Handler 將r添加進消息隊列中。              }               public class TextRunnable implements Runnable{           @Override         public void run() {             // TODO Auto-generated method stub             SpannableString sb=new SpannableString(""+myText);   //獲得要顯示的文本             int gifCount=0;             SpanInfo info=null;             for(int i=0; i<spanInfoList.size(); i++){  //for循環,處理顯示多個圖片的問題                 info=spanInfoList.get(i);                 if(info.mapList.size()>1){                            /*                      * gifCount用來區分是Gif還是BMP,若是gif gifCount>0 ,否則gifCount=0                      */                     gifCount++;                                      }                 Bitmap bitmap=info.mapList.get(info.currentFrameIndex);                 info.currentFrameIndex=(info.currentFrameIndex+1)%(info.frameCount);                 /**                  * currentFrameIndex 用於控制當前應該顯示的幀的序號,每次顯示之後currentFrameIndex                  * 應該加1 ,加到frameCount後再變成0循環顯示                  */                                  if(gifCount!=0){                     bitmap=Bitmap.createScaledBitmap(bitmap, 60, 60, true);                                  }                 else{                     bitmap=Bitmap.createScaledBitmap(bitmap, 30, 30, true);                 }                 ImageSpan imageSpan=new ImageSpan(getContext(),bitmap);                    sb.setSpan(imageSpan, info.start, info.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);                              }             //對所有的圖片對應的ImageSpan完成設置後,調用TextView的setText方法設置文本             MyTextView.this.setText(sb);                            /**              * 這一步是為了節省內存而是用,即如果文本中只有靜態圖片沒有動態圖片,那麼該線程就此終止,不會重復執行              * 。而如果有動圖,那麼會一直執行              */             if(gifCount!=0){                 handler.postDelayed(this,info.delay );                          }                      }              }             } 復制代碼       這裡有幾點要特別說明:   1、不能在MyTextView中開辟子線程,然後利用sleep方法每隔一段時間像ChatActivity的Handler發送一個Message, 這樣的話系統也認為是線程不安全的,會報錯。經過我嘗試,目前可行的辦法,就是在MyTextView中創建一個Runnable對象,然後用UI的Handler的post() 和postDelay() 方法按照一定的時間間隔調用。至於其原因我也不太明白,但這說明采用Runnable對象比采用Message對象的線程安全性更高一些。   2、還有一點是為什麼創建這個類?因為在QQ聊天ListView中有多個TextView,幾乎不可能在UI線程中獲得每一個TextView的引用然後同步控制它們所顯示的圖片,因此只能把這個操作放給每一個TextView自己去執行相關的操作,這樣的代碼寫出來比較簡潔,最大的一個好處就是可復用性好,並且接口調用十分簡單:只需要setSpanText(Handler handler, String text);即可,後面會有說明。         此外上面已經提到,這裡對表情數據的組織方式進行了優化,新建了一個類用於存儲靜態對象,並且采用了靜態塊完成初始化:   復制代碼 package com.example.utils;   import java.util.HashMap;     import com.example.android_qqfix.R;   public class FaceData {     public static HashMap<String, Integer> gifFaceInfo;     public static HashMap<String,Integer> staticFaceInfo;     public static String[] gifFaceName={"\\ji","\\gl"};     public static Integer[] gifFaceId={R.raw.ji,R.raw.gl};     public static int[] faceId={R.drawable.f_static_000,R.drawable.f_static_001,R.drawable.f_static_002,R.drawable.f_static_003             ,R.drawable.f_static_004,R.drawable.f_static_005,R.drawable.f_static_006,R.drawable.f_static_009,R.drawable.f_static_010,R.drawable.f_static_011             ,R.drawable.f_static_012,R.drawable.f_static_013,R.drawable.f_static_014,R.drawable.f_static_015,R.drawable.f_static_017,R.drawable.f_static_018};     public static String[] faceName={"\\呲牙","\\淘氣","\\流汗","\\偷笑","\\再見","\\敲打","\\擦汗","\\流淚","\\掉淚","\\小聲","\\炫酷","\\發狂"              ,"\\委屈","\\便便","\\菜刀","\\微笑","\\色色","\\害羞"};          //靜態塊的使用     static {         gifFaceInfo=new HashMap<String,Integer>();         staticFaceInfo=new HashMap<String,Integer>();         for(int i=0;i<gifFaceName.length;i++){             gifFaceInfo.put(gifFaceName[i], gifFaceId[i]);         }         for(int i=0;i<faceId.length;i++){             staticFaceInfo.put(faceName[i], faceId[i]);         }     }        } 復制代碼 如果有需要的話,可以再添加幾個靜態方法用於對表情數據進行修改,這裡不再贅述。     以上三個類(MyTextView,GifOpenHelper, FaceData)配合起來使用,就可以構成一個比較獨立的自定義TextView, 具有顯示圖片的功能,並且不需要在外部使用SpannableString 的方法設置,因為MyTextView內部已經封裝好了,下面那QQ ChatActivity的代碼做一個對比:   這是不使用自定義MyTextView之前需要顯示圖片進行的操作:         復制代碼  private void setFaceText(TextView textView,String text){         SpannableString spanStr=parseString(text);         textView.setText(spanStr);     }              private void setFace(SpannableStringBuilder spb, String faceName){         Integer faceId=faceMap.get(faceName);         if(faceId!=null){             Bitmap bitmap=BitmapFactory.decodeResource(getResources(), faceId);             bitmap=Bitmap.createScaledBitmap(bitmap, 30, 30, true);             ImageSpan imageSpan=new ImageSpan(this,bitmap);             SpannableString spanStr=new SpannableString(faceName);             spanStr.setSpan(imageSpan, 0, faceName.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);             spb.append(spanStr);             }         else{             spb.append(faceName);         }              }     private SpannableString parseString(String inputStr){         SpannableStringBuilder spb=new SpannableStringBuilder();         Pattern mPattern= Pattern.compile("\\\\..");         Matcher mMatcher=mPattern.matcher(inputStr);         String tempStr=inputStr;                  while(mMatcher.find()){             int start=mMatcher.start();             int end=mMatcher.end();             spb.append(tempStr.substring(0,start));             String faceName=mMatcher.group();             setFace(spb, faceName);             tempStr=tempStr.substring(end, tempStr.length());                          mMatcher.reset(tempStr);         }         spb.append(tempStr);         return new SpannableString(spb);     }                 //然後調用時 setFaceText(holder.textView, chatList.get(position).get(from[1]).toString()); 復制代碼   而這是采用MyTextView封裝之後的調用方法:   holder.textView.setSpanText(handler,chatList.get(position).get(from[1]).toString()); 可見雖然設計MyTextView需要花點功夫,但是調用時換來極大的方便! 而且可以支持動態表情。       另外注意一點如使用MyTextView 那麼xml布局文件中需要做一些修改,就是把TextView 改成自己定義的TextView子類   復制代碼 <com.example.android_qqfix.MyTextView          android:id="@+id/chatlist_text_me"         android:layout_width="wrap_content"         android:layout_height="wrap_content"         android:layout_toLeftOf="@id/chatlist_image_me"         android:layout_alignTop="@id/chatlist_image_me"         android:textSize="8pt"         android:background="@drawable/balloon_back_right"         android:paddingTop="13dip"         android:paddingBottom="18dip"      />
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved