53、Gif 控件GifView 的使用,播放gif图片

GifView 是一个为了解决android中现在没有直接显示gif的view,只能通过mediaplay来显示这个问题的项目,其用法和 ImageView一样,支持gif图片。可监视GIF是否加载成功。

GifView的功能: 播放Gif图片 Gif动画监听

Android GifView 的用法:

GifAction.java 观察者类,监视GIF是否加载成功
GifFrame.java 里面三个成员:当前图片、延时、下张Frame的链接。
GifDecoder.java 解码线程类
GifView.java 主类,包括常用方法,如GifView构造方法、设置图片源、延迟、绘制等。

 3 public interface GifAction {
 4     /**
 5      * gif解码观察者
 6      * @param parseStatus 解码是否成功,成功会为true
 7      * @param frameIndex 当前解码的第几帧,当全部解码成功后,这里为-1
 8      */
 9     public void parseOk(boolean parseStatus,int frameIndex);
10 }
 1 import android.graphics.Bitmap;
 2 
 3 public class GifFrame {
 4     /**
 5      * 构造函数
 6      * @param im 图片
 7      * @param del 延时
 8      */
 9     public GifFrame(Bitmap im, int del) {
10         image = im;
11         delay = del;
12     }
13     /**图片*/
14     public Bitmap image;
15     /**延时*/
16     public int delay;
17     /**下一帧*/
18     public GifFrame nextFrame = null;
19 }
  1  import java.io.ByteArrayInputStream;
  2 import java.io.InputStream;
  3 import android.graphics.Bitmap;
  4 import android.graphics.Bitmap.Config;
  5 
  6 public class GifDecoder extends Thread {
  7 
  8     /** 状态:正在解码中 */
  9     public static final int STATUS_PARSING = 0;
 10     /** 状态:图片格式错误 */
 11     public static final int STATUS_FORMAT_ERROR = 1;
 12     /** 状态:打开失败 */
 13     public static final int STATUS_OPEN_ERROR = 2;
 14     /** 状态:解码成功 */
 15     public static final int STATUS_FINISH = -1;
 16 
 17     private InputStream in;
 18     private int status;
 19 
 20     public int width; // full image width
 21     public int height; // full image height
 22     private boolean gctFlag; // global color table used
 23     private int gctSize; // size of global color table
 24     private int loopCount = 1; // iterations; 0 = repeat forever
 25 
 26     private int[] gct; // global color table
 27     private int[] lct; // local color table
 28     private int[] act; // active color table
 29 
 30     private int bgIndex; // background color index
 31     private int bgColor; // background color
 32     private int lastBgColor; // previous bg color
 33     private int pixelAspect; // pixel aspect ratio
 34 
 35     private boolean lctFlag; // local color table flag
 36     private boolean interlace; // interlace flag
 37     private int lctSize; // local color table size
 38 
 39     private int ix, iy, iw, ih; // current image rectangle
 40     private int lrx, lry, lrw, lrh;
 41     private Bitmap image; // current frame
 42     private Bitmap lastImage; // previous frame
 43     private GifFrame currentFrame = null;
 44 
 45     private boolean isShow = false;
 46 
 47     // current data block
 48     private byte[] block = new byte[256];
 49     private int blockSize = 0; // block size
 50 
 51     // last graphic control extension info
 52     private int dispose = 0;
 53     // 0=no action; 1=leave in place; 2=restore to bg; 3=restore to prev
 54     private int lastDispose = 0;
 55     // use transparent color
 56     private boolean transparency = false;
 57     // delay in milliseconds
 58     private int delay = 0;
 59     // transparent color index
 60     private int transIndex;
 61 
 62     private static final int MaxStackSize = 4096;
 63     // max decoder pixel stack size
 64 
 65     // LZW decoder working arrays
 66     private short[] prefix;
 67     private byte[] suffix;
 68     private byte[] pixelStack;
 69     private byte[] pixels;
 70     // frames read from current file
 71     private GifFrame gifFrame;
 72     private int frameCount;
 73 
 74     private GifAction action = null;
 75 
 76     private byte[] gifData = null;
 77 
 78     public GifDecoder(byte[] data, GifAction act) {
 79         gifData = data;
 80         action = act;
 81     }
 82 
 83     public GifDecoder(InputStream is, GifAction act) {
 84         in = is;
 85         action = act;
 86     }
 87 
 88     public void run() {
 89         if (in != null) {
 90             readStream();
 91         } else if (gifData != null) {
 92             readByte();
 93         }
 94     }
 95 
 96     /**
 97      * 释放资源
 98      */
 99     public void free() {
100         GifFrame fg = gifFrame;
101         while (fg != null) {
102             fg.image = null;
103             fg = null;
104             gifFrame = gifFrame.nextFrame;
105             fg = gifFrame;
106         }
107         if (in != null) {
108             try {
109                 in.close();
110             } catch (Exception ex) {
111             }
112             in = null;
113         }
114         gifData = null;
115     }
116 
117     /**
118      * 当前状态
119      * @return
120      */
121     public int getStatus() {
122         return status;
123     }
124 
125     /**
126      * 解码是否成功,成功返回true
127      * @return 成功返回true,否则返回false
128      */
129     public boolean parseOk() {
130         return status == STATUS_FINISH;
131     }
132 
133     /**
134      * 取某帧的延时时间
135      * @param n
136      *            第几帧
137      * @return 延时时间,毫秒
138      */
139     public int getDelay(int n) {
140         delay = -1;
141         if ((n >= 0) && (n < frameCount)) {
142             // delay = ((GifFrame) frames.elementAt(n)).delay;
143             GifFrame f = getFrame(n);
144             if (f != null)
145                 delay = f.delay;
146         }
147         return delay;
148     }
149 
150     /**
151      * 取所有帧的延时时间
152      * @return
153      */
154     public int[] getDelays() {
155         GifFrame f = gifFrame;
156         int[] d = new int[frameCount];
157         int i = 0;
158         while (f != null && i < frameCount) {
159             d[i] = f.delay;
160             f = f.nextFrame;
161             i++;
162         }
163         return d;
164     }
165 
166     /**
167      * 取总帧 数
168      * @return 图片的总帧数
169      */
170     public int getFrameCount() {
171         return frameCount;
172     }
173 
174     /**
175      * 取第一帧图片
176      * @return
177      */
178     public Bitmap getImage() {
179         return getFrameImage(0);
180     }
181 
182     public int getLoopCount() {
183         return loopCount;
184     }
185 
186     private void setPixels() {
187         int[] dest = new int[width * height];
188         // fill in starting image contents based on last image's dispose code
189         if (lastDispose > 0) {
190             if (lastDispose == 3) {
191                 // use image before last
192                 int n = frameCount - 2;
193                 if (n > 0) {
194                     lastImage = getFrameImage(n - 1);
195                 } else {
196                     lastImage = null;
197                 }
198             }
199             if (lastImage != null) {
200                 lastImage.getPixels(dest, 0, width, 0, 0, width, height);
201                 // copy pixels
202                 if (lastDispose == 2) {
203                     // fill last image rect area with background color
204                     int c = 0;
205                     if (!transparency) {
206                         c = lastBgColor;
207                     }
208                     for (int i = 0; i < lrh; i++) {
209                         int n1 = (lry + i) * width + lrx;
210                         int n2 = n1 + lrw;
211                         for (int k = n1; k < n2; k++) {
212                             dest[k] = c;
213                         }
214                     }
215                 }
216             }
217         }
218 
219         // copy each source line to the appropriate place in the destination
220         int pass = 1;
221         int inc = 8;
222         int iline = 0;
223         for (int i = 0; i < ih; i++) {
224             int line = i;
225             if (interlace) {
226                 if (iline >= ih) {
227                     pass++;
228                     switch (pass) {
229                     case 2:
230                         iline = 4;
231                         break;
232                     case 3:
233                         iline = 2;
234                         inc = 4;
235                         break;
236                     case 4:
237                         iline = 1;
238                         inc = 2;
239                     }
240                 }
241                 line = iline;
242                 iline += inc;
243             }
244             line += iy;
245             if (line < height) {
246                 int k = line * width;
247                 int dx = k + ix; // start of line in dest
248                 int dlim = dx + iw; // end of dest line
249                 if ((k + width) < dlim) {
250                     dlim = k + width; // past dest edge
251                 }
252                 int sx = i * iw; // start of line in source
253                 while (dx < dlim) {
254                     // map color and insert in destination
255                     int index = ((int) pixels[sx++]) & 0xff;
256                     int c = act[index];
257                     if (c != 0) {
258                         dest[dx] = c;
259                     }
260                     dx++;
261                 }
262             }
263         }
264         image = Bitmap.createBitmap(dest, width, height, Config.ARGB_4444);
265     }
266 
267     /**
268      * 取第几帧的图片
269      * 
270      * @param n
271      *            帧数
272      * @return 可画的图片,如果没有此帧或者出错,返回null
273      */
274     public Bitmap getFrameImage(int n) {
275         GifFrame frame = getFrame(n);
276         if (frame == null)
277             return null;
278         else
279             return frame.image;
280     }
281 
282     /**
283      * 取当前帧图片
284      * 
285      * @return 当前帧可画的图片
286      */
287     public GifFrame getCurrentFrame() {
288         return currentFrame;
289     }
290 
291     /**
292      * 取第几帧,每帧包含了可画的图片和延时时间
293      * 
294      * @param n
295      *            帧数
296      * @return
297      */
298     public GifFrame getFrame(int n) {
299         GifFrame frame = gifFrame;
300         int i = 0;
301         while (frame != null) {
302             if (i == n) {
303                 return frame;
304             } else {
305                 frame = frame.nextFrame;
306             }
307             i++;
308         }
309         return null;
310     }
311 
312     /**
313      * 重置,进行本操作后,会直接到第一帧
314      */
315     public void reset() {
316         currentFrame = gifFrame;
317     }
318 
319     /**
320      * 下一帧,进行本操作后,通过getCurrentFrame得到的是下一帧
321      * 
322      * @return 返回下一帧
323      */
324     public GifFrame next() {
325         if (isShow == false) {
326             isShow = true;
327             return gifFrame;
328         } else {
329             if (status == STATUS_PARSING) {
330                 if (currentFrame.nextFrame != null)
331                     currentFrame = currentFrame.nextFrame;
332                 // currentFrame = gifFrame;
333             } else {
334                 currentFrame = currentFrame.nextFrame;
335                 if (currentFrame == null) {
336                     currentFrame = gifFrame;
337                 }
338             }
339             return currentFrame;
340         }
341     }
342 
343     private int readByte() {
344         in = new ByteArrayInputStream(gifData);
345         gifData = null;
346         return readStream();
347     }
348 
349     // public int read(byte[] data){
350     // InputStream is = new ByteArrayInputStream(data);
351     // return read(is);
352     // }
353 
354     private int readStream() {
355         init();
356         if (in != null) {
357             readHeader();
358             if (!err()) {
359                 readContents();
360                 if (frameCount < 0) {
361                     status = STATUS_FORMAT_ERROR;
362                     action.parseOk(false, -1);
363                 } else {
364                     status = STATUS_FINISH;
365                     action.parseOk(true, -1);
366                 }
367             }
368             try {
369                 in.close();
370             } catch (Exception e) {
371                 e.printStackTrace();
372             }
373 
374         } else {
375             status = STATUS_OPEN_ERROR;
376             action.parseOk(false, -1);
377         }
378         return status;
379     }
380 
381     private void decodeImageData() {
382         int NullCode = -1;
383         int npix = iw * ih;
384         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;
385 386 if ((pixels == null) || (pixels.length < npix)) { 387 pixels = new byte[npix]; // allocate new pixel array 388 } 389 if (prefix == null) { 390 prefix = new short[MaxStackSize]; 391 } 392 if (suffix == null) { 393 suffix = new byte[MaxStackSize]; 394 } 395 if (pixelStack == null) { 396 pixelStack = new byte[MaxStackSize + 1]; 397 } 398 // Initialize GIF data stream decoder. 399 data_size = read(); 400 clear = 1 << data_size; 401 end_of_information = clear + 1; 402 available = clear + 2; 403 old_code = NullCode; 404 code_size = data_size + 1; 405 code_mask = (1 << code_size) - 1; 406 for (code = 0; code < clear; code++) { 407 prefix[code] = 0; 408 suffix[code] = (byte) code; 409 } 410 411 // Decode GIF pixel stream. 412 datum = bits = count = first = top = pi = bi = 0; 413 for (i = 0; i < npix;) { 414 if (top == 0) { 415 if (bits < code_size) { 416 // Load bytes until there are enough bits for a code. 417 if (count == 0) { 418 // Read a new data block. 419 count = readBlock(); 420 if (count <= 0) { 421 break; 422 } 423 bi = 0; 424 } 425 datum += (((int) block[bi]) & 0xff) << bits; 426 bits += 8; 427 bi++; 428 count--; 429 continue; 430 } 431 // Get the next code. 432 code = datum & code_mask; 433 datum >>= code_size; 434 bits -= code_size; 435 436 // Interpret the code 437 if ((code > available) || (code == end_of_information)) { 438 break; 439 } 440 if (code == clear) { 441 // Reset decoder. 442 code_size = data_size + 1; 443 code_mask = (1 << code_size) - 1; 444 available = clear + 2; 445 old_code = NullCode; 446 continue; 447 } 448 if (old_code == NullCode) { 449 pixelStack[top++] = suffix[code]; 450 old_code = code; 451 first = code; 452 continue; 453 } 454 in_code = code; 455 if (code == available) { 456 pixelStack[top++] = (byte) first; 457 code = old_code; 458 } 459 while (code > clear) { 460 pixelStack[top++] = suffix[code]; 461 code = prefix[code]; 462 } 463 first = ((int) suffix[code]) & 0xff; 464 // Add a new string to the string table, 465 if (available >= MaxStackSize) { 466 break; 467 } 468 pixelStack[top++] = (byte) first; 469 prefix[available] = (short) old_code; 470 suffix[available] = (byte) first; 471 available++; 472 if (((available & code_mask) == 0) 473 && (available < MaxStackSize)) { 474 code_size++; 475 code_mask += available; 476 } 477 old_code = in_code; 478 } 479 480 // Pop a pixel off the pixel stack. 481 top--; 482 pixels[pi++] = pixelStack[top]; 483 i++; 484 } 485 for (i = pi; i < npix; i++) { 486 pixels[i] = 0; // clear missing pixels 487 } 488 } 489 490 private boolean err() { 491 return status != STATUS_PARSING; 492 } 493 494 private void init() { 495 status = STATUS_PARSING; 496 frameCount = 0; 497 gifFrame = null; 498 gct = null; 499 lct = null; 500 } 501 502 private int read() { 503 int curByte = 0; 504 try { 505 506 curByte = in.read(); 507 } catch (Exception e) { 508 status = STATUS_FORMAT_ERROR; 509 } 510 return curByte; 511 } 512 513 private int readBlock() { 514 blockSize = read(); 515 int n = 0; 516 if (blockSize > 0) { 517 try { 518 int count = 0; 519 while (n < blockSize) { 520 count = in.read(block, n, blockSize - n); 521 if (count == -1) { 522 break; 523 } 524 n += count; 525 } 526 } catch (Exception e) { 527 e.printStackTrace(); 528 } 529 if (n < blockSize) { 530 status = STATUS_FORMAT_ERROR; 531 } 532 } 533 return n; 534 } 535 536 private int[] readColorTable(int ncolors) { 537 int nbytes = 3 * ncolors; 538 int[] tab = null; 539 byte[] c = new byte[nbytes]; 540 int n = 0; 541 try { 542 n = in.read(c); 543 } catch (Exception e) { 544 e.printStackTrace(); 545 } 546 if (n < nbytes) { 547 status = STATUS_FORMAT_ERROR; 548 } else { 549 tab = new int[256]; // max size to avoid bounds checks 550 int i = 0; 551 int j = 0; 552 while (i < ncolors) { 553 int r = ((int) c[j++]) & 0xff; 554 int g = ((int) c[j++]) & 0xff; 555 int b = ((int) c[j++]) & 0xff; 556 tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b; 557 } 558 } 559 return tab; 560 } 561 562 private void readContents() { 563 // read GIF file content blocks 564 boolean done = false; 565 while (!(done || err())) { 566 int code = read(); 567 switch (code) { 568 case 0x2C: // image separator 569 readImage(); 570 break; 571 case 0x21: // extension 572 code = read(); 573 switch (code) { 574 case 0xf9: // graphics control extension 575 readGraphicControlExt(); 576 break; 577 case 0xff: // application extension 578 readBlock(); 579 String app = ""; 580 for (int i = 0; i < 11; i++) { 581 app += (char) block[i]; 582 } 583 if (app.equals("NETSCAPE2.0")) { 584 readNetscapeExt(); 585 } else { 586 skip(); // don't care 587 } 588 break; 589 default: // uninteresting extension 590 skip(); 591 } 592 break; 593 case 0x3b: // terminator 594 done = true; 595 break; 596 case 0x00: // bad byte, but keep going and see what happens 597 break; 598 default: 599 status = STATUS_FORMAT_ERROR; 600 } 601 } 602 } 603 604 private void readGraphicControlExt() { 605 read(); // block size 606 int packed = read(); // packed fields 607 dispose = (packed & 0x1c) >> 2; // disposal method 608 if (dispose == 0) { 609 dispose = 1; // elect to keep old image if discretionary 610 } 611 transparency = (packed & 1) != 0; 612 delay = readShort() * 10; // delay in milliseconds 613 transIndex = read(); // transparent color index 614 read(); // block terminator 615 } 616 617 private void readHeader() { 618 String id = ""; 619 for (int i = 0; i < 6; i++) { 620 id += (char) read(); 621 } 622 if (!id.startsWith("GIF")) { 623 status = STATUS_FORMAT_ERROR; 624 return; 625 } 626 readLSD(); 627 if (gctFlag && !err()) { 628 gct = readColorTable(gctSize); 629 bgColor = gct[bgIndex]; 630 } 631 } 632 633 private void readImage() { 634 ix = readShort(); // (sub)image position & size 635 iy = readShort(); 636 iw = readShort(); 637 ih = readShort(); 638 int packed = read(); 639 lctFlag = (packed & 0x80) != 0; // 1 - local color table flag 640 interlace = (packed & 0x40) != 0; // 2 - interlace flag 641 // 3 - sort flag 642 // 4-5 - reserved 643 lctSize = 2 << (packed & 7); // 6-8 - local color table size 644 if (lctFlag) { 645 lct = readColorTable(lctSize); // read table 646 act = lct; // make local table active 647 } else { 648 act = gct; // make global table active 649 if (bgIndex == transIndex) { 650 bgColor = 0; 651 } 652 } 653 int save = 0; 654 if (transparency) { 655 save = act[transIndex]; 656 act[transIndex] = 0; // set transparent color if specified 657 } 658 if (act == null) { 659 status = STATUS_FORMAT_ERROR; // no color table defined 660 } 661 if (err()) { 662 return; 663 } 664 decodeImageData(); // decode pixel data 665 skip(); 666 if (err()) { 667 return; 668 } 669 frameCount++; 670 // create new image to receive frame data 671 image = Bitmap.createBitmap(width, height, Config.ARGB_4444); 672 // createImage(width, height); 673 setPixels(); // transfer pixel data to image 674 if (gifFrame == null) { 675 gifFrame = new GifFrame(image, delay); 676 currentFrame = gifFrame; 677 } else { 678 GifFrame f = gifFrame; 679 while (f.nextFrame != null) { 680 f = f.nextFrame; 681 } 682 f.nextFrame = new GifFrame(image, delay); 683 } 684 // frames.addElement(new GifFrame(image, delay)); // add image to frame 685 // list 686 if (transparency) { 687 act[transIndex] = save; 688 } 689 resetFrame(); 690 action.parseOk(true, frameCount); 691 } 692 693 private void readLSD() { 694 // logical screen size 695 width = readShort(); 696 height = readShort(); 697 // packed fields 698 int packed = read(); 699 gctFlag = (packed & 0x80) != 0; // 1 : global color table flag 700 // 2-4 : color resolution 701 // 5 : gct sort flag 702 gctSize = 2 << (packed & 7); // 6-8 : gct size 703 bgIndex = read(); // background color index 704 pixelAspect = read(); // pixel aspect ratio 705 } 706 707 private void readNetscapeExt() { 708 do { 709 readBlock(); 710 if (block[0] == 1) { 711 // loop count sub-block 712 int b1 = ((int) block[1]) & 0xff; 713 int b2 = ((int) block[2]) & 0xff; 714 loopCount = (b2 << 8) | b1; 715 } 716 } while ((blockSize > 0) && !err()); 717 } 718 719 private int readShort() { 720 // read 16-bit value, LSB first 721 return read() | (read() << 8); 722 } 723 724 private void resetFrame() { 725 lastDispose = dispose; 726 lrx = ix; 727 lry = iy; 728 lrw = iw; 729 lrh = ih; 730 lastImage = image; 731 lastBgColor = bgColor; 732 dispose = 0; 733 transparency = false; 734 delay = 0; 735 lct = null; 736 } 737 738 /** 739 * Skips variable length blocks up to and including next zero length block. 740 */ 741 private void skip() { 742 do { 743 readBlock(); 744 } while ((blockSize > 0) && !err()); 745 } 746 }
  1 import java.io.InputStream;
  2 import android.content.Context;
  3 import android.content.res.Resources;
  4 import android.graphics.Bitmap;
  5 import android.graphics.Canvas;
  6 import android.graphics.Rect;
  7 import android.os.Handler;
  8 import android.os.Message;
  9 import android.os.SystemClock;
 10 import android.util.AttributeSet;
 11 import android.util.Log;
 12 import android.view.View;
 13 
 14 /**
 15  * GifView<br>
 16  * 本类可以显示一个gif动画,其使用方法和android的其它view(如imageview)一样。<br>
 17  * 如果要显示的gif太大,会出现OOM的问题。
 18  */
 19 public class GifView extends View implements GifAction {
 20 
 21     /** gif解码器 */
 22     private GifDecoder gifDecoder = null;
 23     /** 当前要画的帧的图 */
 24     private Bitmap currentImage = null;
 25 
 26     private boolean isRun = true;
 27 
 28     private boolean pause = false;
 29 
 30     private int showWidth = -1;
 31     private int showHeight = -1;
 32     private Rect rect = null;
 33 
 34     private DrawThread drawThread = null;
 35 
 36     private GifImageType animationType = GifImageType.SYNC_DECODER;
 37 
 38     /**
 39      * 解码过程中,Gif动画显示的方式<br>
 40      * 如果图片较大,那么解码过程会比较长,这个解码过程中,gif如何显示
 41      * 
 42      * @author liao
 43      *
 44      */
 45     public enum GifImageType {
 46         /**
 47          * 在解码过程中,不显示图片,直到解码全部成功后,再显示
 48          */
 49         WAIT_FINISH(0),
 50         /**
 51          * 和解码过程同步,解码进行到哪里,图片显示到哪里
 52          */
 53         SYNC_DECODER(1),
 54         /**
 55          * 在解码过程中,只显示第一帧图片
 56          */
 57         COVER(2);
 58 
 59         GifImageType(int i) {
 60             nativeInt = i;
 61         }
 62 
 63         final int nativeInt;
 64     }
 65 
 66     public GifView(Context context) {
 67         super(context);
 68 
 69     }
 70 
 71     public GifView(Context context, AttributeSet attrs) {
 72         this(context, attrs, 0);
 73     }
 74 
 75     public GifView(Context context, AttributeSet attrs, int defStyle) {
 76         super(context, attrs, defStyle);
 77 
 78     }
 79 
 80     /**
 81      * 设置图片,并开始解码
 82      * 
 83      * @param gif
 84      *            要设置的图片
 85      */
 86     private void setGifDecoderImage(byte[] gif) {
 87         if (gifDecoder != null) {
 88             gifDecoder.free();
 89             gifDecoder = null;
 90         }
 91         gifDecoder = new GifDecoder(gif, this);
 92         gifDecoder.start();
 93     }
 94 
 95     /**
 96      * 设置图片,开始解码
 97      * 
 98      * @param is
 99      *            要设置的图片
100      */
101     private void setGifDecoderImage(InputStream is) {
102         if (gifDecoder != null) {
103             gifDecoder.free();
104             gifDecoder = null;
105         }
106         gifDecoder = new GifDecoder(is, this);
107         gifDecoder.start();
108     }
109 
110     /**
111      * 以字节数据形式设置gif图片
112      * 
113      * @param gif
114      *            图片
115      */
116     public void setGifImage(byte[] gif) {
117         setGifDecoderImage(gif);
118     }
119 
120     /**
121      * 以字节流形式设置gif图片
122      * 
123      * @param is
124      *            图片
125      */
126     public void setGifImage(InputStream is) {
127         setGifDecoderImage(is);
128     }
129 
130     /**
131      * 以资源形式设置gif图片
132      * 
133      * @param resId
134      *            gif图片的资源ID
135      */
136     public void setGifImage(int resId) {
137         Resources r = this.getResources();
138         InputStream is = r.openRawResource(resId);
139         setGifDecoderImage(is);
140     }
141 
142     protected void onDraw(Canvas canvas) {
143         super.onDraw(canvas);
144         if (gifDecoder == null)
145             return;
146         if (currentImage == null) {
147             currentImage = gifDecoder.getImage();
148         }
149         if (currentImage == null) {
150             return;
151         }
152         int saveCount = canvas.getSaveCount();
153         canvas.save();
154         canvas.translate(getPaddingLeft(), getPaddingTop());
155         if (showWidth == -1) {
156             canvas.drawBitmap(currentImage, 0, 0, null);
157         } else {
158             canvas.drawBitmap(currentImage, null, rect, null);
159         }
160         canvas.restoreToCount(saveCount);
161     }
162 
163     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
164         int pleft = getPaddingLeft();
165         int pright = getPaddingRight();
166         int ptop = getPaddingTop();
167         int pbottom = getPaddingBottom();
168 
169         int widthSize;
170         int heightSize;
171 
172         int w;
173         int h;
174 
175         if (gifDecoder == null) {
176             w = 1;
177             h = 1;
178         } else {
179             w = gifDecoder.width;
180             h = gifDecoder.height;
181         }
182 
183         w += pleft + pright;
184         h += ptop + pbottom;
185 
186         w = Math.max(w, getSuggestedMinimumWidth());
187         h = Math.max(h, getSuggestedMinimumHeight());
188 
189         widthSize = resolveSize(w, widthMeasureSpec);
190         heightSize = resolveSize(h, heightMeasureSpec);
191 
192         setMeasuredDimension(widthSize, heightSize);
193     }
194 
195     /**
196      * 只显示第一帧图片<br>
197      * 调用本方法后,gif不会显示动画,只会显示gif的第一帧图
198      */
199     public void showCover() {
200         if (gifDecoder == null)
201             return;
202         pause = true;
203         currentImage = gifDecoder.getImage();
204         invalidate();
205     }
206 
207     /**
208      * 继续显示动画<br>
209      * 本方法在调用showCover后,会让动画继续显示,如果没有调用showCover方法,则没有任何效果
210      */
211     public void showAnimation() {
212         if (pause) {
213             pause = false;
214         }
215     }
216 
217     /**
218      * 设置gif在解码过程中的显示方式<br>
219      * <strong>本方法只能在setGifImage方法之前设置,否则设置无效</strong>
220      * 
221      * @param type
222      *            显示方式
223      */
224     public void setGifImageType(GifImageType type) {
225         if (gifDecoder == null)
226             animationType = type;
227     }
228 
229     /**
230      * 设置要显示的图片的大小<br>
231      * 当设置了图片大小 之后,会按照设置的大小来显示gif(按设置后的大小来进行拉伸或压缩)
232      * 
233      * @param width
234      *            要显示的图片宽
235      * @param height
236      *            要显示的图片高
237      */
238     public void setShowDimension(int width, int height) {
239         if (width > 0 && height > 0) {
240             showWidth = width;
241             showHeight = height;
242             rect = new Rect();
243             rect.left = 0;
244             rect.top = 0;
245             rect.right = width;
246             rect.bottom = height;
247         }
248     }
249 
250     public void parseOk(boolean parseStatus, int frameIndex) {
251         if (parseStatus) {
252             if (gifDecoder != null) {
253                 switch (animationType) {
254                 case WAIT_FINISH:
255                     if (frameIndex == -1) {
256                         if (gifDecoder.getFrameCount() > 1) { // 当帧数大于1时,启动动画线程
257                             DrawThread dt = new DrawThread();
258                             dt.start();
259                         } else {
260                             reDraw();
261                         }
262                     }
263                     break;
264                 case COVER:
265                     if (frameIndex == 1) {
266                         currentImage = gifDecoder.getImage();
267                         reDraw();
268                     } else if (frameIndex == -1) {
269                         if (gifDecoder.getFrameCount() > 1) {
270                             if (drawThread == null) {
271                                 drawThread = new DrawThread();
272                                 drawThread.start();
273                             }
274                         } else {
275                             reDraw();
276                         }
277                     }
278                     break;
279                 case SYNC_DECODER:
280                     if (frameIndex == 1) {
281                         currentImage = gifDecoder.getImage();
282                         reDraw();
283                     } else if (frameIndex == -1) {
284                         reDraw();
285                     } else {
286                         if (drawThread == null) {
287                             drawThread = new DrawThread();
288                             drawThread.start();
289                         }
290                     }
291                     break;
292                 }
293 
294             } else {
295                 Log.e("gif", "parse error");
296             }
297 
298         }
299     }
300 
301     private void reDraw() {
302         if (redrawHandler != null) {
303             Message msg = redrawHandler.obtainMessage();
304             redrawHandler.sendMessage(msg);
305         }
306     }
307 
308     private Handler redrawHandler = new Handler() {
309         public void handleMessage(Message msg) {
310             invalidate();
311         }
312     };
313 
314     /**
315      * 动画线程317      * @author liao319      */
320     private class DrawThread extends Thread {
321         public void run() {
322             if (gifDecoder == null) {
323                 return;
324             }
325             while (isRun) {
326                 if (pause == false) {
327                     // if(gifDecoder.parseOk()){
328                     GifFrame frame = gifDecoder.next();
329                     currentImage = frame.image;
330                     long sp = frame.delay;
331                     if (redrawHandler != null) {
332                         Message msg = redrawHandler.obtainMessage();
333                         redrawHandler.sendMessage(msg);
334                         SystemClock.sleep(sp);
335                     } else {
336                         break;
337                     }
338                     // }else{
339                     // currentImage = gifDecoder.getImage();
340                     // break;
341                     // }
342                 } else {
343                     SystemClock.sleep(10);
344                 }
345             }
346         }
347     }
348 
349 }

   以上是Gif用到的所有类库。

   以下是该控件的使用方法。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout 
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="fill_parent"
 5     android:layout_height="fill_parent"
 6     android:orientation="vertical" >
 7 
 8     <com.test.gifview.GifView
 9         android:id="@+id/gif1"
10         android:layout_width="wrap_content"
11         android:layout_height="wrap_content"
12         android:enabled="false"
13         android:paddingRight="14px" />
14 
15     <TextView
16         android:id="@+id/tsxt"
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:enabled="false"
20         android:paddingRight="4px"
21         android:text="click the Angel" />
22 
23     <com.test.gifview.GifView
24         android:id="@+id/gif2"
25         android:layout_width="wrap_content"
26         android:layout_height="wrap_content"
27         android:enabled="false"
28         android:paddingLeft="14px"
29         android:paddingTop="4px" />
30 
31 </LinearLayout>
 1 import android.app.Activity;
 2 import android.os.Bundle;
 3 import android.view.View;
 4 import android.view.View.OnClickListener;
 6 import com.test.gifview.GifView;
 7 import com.test.gifview.GifView.GifImageType;
 8 
 9 public class TestAction extends Activity implements OnClickListener {
10 
11     private GifView gf1;
12     private GifView gf2;
13     private boolean f = true;
14 
15     public void onCreate(Bundle icicle) {
16         super.onCreate(icicle);
17         // Log.d("dddddddddd",Environment.getRootDirectory().getAbsolutePath());
18         // LinearLayout ll = new LinearLayout(this);
19         // LayoutParams la = new LayoutParams(LayoutParams.FILL_PARENT,
20         // LayoutParams.FILL_PARENT);
21         //
22         // ll.setLayoutParams(la);
23         // gf1 = new GifView(this);
24         // gf2 = new GifView(this);
25         //
26         // gf1.setGifImage(R.drawable.gif1);
27         // gf2.setGifImage(R.drawable.gif2);
28         //
29         // ll.addView(gf1);
30         // ll.addView(gf2);
31         //
32         // setContentView(ll);
33 
34         setContentView(R.layout.gif);
35         gf1 = (GifView) findViewById(R.id.gif1);
36         // 设置Gif图片源
37         gf1.setGifImage(R.drawable.big_mm);
38         // 添加监听器
39         gf1.setOnClickListener(this);
40 
41         gf2 = (GifView) findViewById(R.id.gif2);
42         // 设置加载方式:先加载后显示、边加载边显示、只显示第一帧再显示
43         gf2.setGifImageType(GifImageType.COVER);
44         // 设置显示的大小,拉伸或者压缩
45         gf2.setShowDimension(300, 300);
46         // 设置Gif图片源
47         gf2.setGifImage(R.drawable.a);
48         // 添加监听器
49         // gf2.setOnClickListener(this);
50     }
51 
52     public void onClick(View v) {
53         if (f) {
54             // 点击停止动画
55             gf1.showCover();
56             f = false;
57         } else {
58             // 点击播放动画
59             gf1.showAnimation();
60             f = true;
61         }
62     }
63 }

 https://code.google.com/archive/p/gifview/downloads

原文地址:https://www.cnblogs.com/androidsj/p/4973794.html