良好的开端,是成功的一半。A good beginning is half done. -_-
本文目录
● 一、什么是GIF
● 二、Android系统和应用如何播放GIF
一、【什么是GIF】
Gif动态图片,是通过对多图层的图片,按时间进行不同的切换,以达到动画的效果。Gif动态图片的特点是,图片小,易于随时安置调用。比起一张单调的静态图片,Gif格式的动态图片明显更加的生动和有意思。
二、【Android系统和应用如何播放GIF】
在Android系统的原生控件ImageView并不支持播放GIF格式的动态图片。那么Android系统和应用是如何播放Gif格式的动态图片呢?让我们一块去翻看一下Android的API Demos。
在Android 系统(android-17)提供的API Demos列表—>Graphics—>BitmapDecode界面中,我们可以看到一幅飘杨的旗帜,正是API Demos为我们提供的播放Gif动态图片的示例。对应AndroidADT—>sdk—>samples—>android-17—>ApiDemos中graphics包下的BitmapDecode.java类。
图1 API Demos Gif动态图示例
接下来,去翻看一下BitmapDecode.java类的源码。从其中分离出来的相关源码并整理如下:
1. Copy API Demos源码,自定义实现能够播放Gif动态图片的控件GifMovieView.java
package com.lzz.gifmovie; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import android.content.Context; import android.graphics.Canvas; import android.graphics.Movie; import android.os.Environment; import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.widget.Toast; /** * 自定义控件实现播放Gif动态图片 * @author lzz * Movie.decodeByteArray(data, offset, length); * Movie.decodeStream(is); * Movie.decodeFile(pathName); //这个方法不能正常显示Gif,需要指定BufferSize * You need to use InputStream with a BufferSize while loading .GIF images From SDCARD. * It‘ll work for sure. */ public class GifMovieView extends View { private static final String TAG = GifMovieView.class.getSimpleName(); private static final String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator; /** * 这个类目前文档没有API官方说明 */ private Movie mMovie; /** * 记录起始时间 */ private long mMovieStart; /** * gif图片文件名(包含后缀.gif) */ private String gifFileName; /** * gif图片所在项目的资源id */ private int gifResourceId; public GifMovieView(Context context) { super(context); System.out.println("第1个构造方法"); this.gifResourceId = R.drawable.animated_gif; this.gifFileName = "giftest1.gif"; playGifImage(gifResourceId); playGifImage(this.gifFileName); } public GifMovieView(Context context, AttributeSet attrs) { super(context, attrs); Log.i(TAG, "第2个构造方法"); this.gifResourceId = R.drawable.animated_gif; this.gifFileName = "giftest2.gif"; playGifImage(gifResourceId); playGifImage(gifFileName); } public GifMovieView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); Log.i(TAG, "第3个构造方法"); this.gifResourceId = R.drawable.animated_gif; this.gifFileName = "giftest3.gif"; playGifImage(gifResourceId); playGifImage(gifFileName); } /** * 设置所要播放的gif图片的文件名 * @param gifFileName */ public void playGifImage(String gifFileName) { this.gifFileName = gifFileName; File file = new File(externalPath + gifFileName); System.out.println("gif文件路径: "+ externalPath + gifFileName); if(file.exists()){ InputStream inputStream = null; try { inputStream = new BufferedInputStream(new FileInputStream(file)); byte[] array = streamToBytes(inputStream); mMovie = Movie.decodeByteArray(array, 0, array.length); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if(null != inputStream){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }else{ Toast.makeText(this.getContext(), "所要播放的gif图片不存在", Toast.LENGTH_LONG).show(); } } /** * 把流转换成byte[] * @param is 输入流 * @return 字节数组 */ private static byte[] streamToBytes(InputStream is) { ByteArrayOutputStream os = new ByteArrayOutputStream(1024); byte[] buffer = new byte[1024]; int len; try { while ((len = is.read(buffer)) >= 0) { os.write(buffer, 0, len); } } catch (java.io.IOException e) { } return os.toByteArray(); } /** * 设置所要播放的gif图片资源id * @param id */ public void playGifImage(int id) { if(0 != id){ this.gifResourceId = id; InputStream inputStream = this.getResources().openRawResource(id); mMovie = Movie.decodeStream(inputStream); }else{ Toast.makeText(this.getContext(), "所要播放的gif图片不存在", Toast.LENGTH_LONG).show(); } } /** * 重载onDraw()方法,实现gif图片的逐帧播放 */ @Override protected void onDraw(Canvas canvas) { long now = SystemClock.uptimeMillis(); // 当前时间 if(0 == mMovieStart){ // 如果第一帧,记录起始时间 mMovieStart = now; } if(null != mMovie){ int mDuration = mMovie.duration(); // 取出动画的时长 if(0 == mDuration){ mDuration = 1000; } // 算出需要显示第几帧 int realTime = (int)((now - mMovieStart) % mDuration); mMovie.setTime(realTime); // 需要显示的帧,绘制即可 mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height()); invalidate(); } } }
2. GifMovieActivity的布局文件activity_gif_movie.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/container" android:orientation="vertical" > </LinearLayout>
3. GifMovieActivity.java代码
package com.lzz.gifmovie; import android.annotation.SuppressLint; import android.app.ActionBar.LayoutParams; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.DisplayMetrics; import android.view.Gravity; import android.widget.LinearLayout; /** * 测试播放Gif动态图片的自定义控件GifMovieView * @author lzz * */ @SuppressLint("NewApi") public class GifMovieActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_gif_movie); // 获取屏幕的宽高 DisplayMetrics displayMetrics = new DisplayMetrics(); this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; System.out.println("手机屏幕分辨率为:" + screenWidth +"*" + screenHeight); LayoutParams params1 = new LayoutParams( LayoutParams.WRAP_CONTENT, screenHeight/3 ); params1.gravity = Gravity.CENTER; // 添加一个自定义的GifMovieView控件 GifMovieView gifMovieView1 = new GifMovieView(this); gifMovieView1.setLayoutParams(params1); gifMovieView1.playGifImage(R.drawable.animated_gif); LayoutParams params2 = new LayoutParams( LayoutParams.WRAP_CONTENT, screenHeight/2 ); params2.gravity = Gravity.CENTER; GifMovieView gifMovieView2 = new GifMovieView(this); gifMovieView2.setLayoutParams(params2); gifMovieView2.playGifImage("giftest1.gif"); // 往自定义控件上添加前面自定义的两个控件GifMovieView LinearLayout linearLayout = (LinearLayout) findViewById(R.id.container); linearLayout.addView(gifMovieView1); linearLayout.addView(gifMovieView2); } }
4. 重点记得在AndroidManifest.xml文中的<application>或者有播放Gif动态图的<activity>结点中
加入:android:hardwareAccelerated="false" 这一行代码。
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.lzz.gifmovie.GifMovieActivity" android:hardwareAccelerated="false" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
5. 有图有真相:
图2 自定义控件GifMovieView播放Gif动态图片效果