android 怎样显示gif动画
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 怎样显示gif动画相关的知识,希望对你有一定的参考价值。
android中现在没有直接显示gif的view,只能通过mediaplay来显示,且还常常不能正常显示出来,为此写了这个gifview,其用法和imageview一样使用方法:
1-把GifView.jar加入你的项目。
2-在xml中配置GifView的基本属性,GifView继承自View类,和Button、ImageView一样是一个UI控件。如:
<com.ant.liao.GifView android:id="@+id/gif2"
android:layout_ android:layout_
android:paddingTop="4px" android:paddingLeft="14px" android:enabled="false" />
3-在代码中配置常用属性:
// 从xml中得到GifView的句柄
gf1 = (GifView) findViewById(R.id.gif1);
// 设置Gif图片源
gf1.setGifImage(R.drawable.gif1);
// 添加监听器
gf1.setOnClickListener(this);
// 设置显示的大小,拉伸或者压缩
gf1.setShowDimension(300, 300);
// 设置加载方式:先加载后显示、边加载边显示、只显示第一帧再显示
gf1.setGifImageType(GifImageType.COVER); 参考技术A android中的android.graphics.Movie 这个类,这是android提供给我们的一个非常方便的工具。
首先,重写控件View,自定义一个展示gif图的GifView,代码如下:
package net.loonggg.gif.view;
import net.loonggg.gif.R;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
public class GifView extends View
/**
* 默认为1秒
*/
private static final int DEFAULT_MOVIE_DURATION = 1000;
private int mMovieResourceId;
private Movie mMovie;
private long mMovieStart;
private int mCurrentAnimationTime = 0;
private float mLeft;
private float mTop;
private float mScale;
private int mMeasuredMovieWidth;
private int mMeasuredMovieHeight;
private boolean mVisible = true;
private volatile boolean mPaused = false;
public GifView(Context context)
this(context, null);
public GifView(Context context, AttributeSet attrs)
this(context, attrs, R.styleable.CustomTheme_gifViewStyle);
public GifView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
setViewAttributes(context, attrs, defStyle);
@SuppressLint("NewApi")
private void setViewAttributes(Context context, AttributeSet attrs,
int defStyle)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
// 从描述文件中读出gif的值,创建出Movie实例
final TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.GifView, defStyle, R.style.Widget_GifView);
mMovieResourceId = array.getResourceId(R.styleable.GifView_gif, -1);
mPaused = array.getBoolean(R.styleable.GifView_paused, false);
array.recycle();
if (mMovieResourceId != -1)
mMovie = Movie.decodeStream(getResources().openRawResource(
mMovieResourceId));
/**
* 设置gif图资源
*
* @param movieResId
*/
public void setMovieResource(int movieResId)
this.mMovieResourceId = movieResId;
mMovie = Movie.decodeStream(getResources().openRawResource(
mMovieResourceId));
requestLayout();
public void setMovie(Movie movie)
this.mMovie = movie;
requestLayout();
public Movie getMovie()
return mMovie;
public void setMovieTime(int time)
mCurrentAnimationTime = time;
invalidate();
/**
* 设置暂停
*
* @param paused
*/
public void setPaused(boolean paused)
this.mPaused = paused;
if (!paused)
mMovieStart = android.os.SystemClock.uptimeMillis()
- mCurrentAnimationTime;
invalidate();
/**
* 判断gif图是否停止了
*
* @return
*/
public boolean isPaused()
return this.mPaused;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
if (mMovie != null)
int movieWidth = mMovie.width();
int movieHeight = mMovie.height();
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
float scaleW = (float) movieWidth / (float) maximumWidth;
mScale = 1f / scaleW;
mMeasuredMovieWidth = maximumWidth;
mMeasuredMovieHeight = (int) (movieHeight * mScale);
setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);
else
setMeasuredDimension(getSuggestedMinimumWidth(),
getSuggestedMinimumHeight());
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
super.onLayout(changed, l, t, r, b);
mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
mVisible = getVisibility() == View.VISIBLE;
@Override
protected void onDraw(Canvas canvas)
if (mMovie != null)
if (!mPaused)
updateAnimationTime();
drawMovieFrame(canvas);
invalidateView();
else
drawMovieFrame(canvas);
@SuppressLint("NewApi")
private void invalidateView()
if (mVisible)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
postInvalidateOnAnimation();
else
invalidate();
private void updateAnimationTime()
long now = android.os.SystemClock.uptimeMillis();
// 如果第一帧,记录起始时间
if (mMovieStart == 0)
mMovieStart = now;
// 取出动画的时长
int dur = mMovie.duration();
if (dur == 0)
dur = DEFAULT_MOVIE_DURATION;
// 算出需要显示第几帧
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
private void drawMovieFrame(Canvas canvas)
// 设置要显示的帧,绘制即可
mMovie.setTime(mCurrentAnimationTime);
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScale, mScale);
mMovie.draw(canvas, mLeft / mScale, mTop / mScale);
canvas.restore();
@SuppressLint("NewApi")
@Override
public void onScreenStateChanged(int screenState)
super.onScreenStateChanged(screenState);
mVisible = screenState == SCREEN_STATE_ON;
invalidateView();
@SuppressLint("NewApi")
@Override
protected void onVisibilityChanged(View changedView, int visibility)
super.onVisibilityChanged(changedView, visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
@Override
protected void onWindowVisibilityChanged(int visibility)
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
Movie其实管理着GIF动画中的多个帧,只需要通过 setTime() 一下就可以让它在draw()的时候绘出相应的那帧图像。通过当前时间与duration之间的换算关系,是很容易实现GIF动起来的效果。
其次,在xml布局文件中,把这个view定义进去,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<net.loonggg.gif.view.GifView
android:id="@+id/gif1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
android:enabled="false" />
<net.loonggg.gif.view.GifView
android:id="@+id/gif2"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:enabled="false" />
</LinearLayout>
最后,在MainActivity中的使用,代码如下:
package net.loonggg.gif;
import net.loonggg.gif.view.GifView;
import android.app.Activity;
import android.os.Bundle;
public class Gif extends Activity
private GifView gif1, gif2;
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gif1 = (GifView) findViewById(R.id.gif1);
// 设置背景gif图片资源
gif1.setMovieResource(R.raw.kitty);
gif2 = (GifView) findViewById(R.id.gif2);
gif2.setMovieResource(R.raw.b);
// 设置暂停
// gif2.setPaused(true);
注意:与ImageView和其他View唯一的区别在于我加了一个gif属性。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="GifView">
<attr name="gif" format="reference" />
<attr name="paused" format="boolean" />
</declare-styleable>
<declare-styleable name="CustomTheme">
<attr name="gifViewStyle" format="reference" />
</declare-styleable> 参考技术B 现在的安卓系统都是自带Flash,直接可以打开gif. 参考技术C 换手机就可以了
Android开发之ImageView播放GIF动画实例
参考技术AAndroid开发之ImageView播放GIF动画实例
Android的原生控件并不支持播放GIF格式的图片,如果想在Android中显示一张GIF动态图片,可以利用 ImageView控件来完成,但是放进去之后,你会发现,ImageView它只会显示这张图片的第一帧,不会产生任何的动画效果。我们必须通过自定义控件的方式来实现ImageView播放GIF 图片的功能。
首先我们来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可以播放GIF动态图片。
先新建一个项目PowerImageViewTest,这里使用Android 4.0+Eclipse。
由于是要自定义控件,会需要一些自定义的控件属性,因此我们需要在values目录下新建一个attrs.xml的文件,在这个文件中添加项目需要的自定义属性。
这里我们目前暂时只需要一个自动播放auto_play属性,XML文件代码如下:
<?xml version="1.0" encoding="utf-8"?>
这个文件完成之后,下面我们来开始编写主类PowerImageView类,由于PowerImageView类需要支持ImageView的所有功能,我们必须要让PowerImageView继承自ImageView,代码如下:
public class PowerImageView extends ImageView implements OnClickListener
/**
* 播放GIF动画的关键类
*/
private Movie mMovie;
/**
* 开始播放按钮图片
*/
private Bitmap mStartButton;
/**
* 记录动画开始的时间
*/
private long mMovieStart;
/**
* GIF图片的宽度
*/
private int mImageWidth;
/**
* GIF图片的高度
*/
private int mImageHeight;
/**
* 图片是否正在播放
*/
private boolean isPlaying;
/**
* 是否允许自动播放
*/
private boolean isAutoPlay;
/**
* PowerImageView构造函数。
*
* @param context
*/
public PowerImageView(Context context)
super(context);
/**
* PowerImageView构造函数。
*
* @param context
*/
public PowerImageView(Context context, AttributeSet attrs)
this(context, attrs, 0);
/**
* PowerImageView构造函数,在这里完成所有必要的初始化操作。
*
* @param context
*/
public PowerImageView(Context context, AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PowerImageView);
int resourceId = getResourceId(a, context, attrs);
if (resourceId != 0)
// 当资源id不等于0时,就去获取该资源的流
InputStream is = getResources().openRawResource(resourceId);
// 使用Movie类对流进行解码
mMovie = Movie.decodeStream(is);
if (mMovie != null)
// 如果返回值不等于null,就说明这是一个GIF图片,下面获取是否自动播放的属性
isAutoPlay = a.getBoolean(R.styleable.PowerImageView_auto_play, false);
Bitmap bitmap = BitmapFactory.decodeStream(is);
mImageWidth = bitmap.getWidth();
mImageHeight = bitmap.getHeight();
bitmap.recycle();
if (!isAutoPlay)
// 当不允许自动播放的时候,得到开始播放按钮的图片,并注册点击事件
mStartButton = BitmapFactory.decodeResource(getResources(),R.drawable.start_play);
setOnClickListener(this);
@Override
public void onClick(View v)
if (v.getId() == getId())
// 当用户点击图片时,开始播放GIF动画
isPlaying = true;
invalidate();
@Override
protected void onDraw(Canvas canvas)
if (mMovie == null)
// mMovie等于null,说明是张普通的图片,则直接调用父类的onDraw()方法
super.onDraw(canvas);
else
// mMovie不等于null,说明是张GIF图片
if (isAutoPlay)
// 如果允许自动播放,就调用playMovie()方法播放GIF动画
playMovie(canvas);
invalidate();
else
// 不允许自动播放时,判断当前图片是否正在播放
if (isPlaying)
// 正在播放就继续调用playMovie()方法,一直到动画播放结束为止
if (playMovie(canvas))
isPlaying = false;
invalidate();
else
// 还没开始播放就只绘制GIF图片的第一帧,并绘制一个开始按钮
mMovie.setTime(0);
mMovie.draw(canvas, 0, 0);
int offsetW = (mImageWidth - mStartButton.getWidth()) / 2;
int offsetH = (mImageHeight - mStartButton.getHeight()) / 2;
canvas.drawBitmap(mStartButton, offsetW, offsetH, null);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mMovie != null)
// 如果是GIF图片则重写设定PowerImageView的大小
setMeasuredDimension(mImageWidth, mImageHeight);
/**
* 开始播放GIF动画,播放完成返回true,未完成返回false。
*
* @param canvas
* @return 播放完成返回true,未完成返回false。
*/
private boolean playMovie(Canvas canvas)
long now = SystemClock.uptimeMillis();
if (mMovieStart == 0)
mMovieStart = now;
int duration = mMovie.duration();
if (duration == 0)
duration = 1000;
int relTime = (int) ((now - mMovieStart) % duration);
mMovie.setTime(relTime);
mMovie.draw(canvas, 0, 0);
if ((now - mMovieStart) >= duration)
mMovieStart = 0;
return true;
return false;
/**
* 通过Java反射,获取到src指定图片资源所对应的id。
*
* @param a
* @param context
* @param attrs
* @return 返回布局文件中指定图片资源所对应的id,没有指定任何图片资源就返回0。
*/
private int getResourceId(TypedArray a, Context context, AttributeSet attrs)
try
Field field = TypedArray.class.getDeclaredField("mValue");
field.setAccessible(true);
TypedValue typedValueObject = (TypedValue) field.get(a);
return typedValueObject.resourceId;
catch (Exception e)
e.printStackTrace();
finally
if (a != null)
a.recycle();
return 0;
这个类的代码注释已经非常详细了,我再来简单地解释一下。可以看到,我们重写了ImageView中所有的构建函数,使得 PowerImageView的用法可以和ImageView完全相同。在构造函数中,则是对所有必要的数据进行了初始化操作。首先,我们调用了 getResourceId()方法去获取图片资源对应的id值,在getResourceId()方法内部是通过Java的反射机制来进行获取的。得到了图片资源的id后,我们将它转换成InputStream,然后传入到Movie.decodeStream()方法中以解码出Movie对象。如果得到的Movie对象等于null,说明这是一张普通的图片资源,就不再进行任何特殊处理,因为父类ImageView都帮我们处理好了。如果得到的 Movie对象不等于null,则说明这是一张GIF图片,接着就要去获取是否允许自动播放、图片的宽高等属性的值。如果不允许自动播放,还要给播放按钮 注册点击事件,默认是不允许自动播放的。
接下来会进入到onMeasure()方法中。在这个方法中我们进行判断,如果这是一张GIF图片,则需要将PowerImageView的宽高重定义,使得控件的大小刚好可以放得下这张GIF图片。
再往后就会进入到onDraw()方法中。在这个方法里同样先判断当前是一张普通的图片还是GIF图片,如果是普通的图片就直接调用 super.onDraw()方法交给ImageView去处理就好了。如果是GIF图片,则先判断该图是否允许自动播放,允许的话就调用 playMovie()方法去播放GIF图片就好,不允许的话则会先在PowerImageView中绘制该GIF图片的第一帧,并在图片上绘制一个播放 按钮,当用户点击了播放按钮时,再去调用playMovie()方法去播放GIF图片。
下面我们来看看playMovie()方法中是怎样播放GIF图片的吧。可以看到,首先会对动画开始的时间做下记录,然后对动画持续的时间做下记 录,接着使用当前的时间减去动画开始的时间,得到的时间就是此时PowerImageView应该显示的那一帧,然后借助Movie对象将这一帧绘制到屏 幕上即可。之后每次调用playMovie()方法都会绘制一帧图片,连贯起来也就形成了GIF动画。注意,这个方法是有返回值的,如果当前时间减去动画 开始时间大于了动画持续时间,那就说明动画播放完成了,返回true,否则返回false。
完成了PowerImageView的编写,下面我们就来看一看如何使用它吧,其实非常简单,打开或新建activity_main.xml,代码如下所示:
<relativelayout p=""
android:layout_
android:layout_ >
<com.example.powerimageviewtest.powerimageview p=""
android:id="@+id/image_view"
android:layout_
android:layout_
android:layout_centerInParent="true"
android:src="@drawable/anim"
/>
可以看到,PowerImageView的用法和ImageView几乎完全一样,使用android:src属性来指定一张图片即可,这里指定的anim就是一张GIF图片。然后我们让PowerImageView在布局里居中显示MainActivity中的代码都是自动生成的,这里就不再贴出来了。在AndroidManifest.xml中还有一点需要注意,有些4.0 以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated属性来完成,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest p=""
package="com.example.powerimageviewtest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk p=""
android:minSdkVersion="14"
android:targetSdkVersion="17" />
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:hardwareAccelerated="false"
>
android:name="com.example.powerimageviewtest.MainActivity"
android:label="@string/app_name" >
现在可以来运行一下代码了,一打开程序你就会看到GIF图片的第一帧,点击图片之后就可以播放GIF动画了。
然后我们还可以通过修改activity_main.xml中的代码,给它加上允许自动播放的属性,代码如下所示:
<relativelayout p=""
xmlns:attr="http://schemas.android.com/apk/res/com.example.powerimageviewtest"
android:layout_
android:layout_ >
<com.example.powerimageviewtest.powerimageview p=""
android:id="@+id/image_view"
android:layout_
android:layout_
android:layout_centerInParent="true"
android:src="@drawable/anim"
attr:auto_play="true"
/>
这里使用了刚才我们自定义的属性,通过attr:auto_play来启用和禁用自动播放功能。现在将auto_play属性指定成true后,PowerImageView上就不会再显示一个播放按钮,而是会循环地自动播放动画。不仅如此,PowerImageView还继承了ImageView原生的所有功能,只要指定的不是GIF图 片,PowerImageView表现的结果就和ImageView完全一致,现在我们来放一张普通的PNG图片,修改 activity_main.xml中的代码,如下所示:
<relativelayout p=""
android:layout_
android:layout_ >
<com.example.powerimageviewtest.powerimageview p=""
android:id="@+id/image_view"
android:layout_
android:layout_
android:layout_centerInParent="true"
android:src="@drawable/myphoto"
/>
这里在src属性里面指定了一张名字为myphoto的PNG图片,图片在布局正中央显示出来了,正是普通ImageView所具备的功能。我们还可以在PowerImageView中指定android:scaleType等属性,用法和原生的ImageView完全一样。
以上是关于android 怎样显示gif动画的主要内容,如果未能解决你的问题,请参考以下文章