我个人是比较喜欢逛贴吧的,贴吧里总是会有很多搞笑的动态图片,经常看一看就会感觉欢乐很多,可以释放掉不少平时的压力。确实,比起一张单调的图片,动态图片明显更加的有意思。一般动态图片都是GIF格式的,浏览器中可以直接将这种格式的图片播放成动画。
不过很可惜的是,Android的原生控件并不支持播放GIF格式的图片。我们都知道,在Android中如果想要显示一张图片,可以借助ImageView控件来完成,但是如果将一张GIF图片设置到ImageView里,它只会显示这张图片的第一帧,不会产生任何的动画效果。
那么就没有办法在Android里播放GIF图片了吗?当然不是,我们可以通过自定义控件的方式来实现这个功能。ImageView无法播放GIF图片说明它的功能还不够强大,那么今天我们就来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可以播放GIF图片。
下面我们就开始吧,首先新建一个项目,起名就叫PowerImageViewTest,这里使用Android 4.0的API。
由于是要自定义控件,我们还可能会用到一些自定义的属性,因此在values目录下新建一个attrs.xml的文件,可以在这个文件中添加任何需要自定义的属性。这里我们目前只需要一个auto_play属性,代码如下所示:
-
<?xmlversion="1.0"encoding="utf-8"?>
-
<resources>
-
-
<declare-styleablename="PowerImageView">
-
<attrname="auto_play"format="boolean"></attr>
-
</declare-styleable>
-
-
</resources>
完成了这个文件之后,下面我们来开始编写最最重要的PowerImageView类,由于这个类要支持ImageView的所有功能,因此需要让PowerImageView继承自ImageView,代码如下所示:
-
publicclassPowerImageViewextendsImageViewimplementsOnClickListener{
-
-
-
-
-
privateMoviemMovie;
-
-
-
-
-
privateBitmapmStartButton;
-
-
-
-
-
privatelongmMovieStart;
-
-
-
-
-
privateintmImageWidth;
-
-
-
-
-
privateintmImageHeight;
-
-
-
-
-
privatebooleanisPlaying;
-
-
-
-
-
privatebooleanisAutoPlay;
-
-
-
-
-
-
-
publicPowerImageView(Contextcontext){
-
super(context);
-
}
-
-
-
-
-
-
-
publicPowerImageView(Contextcontext,AttributeSetattrs){
-
this(context,attrs,0);
-
}
-
-
-
-
-
-
-
publicPowerImageView(Contextcontext,AttributeSetattrs,intdefStyle){
-
super(context,attrs,defStyle);
-
TypedArraya=context.obtainStyledAttributes(attrs,R.styleable.PowerImageView);
-
intresourceId=getResourceId(a,context,attrs);
-
if(resourceId!=0){
-
-
InputStreamis=getResources().openRawResource(resourceId);
-
-
mMovie=Movie.decodeStream(is);
-
if(mMovie!=null){
-
-
isAutoPlay=a.getBoolean(R.styleable.PowerImageView_auto_play,false);
-
Bitmapbitmap=BitmapFactory.decodeStream(is);
-
mImageWidth=bitmap.getWidth();
-
mImageHeight=bitmap.getHeight();
-
bitmap.recycle();
-
if(!isAutoPlay){
-
-
mStartButton=BitmapFactory.decodeResource(getResources(),
-
R.drawable.start_play);
-
setOnClickListener(this);
-
}
-
}
-
}
-
}
-
-
@Override
-
publicvoidonClick(Viewv){
-
if(v.getId()==getId()){
-
-
isPlaying=true;
-
invalidate();
-
}
-
}
-
-
@Override
-
protectedvoidonDraw(Canvascanvas){
-
if(mMovie==null){
-
-
super.onDraw(canvas);
-
}else{
-
-
if(isAutoPlay){
-
-
playMovie(canvas);
-
invalidate();
-
}else{
-
-
if(isPlaying){
-
-
if(playMovie(canvas)){
-
isPlaying=false;
-
}
-
invalidate();
-
}else{
-
-
mMovie.setTime(0);
-
mMovie.draw(canvas,0,0);
-
intoffsetW=(mImageWidth-mStartButton.getWidth())/2;
-
intoffsetH=(mImageHeight-mStartButton.getHeight())/2;
-
canvas.drawBitmap(mStartButton,offsetW,offsetH,null);
-
}
-
}
-
}
-
}
-
-
@Override
-
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
-
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
-
if(mMovie!=null){
-
-
setMeasuredDimension(mImageWidth,mImageHeight);
-
}
-
}
-
-
-
-
-
-
-
-
privatebooleanplayMovie(Canvascanvas){
-
longnow=SystemClock.uptimeMillis();
-
if(mMovieStart==0){
-
mMovieStart=now;
-
}
-
intduration=mMovie.duration();
-
if(duration==0){
-
duration=1000;
-
}
-
intrelTime=(int)((now-mMovieStart)%duration);
-
mMovie.setTime(relTime);
-
mMovie.draw(canvas,0,0);
-
if((now-mMovieStart)>=duration){
-
mMovieStart=0;
-
returntrue;
-
}
-
returnfalse;
-
}
-
-
-
-
-
-
-
-
-
-
privateintgetResourceId(TypedArraya,Contextcontext,AttributeSetattrs){
-
try{
-
Fieldfield=TypedArray.class.getDeclaredField("mValue");
-
field.setAccessible(true);
-
TypedValuetypedValueObject=(TypedValue)field.get(a);
-
returntypedValueObject.resourceId;
-
}catch(Exceptione){
-
e.printStackTrace();
-
}finally{
-
if(a!=null){
-
a.recycle();
-
}
-
}
-
return0;
-
}
-
-
}
这个类的代码注释已经非常详细了,我再来简单地解释一下。可以看到,我们重写了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,代码如下所示:
-
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent">
-
-
<com.example.powerimageviewtest.PowerImageView
-
android:id="@+id/image_view"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_centerInParent="true"
-
android:src="@drawable/anim"
-
/>
-
-
</RelativeLayout>
可以看到,PowerImageView的用法和ImageView几乎完全一样,使用android:src属性来指定一张图片即可,这里指定的anim就是一张GIF图片。然后我们让PowerImageView在布局里居中显示。
MainActivity中的代码都是自动生成的,这里就不再贴出来了。在AndroidManifest.xml中还有一点需要注意,有些4.0以上系统的手机启动了硬件加速功能之后会导致GIF动画播放不出来,因此我们需要在AndroidManifest.xml中去禁用硬件加速功能,可以通过指定android:hardwareAccelerated属性来完成,代码如下所示:
-
<?xmlversion="1.0"encoding="utf-8"?>
-
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
-
package="com.example.powerimageviewtest"
-
android:versionCode="1"
-
android:versionName="1.0">
-
-
<uses-sdk
-
android:minSdkVersion="14"
-
android:targetSdkVersion="17"/>
-
-
<application
-
android:allowBackup="true"
-
android:icon="@drawable/ic_launcher"
-
android:label="@string/app_name"
-
android:theme="@style/AppTheme"
-
android:hardwareAccelerated="false"
-
>
-
<activity
-
android:name="com.example.powerimageviewtest.MainActivity"
-
android:label="@string/app_name">
-
<intent-filter>
-
<actionandroid:name="android.intent.action.MAIN"/>
-
<categoryandroid:name="android.intent.category.LAUNCHER"/>
-
</intent-filter>
-
</activity>
-
</application>
-
-
</manifest>
现在可以来运行一下代码了,一打开程序你就会看到GIF图片的第一帧,点击图片之后就可以播放GIF动画了,如下图所示:
然后我们还可以通过修改activity_main.xml中的代码,给它加上允许自动播放的属性,代码如下所示:
-
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
-
xmlns:attr="http://schemas.android.com/apk/res/com.example.powerimageviewtest"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent">
-
-
<com.example.powerimageviewtest.PowerImageView
-
android:id="@+id/image_view"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_centerInParent="true"
-
android:src="@drawable/anim"
-
attr:auto_play="true"
-
/>
-
-
</RelativeLayout>
这里使用了刚才我们自定义的属性,通过attr:auto_play来启用和禁用自动播放功能。现在将auto_play属性指定成true后,PowerImageView上就不会再显示一个播放按钮,而是会循环地自动播放动画。现在重新运行一下程序,效果如下图所示:
怎么样?效果还不错吧。不仅如此,PowerImageView还继承了ImageView原生的所有功能,只要指定的不是GIF图片,PowerImageView表现的结果就和ImageView完全一致,让我们来放一张普通的PNG图片看看吧,修改activity_main.xml中的代码,如下所示:
-
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent">
-
-
<com.example.powerimageviewtest.PowerImageView
-
android:id="@+id/image_view"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:layout_centerInParent="true"
-
android:src="@drawable/conan"
-
/>
-
-
</RelativeLayout>
这里在src属性里面指定了一张名字为conan的图片,这是一张PNG图片,效果如下图所示:
一张图片在布局正中央显示出来了,正是普通ImageView所具备的功能。你还可以在PowerImageView中指定android:scaleType等属性,用法和原生的ImageView完全一样。怎么样,是不是确实算得上是Power版的ImageView了?
源代码下载:http://download.csdn.net/detail/lxq_xsyu/6452425
分享到:
相关推荐
android自定义ImageView实现旋转动画
今天我们就来编写一个PowerImageView控件,让它既能支持ImageView控件原生的所有功能,同时还可以播放GIF图片
主要为大家详细介绍了Android实现可播放GIF动画的ImageView,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
此为示例代码,详解讲解请参考: http://blog.csdn.net/guolin_blog/article/details/11100315 如对资源内容有疑问,可以到博客中留言。
FAImageView is a Frame Animation ImageView for Android. You can set multiple frame images and start frame animation like UIImageView in iOS. You can animate multiple image like below! Setup Gradle ...
android 自定义属性实现 ImageView 透明度渐变效果
可以播放gif动画的ImageView 博客地址:http://blog.csdn.net/pangzaifei/article/details/40077021
大家在使用APP的时候,有的APP在点击语音搜索界面后,会出现一个小话筒,小话筒会类似雷达似得在闪烁,表示正在倾听你说话的内容(这个大家可以参照微软的必应APP),那么问题来了,这种动画效果是如何实现的呢?...
Android 点击有缩放动画的imageview
Android 扩展ImageView来播放gif动画
android 将Imageview 将view生成图片后保存到本地相册的实现生成图片后保存到本地相册的实现,android 将Imageview生成图片后保存到本地相册的实现 android 将Imageview生成图片后保存到本地相册的实现, 有需要的朋友...
Android中ImageView实现平铺多张图片Android中实现平铺图片有两种方式:在drawable中定义平铺的Bitmap然后在ImageView中引
在Android系统中实现ImageView控件的动画效果
Android用线程实现ImageView图片变换+可以停止和继续
Android应用源码之imageView1_imageView
在Android Studio 中使用Imageview播放帧动画是不能代码混淆,可以使用FAImageView实现
Android 使用animation_list 实现imageview 动画效果
详见http://blog.csdn.net/huaxun66/article/details/52269260