[Lập trình Android] Tạo một Password ngẫu nhiên

Đầu tiên, tạo một ứng dụng Android và đặt tên là GenerateRandomPassword với Activity chính là MainActivity.
Chúng ta cần một class để xây dựng mật khẩu. Tạo class RandomPasswordGenerator với nội dung sau

public class RandomPasswordGenerator {
 
	private static final String ALPHA_CAPS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	private static final String ALPHA = "abcdefghijklmnopqrstuvwxyz";
	private static final String NUM = "0123456789";
	private static final String SPL_CHARS = "!@#$%^&*_=+-/";
 
	public static char[] generatePswd(int minLen, int maxLen,
			int noOfCAPSAlpha, int noOfDigits, int noOfSplChars) {
		if (minLen > maxLen)
			throw new IllegalArgumentException("Min. Length > Max. Length!");
		if ((noOfCAPSAlpha + noOfDigits + noOfSplChars) > minLen)
			throw new IllegalArgumentException(
					"Min. Length should be atleast sum of (CAPS, DIGITS, SPL CHARS) Length!");
		Random rnd = new Random();
		int len = rnd.nextInt(maxLen - minLen + 1) + minLen;
		char[] pswd = new char[len];
		int index = 0;
		for (int i = 0; i < noOfCAPSAlpha; i++) {
			index = getNextIndex(rnd, len, pswd);
			pswd[index] = ALPHA_CAPS.charAt(rnd.nextInt(ALPHA_CAPS.length()));
		}
		for (int i = 0; i < noOfDigits; i++) {
			index = getNextIndex(rnd, len, pswd);
			pswd[index] = NUM.charAt(rnd.nextInt(NUM.length()));
		}
		for (int i = 0; i < noOfSplChars; i++) {
			index = getNextIndex(rnd, len, pswd);
			pswd[index] = SPL_CHARS.charAt(rnd.nextInt(SPL_CHARS.length()));
		}
		for (int i = 0; i < len; i++) {
			if (pswd[i] == 0) {
				pswd[i] = ALPHA.charAt(rnd.nextInt(ALPHA.length()));
			}
		}
		return pswd;
	}
 
	private static int getNextIndex(Random rnd, int len, char[] pswd) {
		int index = rnd.nextInt(len);
		while (pswd[index = rnd.nextInt(len)] != 0)
			;
		return index;
	}
 
}

Class này chứa một phương thức staticgeneratePswd() với 5 tham số truyền vào:
minLen – tối thiểu số ký tự
maxLen – tối đa số ký tự
noOfCAPSAlpha – số lượng ký tự in hoa
noOfDigits – số lượng ký tự là số
noOfSplChars – số lượng ký tự đặc biệt
Tiếp theo, chỉnh sửa lại layout cho MainActivity như sau

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <TextView
        android:id="@+id/txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:textSize="20dp" />
 
</RelativeLayout>

Chúng ta sử dụng TextView để hiển thị kết quả các mật khẩu được tạo.
Cuối cùng là chỉnh sửa lại MainActivity để tạo 10 mật khẩu ngẫu nhiên dùng class RandomPasswordGenerator

public class MainActivity extends Activity {
 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
 
		TextView txt = (TextView) findViewById(R.id.txt);
 
		int noOfCAPSAlpha = 1;
		int noOfDigits = 1;
		int noOfSplChars = 1;
		int minLen = 8;
		int maxLen = 12;
 
		String res = "";
		for (int i = 0; i < 10; i++) {
			char[] pswd = RandomPasswordGenerator.generatePswd(minLen, maxLen,
					noOfCAPSAlpha, noOfDigits, noOfSplChars);
			res += "Len = " + pswd.length + ", " + new String(pswd) + "n";
		}
 
		txt.setText(res);
	}
}

Các password được tạo ra trong mỗi lần chạy ứng dụng sẽ khác nhau.

[Lập trình Android] Hướng dẫn giải nén một tập tin *.zip tải từ Internet trong ứng dụng Android

Đầu tiên, tạo một ứng dụng Android và đặt tên là UnzipFile với Activity chính là MainActivity.
Chúng ta bắt đầu bằng việc thêm permission cho ứng dụng. Mở tập tin Android Manifest, thêm vào 2 permission sau

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />

Quyền truy cập Internet để download tập tin *.zip về, sau đó sử dụng quyền ghi lên bộ nhớ ngoài để giải nén được tập tin.
Tiếp theo, chúng ta cần một class để giải nén tập tin. Tạo một class mới đặt tên là UnzipUtil, với nội dung sau

public class UnzipUtil {
	private String _zipFile;
	private String _location;
 
	public UnzipUtil(String zipFile, String location) {
		_zipFile = zipFile;
		_location = location;
		_dirChecker("");
	}
 
