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 }