Android实现可播放GIF动画的ImageView
程序员文章站
2024-03-06 11:18:31
android的原生控件并不支持播放gif格式的图片。我们都知道,在android中如果想要显示一张图片,可以借助imageview来完成,但是如果将一张gif图片设置到i...
android的原生控件并不支持播放gif格式的图片。我们都知道,在android中如果想要显示一张图片,可以借助imageview来完成,但是如果将一张gif图片设置到imageview里,它只会显示这张图片的第一帧,不会产生任何的动画效果。今天我们来编写一个自定义的增强型imageview(继承imageview),可以播放gif格式的图片,暂且叫做gifimageview吧。
1.自定义属性
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="gifimageview"> <attr name="auto_play" format="boolean"></attr> </declare-styleable> </resources>
2.自定义view中获取属性值
private movie mmovie;//播放动画需要用到的,系统类 private int mimagewidth;//动画的imageview的宽度 private int mimageheight;//动画imageview的高度 private long mmoviestart = 0;// 播放开始 private boolean isautoplay;//是否自动播放 private bitmap mstartplay;//开始按钮 private boolean isplaying=false;//记录是否正在播放 private float mscale;//图片的缩放比 private int mmeasuredgifwidth;//缩放后图片宽 private int mmeasuredgifheight;//缩放后图片高 ... private void init(context context, attributeset attrs) { typedarray attributes = context.obtainstyledattributes(attrs,r.styleable.gifimageview); // 通过反射拿布局中src的资源id,所以gif文件需要放在布局的src中 int resourceid = getresourceid(attributes, context, attrs); if (resourceid != 0) { // 说明是gif动画 // 1.将resourcesid变成流 // 2.用move来decode解析流 // 3.获得bitmap的长宽 inputstream is = getresources().openrawresource(resourceid); mmovie = movie.decodestream(is); if (mmovie != null) { bitmap bitmap = bitmapfactory.decodestream(is); mimagewidth = bitmap.getwidth(); mimageheight = bitmap.getheight(); // 用完释放 bitmap.recycle(); // 获得是否允许自动播放,如果不允许自动播放,则初始化播放按钮 isautoplay = attributes.getboolean(r.styleable.gifimageview_auto_play, false); if (!isautoplay) { mstartplay = bitmapfactory.decoderesource(getresources(),r.drawable.start_play); setonclicklistener(this); } } } //回收资源 attributes.recycle(); } /** * 通过反射拿布局中src的资源id * * @param attrs * @param context * @param attributes */ private int getresourceid(typedarray attributes, context context, attributeset attrs) { try { field filed = typedarray.class.getdeclaredfield("mvalue"); filed.setaccessible(true); typedvalue typevalue = (typedvalue) filed.get(attributes); return typevalue.resourceid; } catch (exception e) { e.printstacktrace(); } } return 0; }
3.重写onmesure()
@override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); if (mmovie != null) { /* * calculate horizontal scaling */ float scalew = 1f; int measuremodewidth = measurespec.getmode(widthmeasurespec); if (measuremodewidth != measurespec.unspecified) { int maximumwidth = measurespec.getsize(widthmeasurespec); scalew = (float) mimagewidth / (float) maximumwidth; } /* * calculate vertical scaling */ float scaleh = 1f; int measuremodeheight = measurespec.getmode(heightmeasurespec); if (measuremodeheight != measurespec.unspecified) { int maximumheight = measurespec.getsize(heightmeasurespec); scaleh = (float) mimageheight / (float) maximumheight; } /* * calculate overall scale */ mscale = 1f / math.max(scaleh, scalew); mmeasuredgifwidth = (int) (mimagewidth * mscale); mmeasuredgifheight = (int) (mimageheight * mscale); setmeasureddimension(mmeasuredgifwidth, mmeasuredgifheight); } }
4.重写ondraw()
@override protected void ondraw(canvas canvas) { if (mmovie == null) { // mmovie等于null,说明是张普通的图片,则直接调用父类的ondraw()方法 super.ondraw(canvas); } else { // mmovie不等于null,说明是张gif图片 if (isautoplay) { // 如果允许自动播放,就播放 playmovie(canvas); invalidate(); } else { // 不允许自动播放的话 // 1.判断是否正在播放 // 2.获得第一帧的图像 // 3.然后添加播放按钮 if (isplaying) { // 如果正在播放就playmoive继续播放 if (playmovie(canvas)) { isplaying = false; } invalidate(); } else { // 第一帧 mmovie.settime(0); canvas.save(canvas.matrix_save_flag); canvas.scale(mscale, mscale); mmovie.draw(canvas, 0, 0);// 画 canvas.restore(); // 绘制开始按钮 int offsetw = (mmeasuredgifwidth - mstartplay.getwidth()) / 2; int offseth = (mmeasuredgifheight - mstartplay.getheight()) / 2; canvas.drawbitmap(mstartplay, offsetw, offseth, null); } } } } /** * 播放gif动画 * * @param canvas */ private boolean playmovie(canvas canvas) { // 1.获取播放的时间 // 2.如果开始start=0,则认为是开始 // 3.记录播放的时间 // 4.设置进度 // 5.画动画 // 6.如果时间大于了播放的时间,则证明结束 long now = systemclock.uptimemillis(); if (mmoviestart == 0) { mmoviestart = now; } int duration = mmovie.duration(); if (duration == 0) { duration = 1000; } //记录gif播放了多少时间 int reltime = (int) ((now - mmoviestart) % duration); mmovie.settime(reltime);// 设置时间 canvas.save(canvas.matrix_save_flag); canvas.scale(mscale, mscale); mmovie.draw(canvas, 0, 0);// 画 canvas.restore(); if ((now - mmoviestart) >= duration) { // 结束 mmoviestart = 0; return true; } return false; }
5.添加点击事件
@override public void onclick(view v) { if(v.getid()==getid()){ isplaying=true; invalidate(); } }
还有一点需要注意,有些4.0以上系统的手机启动了硬件加速功能之后会导致gif动画播放不出来,因此我们需要在androidmanifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareaccelerated=false来完成。
--------------------------------------------------------------------------------
现在我们来看看运行后效果如何吧,
布局文件:
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:attr="http://schemas.android.com/apk/res/com.hx.gifimageview" android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.hx.gifimageview.gifimageview android:layout_width="150dip" android:layout_height="150dip" android:layout_margin="10dp" android:src="@drawable/shulan" attr:auto_play="false" /> <com.hx.gifimageview.gifimageview android:layout_width="150dip" android:layout_height="150dip" android:layout_margin="10dp" android:src="@drawable/shulan" attr:auto_play="true" /> <com.hx.gifimageview.gifimageview android:layout_width="150dip" android:layout_height="150dip" android:layout_margin="10dp" android:src="@drawable/jingtai" attr:auto_play="true" /> </linearlayout>
图一的auto_play属性为false,会显示第一帧和一个播放按钮,点击后gif图片会自动运行。
图二的auto_play属性为true,会自动循环播放gif。
图三我们给的资源是静态图片,因为自定义view继承imageview,所以具备imageview所有特性,因此也能显示静态图片。
源码下载:http://xiazai.jb51.net/201609/yuanma/gifimageview(jb51.net).rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。