	public void unzip() {
		try {
			FileInputStream fin = new FileInputStream(_zipFile);
			ZipInputStream zin = new ZipInputStream(fin);
			ZipEntry ze = null;
			while ((ze = zin.getNextEntry()) != null) {
				if (ze.isDirectory()) {
					_dirChecker(ze.getName());
				} else {
					FileOutputStream fout = new FileOutputStream(_location
							+ ze.getName());
					byte[] buffer = new byte[8192];
					int len;
					while ((len = zin.read(buffer)) != -1) {
						fout.write(buffer, 0, len);
					}
					fout.close();
					zin.closeEntry();
				}
			}
			zin.close();
		} catch (Exception e) {
			Log.e("Decompress", "unzip", e);
		}
	}
 
	private void _dirChecker(String dir) {
		File f = new File(_location + dir);
		if (!f.isDirectory()) {
			f.mkdirs();
		}
	}
}

Chúng ta sẽ sử dụng class này để giải nén một tập tin *.zip từ đường dẫn zipFile vào trong thư mục có đường dẫn location.
Tiếp theo, ta chỉnh sửa lại MainActivity để thực hiện việc download và giải nén *.zip.
Đầu tiên, ta khai báo một số biến cần dùng cho ứng dụng

	private ProgressDialog mProgressDialog;
	String unzipLocation = Environment.getExternalStorageDirectory() + "/";
	String zipFile = Environment.getExternalStorageDirectory()
			+ "/download.zip";

Giả sử ta sẽ download tập tin *.zip từ đường dẫn: http://yeulaptrinh.vn/demo/down.zip
Tiếp theo, trong onCreate của MainActivity, chúng ta sẽ chạy một AsyncTask để download *.zip

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		DownloadMapAsync mew = new DownloadMapAsync();
		mew.execute("http://yeulaptrinh.vn/demo/down.zip");
	}

Nội dung của DownloadMapAsync như sau

	class DownloadMapAsync extends AsyncTask<String, String, Void> {
		boolean result;
 
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			mProgressDialog = new ProgressDialog(MainActivity.this);
			mProgressDialog.setMessage("Downloading Zip File..");
			mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
			mProgressDialog.setCancelable(false);
			mProgressDialog.show();
		}
 
		@Override
		protected Void doInBackground(String... aurl) {
			int count;
			try {
				URL url = new URL(aurl[0]);
				URLConnection conexion = url.openConnection();
				conexion.connect();
				int lenghtOfFile = conexion.getContentLength();
				InputStream input = new BufferedInputStream(url.openStream());
				OutputStream output = new FileOutputStream(zipFile);
				byte data[] = new byte[1024];
				long total = 0;
				while ((count = input.read(data)) != -1) {
					total += count;
					publishProgress("" + (int) ((total * 100) / lenghtOfFile));
					output.write(data, 0, count);
				}
				output.close();
				input.close();
				result = true;
			} catch (Exception e) {
				result = false;
			}
			return null;
		}
 
		protected void onProgressUpdate(String... progress) {
			mProgressDialog.setProgress(Integer.parseInt(progress[0]));
		}
 
		@Override
		protected void onPostExecute(Void params) {
			mProgressDialog.dismiss();
			if (result) {
				new UnZipTask().execute();
			} else {
				Toast.makeText(MainActivity.this, "Download failed",
						Toast.LENGTH_SHORT).show();
			}
		}
	}

Sau khi download xong tập tin vào trong đường dẫn zipFile, chúng ta sẽ tiếp tục cho chạy một AsyncTask khác để giải nén tập tin, đó là

new UnZipTask().execute();

Nội dung UnZipTask như sau:

	private class UnZipTask extends AsyncTask<Void, Void, Boolean> {
 
		@Override
		protected void onPreExecute() {
			mProgressDialog = new ProgressDialog(MainActivity.this);
			mProgressDialog
					.setMessage("Please Wait...Extracting zip file ... ");
			mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
			mProgressDialog.setCancelable(false);
			mProgressDialog.show();
		}
 
		@Override
		protected Boolean doInBackground(Void... params) {
			try {
				UnzipUtil d = new UnzipUtil(zipFile, unzipLocation);
				d.unzip();
			} catch (Exception e) {
				return false;
			}
			return true;
		}
 
		@Override
		protected void onPostExecute(Boolean result) {
			mProgressDialog.dismiss();
			if (!result) {
				Toast.makeText(MainActivity.this, "Unzip failed",
						Toast.LENGTH_SHORT).show();
			}
		}
	}

Chúng ta sử dụng class UnzipUtil đã tạo ở trên để giải nén

				UnzipUtil d = new UnzipUtil(zipFile, unzipLocation);
				d.unzip();

Nếu sử dụng máy ảo để chạy thử ứng dụng, bạn mở phần quản lý File của máy ảo sẽ thấy tập tin down.zip được download và các tập tin giải nén từ down.zip.

[Android] Chèn ảnh động *.gif trong Android

Tạo một ứng dụng Android và đặt tên là AnimationGif với Activity chính là MainActivity.
Tiếp theo, ta copy một hình ảnh có định dạng *.gif vào trong thư mục /assets. Ở đây mình sẽ sử dụng tập tin test.gif.

Cách 1: Sử dụng GifDecoderView

Để hiển thị được ảnh dạng *.gif, chúng ta sẽ sử dụng một custom view có tên là GifDecoderView, và cần một class để decode tập tin *.gif, có tên là GifDecoder.


GifDecoder.java

public class GifDecoder {
        /**
         * File read status: No errors.
         */
        public static final int STATUS_OK = 0;
        /**
         * File read status: Error decoding file (may be partially decoded)
         */
        public static final int STATUS_FORMAT_ERROR = 1;
        /**
         * File read status: Unable to open source.
         */
        public static final int STATUS_OPEN_ERROR = 2;
        /** max decoder pixel stack size */
        protected static final int MAX_STACK_SIZE = 4096;
        protected InputStream in;
        protected int status;
        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 lastBitmap; // previous frame
        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
        // 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;
 
        private static class GifFrame {
                public GifFrame(Bitmap im, int del) {
                        image = im;
                        delay = del;
                }
 
                public Bitmap image;
                public int delay;
        }
 
        /**
         * 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 = frames.elementAt(n).delay;
                }
                return delay;
        }
 
        /**
         * Gets the number of frames read from file.
         * 
         * @return frame count
         */
        public int getFrameCount() {
                return frameCount;
        }
 
        /**
         * Gets the first (or only) image read.
         * 
         * @return BufferedBitmap containing first frame, or null if none.
         */
        public Bitmap getBitmap() {
                return getFrame(0);
        }
 
        /**
         * Gets the "Netscape" iteration count, if any. A count of 0 means repeat indefinitiely.
         * 
         * @return iteration count if one was specified, else 1.
         */
        public int getLoopCount() {
                return loopCount;
        }
 
        /**
         * Creates new frame image from current data (and previous frames as specified by their disposition codes).
         */
        protected void setPixels() {
                // expose destination image's pixels as int array
                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) {
                                        lastBitmap = getFrame(n - 1);
                                } else {
                                        lastBitmap = null;
                                }
                        }
                        if (lastBitmap != null) {
                                lastBitmap.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;
                                                break;
                                        default:
                                                break;
                                        }
                                }
                                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.ARGB_4444);
        }
 
        /**
         * Gets the image contents of frame n.
         * 
         * @return BufferedBitmap representation of frame, or null if n is invalid.
         */
        public Bitmap getFrame(int n) {
                if (frameCount <= 0)
                        return null;
                n = n % frameCount;
                return ((GifFrame) frames.elementAt(n)).image;
        }
 
        /**
         * Reads GIF image from stream
         * 
         * @param is
         *          containing GIF file.
         * @return read status code (0 = no errors)
         */
        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) {
                }
                return status;
        }
 
        /**
         * Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
         */
        protected void decodeBitmapData() {
                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[MAX_STACK_SIZE];
                }
                if (suffix == null) {
                        suffix = new byte[MAX_STACK_SIZE];
                }
                if (pixelStack == null) {
                        pixelStack = new byte[MAX_STACK_SIZE + 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 >= MAX_STACK_SIZE) {
                                        break;
                                }
                                pixelStack[top++] = (byte) first;
                                prefix[available] = (short) old_code;
                                suffix[available] = (byte) first;
                                available++;
                                if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) {
                                        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
                }
        }
 
        /**
         * Returns true if an error was encountered during reading/decoding
         */
        protected boolean err() {
                return status != STATUS_OK;
        }
 
        /**
         * Initializes or re-initializes reader
         */
        protected void init() {
                status = STATUS_OK;
                frameCount = 0;
                frames = new Vector<GifFrame>();
                gct = null;
                lct = null;
        }
 
        /**
         * Reads a single byte from the input stream.
         */
        protected int read() {
                int curByte = 0;
                try {
                        curByte = in.read();
                } catch (Exception e) {
                        status = STATUS_FORMAT_ERROR;
                }
                return curByte;
        }
 
        /**
         * Reads next variable length block from input.
         * 
         * @return number of bytes stored in "buffer"
         */
        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;
        }
 
        /**
         * Reads color table as 256 RGB integer values
         * 
         * @param ncolors
         *          int number of colors to read
         * @return int array containing 256 colors (packed ARGB with full alpha)
         */
        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;
        }
 
        /**
         * Main file parser. Reads GIF content blocks.
         */
        protected void readContents() {
                // read GIF file content blocks
                boolean done = false;
                while (!(done || err())) {
                        int code = read();
                        switch (code) {
                        case 0x2C: // image separator
                                readBitmap();
                                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;
                                case 0xfe:// comment extension
                                        skip();
                                        break;
                                case 0x01:// plain text extension
                                        skip();
                                        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;
                        }
                }
        }
 
        /**
         * Reads Graphics Control Extension values
         */
        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
        }
 
        /**
         * Reads GIF file header information.
         */
        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];
                }
        }
 
        /**
         * Reads next frame image
         */
        protected void readBitmap() {
                ix = readShort(); // (sub)image position & size
                iy = readShort();
                iw = readShort();
                ih = readShort();
                int packed = read();
                lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace
                lctSize = (int) Math.pow(2, (packed & 0x07) + 1);
                // 3 - sort flag
                // 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color
                // table size
                interlace = (packed & 0x40) != 0;
                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;
                }
                decodeBitmapData(); // decode pixel data
                skip();
                if (err()) {
                        return;
                }
                frameCount++;
                // create new image to receive frame data
                image = Bitmap.createBitmap(width, height, Config.ARGB_4444);
                setPixels(); // transfer pixel data to image
                frames.addElement(new GifFrame(image, delay)); // add image to frame
                // list
                if (transparency) {
                        act[transIndex] = save;
                }
                resetFrame();
        }
 
        /**
         * Reads 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
        }
 
        /**
         * Reads Netscape extenstion to obtain iteration count
         */
        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());
        }
 
        /**
         * Reads next 16-bit value, LSB first
         */
        protected int readShort() {
                // read 16-bit value, LSB first
                return read() | (read() << 8);
        }
 
        /**
         * Resets frame state for reading next image.
         */
        protected void resetFrame() {
                lastDispose = dispose;
                lrx = ix;
                lry = iy;
                lrw = iw;
                lrh = ih;
                lastBitmap = 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());
        }
}

GifDecoderView.java

public class GifDecoderView extends ImageView {
 
	public GifDecoderView(Context context) {
		super(context);
	}
 
	public GifDecoderView(Context context, InputStream stream) {
		super(context);
		playGif(stream);
	}
 
	private boolean mIsPlayingGif = false;
 
	private GifDecoder mGifDecoder;
 
	private Bitmap mTmpBitmap;
 
	final Handler mHandler = new Handler();
 
	final Runnable mUpdateResults = new Runnable() {
		public void run() {
			if (mTmpBitmap != null && !mTmpBitmap.isRecycled()) {
				GifDecoderView.this.setImageBitmap(mTmpBitmap);
			}
		}
	};
 
	private void playGif(InputStream stream) {
		mGifDecoder = new GifDecoder();
		mGifDecoder.read(stream);
 
		mIsPlayingGif = true;
 
		new Thread(new Runnable() {
			public void run() {
				final int n = mGifDecoder.getFrameCount();
				final int ntimes = mGifDecoder.getLoopCount();
				int repetitionCounter = 0;
				do {
					for (int i = 0; i < n; i++) {
						mTmpBitmap = mGifDecoder.getFrame(i);
						int t = mGifDecoder.getDelay(i);
						mHandler.post(mUpdateResults);
						try {
							Thread.sleep(t);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					if (ntimes != 0) {
						repetitionCounter++;
					}
				} while (mIsPlayingGif && (repetitionCounter <= ntimes));
			}
		}).start();
	}
 
	public void stopRendering() {
		mIsPlayingGif = true;
	}
}

Tiếp theo, ta chỉnh sửa MainActivity để hiển thị tập tin test.gif.
Chúng ta đọc tập tin test.gif vào trong một InputStream

		InputStream stream = null;
		try {
			stream = getAssets().open(&quot;test.gif&quot;);
		} catch (IOException e) {
			e.printStackTrace();
		}

Sau đó khởi tạo một GifDecoderView với tham số là InputStream vừa có như sau

		GifDecoderView view = new GifDecoderView(this, stream);

Cuối cùng là gán nội dung vào view của Activity để hiển thị:

		setContentView(view);

Cách 2: Sử dụng GifWebView

Cách này, chúng ta sẽ sử dụng GifWebView. Thực tế, ở đây ta sử dụng WebView, chỉ là custom lại để hiển thị ảnh từ đường dẫn đưa vào.
Tạo một class GifWebView với nội dung sau.

public class GifWebView extends WebView {
 
	public GifWebView(Context context, String path) {
		super(context);
		loadUrl(path);
	}
}

Sau đó, chỉnh sửa lại MainActivity. Chúng ta chỉ cần một đoạn mã sau để hiển thị test.gif

		GifWebView view = new GifWebView(this,"file:///android_asset/test.gif");

Và gán view này để hiển thị trên thiết bị:

		setContentView(view);