如果有播放超多帧动画的需求,直接点击 FrameAnimation 在github查看,基本能满足你的所有需求,就不用往下看了,基本能满足 99.99% 人的需求。
创新互联专业为企业提供阿图什网站建设、阿图什做网站、阿图什网站设计、阿图什网站制作等企业网站建设、网页设计与制作、阿图什企业网站模板建站服务,10年阿图什做网站经验,不只是建网站,更提供有价值的思路和整体网络服务。
当在应用中需要使用帧动画的时候,最先想到的就是Android提供的AnimationDrawable了,但是如果帧动画中如果包含上百帧图片,此时再用AnimationDrawable就不是那么理想了。AnimationDrawable使用一个Drawable数组来存储每一帧的图像,会直接把全部图片加载进内存。随着帧数量的增多,就算性能再强劲的机器也会卡顿、OOM。
最近的项目中需要用到大量的帧动画(各种闪瞎24K钛合金狗眼的礼物效果,多的高达200帧),既然AnimationDrawable不行,就想到了两种解决方法。
因为是直播的项目,包含人脸贴图等都是用opengl绘制的,如果用OpenGL绘制一层Texture直接推流还省事。只在主播端处理就行了,但是IOS那边都弄得差不多了,直接原生的不用处理也不会有什么异常什么的。。很尴尬。
好吧,第一个不行那就想到Android自带的surfaceView啦。我首先用不同的手机测试了下应用从本地decode一个bitmap的时间(png格式,414*736,大小在30-100k之间),因为帧动画的每帧不会太大,在性能好点的设备上基本保持在10-30ms之间(不推流基本上推流状态下10ms左右,推流状态下20左右),在性能稍差的设备中基本上也不会超过50ms,所以说是没什么大问题的。
既然不能完全加载到内存,想到的就是类似视频播放或者视频直播类似的思路。首先定义一个Bitmap的缓冲区,边绘制边加载。首先加载一定数量的帧到Bitmap缓冲区,加载完成后通知SurfaceView开始绘制。SurfaceView绘制一帧完成后通知Bitmap缓冲区加载下一帧,同时将绘制过的一帧的从Bitmap缓冲区移除。一帧绘制完成后,绘制线程根据设置的帧间隔休眠一段时间,休眠完成后开始从Bitmap缓冲区获取下一帧,依此类推,一直循环,直到播放完成或者手动停止。按照这种方式实现起来,发现oom卡顿什么的果然不存在了,内存的使用情况如图。
但是看着这个垃圾桶一个挨一个,这个内存回收情况完全不正常!GC太频繁了。想着应该是这里出现了问题。[图片上传失败...(image-96f387-1512626035688)]
频繁的添加移除bitmap,导致了不算太严重的内存抖动。之所以称之为不算太严重,因为大概400ms一次,一次gc花费2ms左右。不看内存,只看运行效果。真的感觉不出来。但是呢,这样显然也是不行滴。
最常见的解决方法就是对象的复用,创建各种pool。Android也提供了Bitmap的复用方式,在加载bitmap的时候传入一个inBitmap,那么加载的bitmap就会复用原bitmap的内存空间,所以理论上将要复用的bitmap和新加载的bitmap在颜色深度一样的情况下,复用的bitmap宽高要大于新加载的bitmap。50L的桶毕竟最多只能装50L的水。关于inBitmap更多资料可以参考 这里 , 还有这里 。(请自备梯子)。 使用起来很简单,大概就是这样
然后实现思路就是在这里修改了,把将要删除的哪一帧留下来作为inBitmap。
可以通过一些其他方式来做到。
方式一,当动画开始start之后,我们可以通过检测是否到达帧动画的最后一帧,来确定动画是否播完。这种方式可以保证动画播完。
方式二,重写AnimationDrawable,获得totalDuration,然后动画start之后的totalDuration,调用结束的接口回调onAnimationFinshed()。为什么必须重写呢?因为,AnimationDrawable仅提供了每一帧的duration,而不能直接获得动画总的duration。
当然还有一些变种的方法,但是其大体思路都应该差不多。至于动画是否流畅播放,这要取决于你播放动画的时候,系统的繁忙程度。
帧动画:第一种方式启动帧动画:(在Activity启动时会自动运行动画)AnimationDrawable ad;ImageView iv = (ImageView) findViewById(R.id.animation_view);iv.setBackgroundResource(R.drawable.animation);ad = (AnimationDrawable) iv.getBackground();iv.getViewTreeObserver().addOnPreDrawListener(opdl);//当一个视图树将要绘制时产生事件,可以添加一个其事件处理函数OnPreDrawListener opdl=new OnPreDrawListener(){@Overridepublicboolean onPreDraw() {ad.start();returntrue; //注意此行返回的值}};第二种方式启动动画:(在Activity启动时会自动运行动画)ImageView image = (ImageView) findViewById(R.id.animation_view);image.setBackgroundResource(R.anim.oldsheep_wait);animationDrawable = (AnimationDrawable) image.getBackground();RunAnim runAnim=new RunAnim();runAnim.execute("");class RunAnim extends AsyncTask{@Overrideprotected String doInBackground(String... params){if (!animationDrawable.isRunning()){animationDrawable.stop();animationDrawable.start();}return"";}}第三种方式启动动画:(在Activity启动时会自动运行动画)ImageView image = (ImageView) findViewById(R.id.animation_view);image.setBackgroundResource(R.anim.oldsheep_wait);animationDrawable = (AnimationDrawable) image.getBackground();image.post(new Runnable(){@Overridepublicvoid run(){animationDrawable.start();}});第四种方式启动动画:(在Activity启动时会自动运行动画)ImageView image = (ImageView) findViewById(R.id.animation_view);image.setBackgroundResource(R.anim.oldsheep_wait);animationDrawable = (AnimationDrawable) image.getBackground();@Overridepublicvoid onWindowFocusChanged(boolean hasFocus){animationDrawable.start();super.onWindowFocusChanged(hasFocus);}这个ad.start不能直接写在onClick,onStart,onResume里面,是无效的,无法启动动画,只能写在比如事件监听当中
动画的使用 是 Android 开发中常用的知识
可是动画的 种类繁多、使用复杂 ,每当需要 采用自定义动画 实现 复杂的动画效果 时,很多开发者就显得束手无策
本文将详细介绍 Android 动画中 逐帧动画 的原理 使用
#1. 作用对象 视图控件( View )
1. 如 Android 的 TextView、Button 等等
2. 不可作用于 View 组件的属性,如:颜色、背景、长度等等
#2. 原理
将动画拆分为 帧 的形式,且定义每一帧 = 每一张图片
逐帧动画的本质:按序播放一组预先定义好的图片
#3. 具体使用 ####步骤1:将动画资源(即每张图片资源)放到 drawable 文件夹里
技巧:
1. 找到自己需要的gif动画
2. 用 gif 分解软件(如 GifSplitter )将 gif 分解成一张张图片即可
Android的SDK提供了三种类型的动画,分别是补间动画、逐帧动画和插值属性动画。下面先介绍第一种动画效果-补间动画。补间动画可以应用于View,让开发者可以定义一些关于大小、位置、旋转和透明度的改变效果,达到让View的内容动起来的效果。补间动画是使用Animation类创建的,它有4个直接子类,分别实现不同的动画效果,分别为:AlphaAnimation 渐变透明度动画效果,即淡入淡出效果ScaleAnimation 渐变尺寸伸缩动画效果,即缩放效果TranslateAnimation 画面转换位置移动动画效果,移动效果RotateAnimation 画面转移旋转动画效果,即旋转效果要使用补间动画的效果,有两种方法,第一种是在XML文件中设置动画效果;第二种是在Java代码中设置。下面分别介绍这两种方法:1.在XML文件中设置方式:在Android项目的res目录下新建anim文件夹,然后在anim文件夹下新建firstanim.xml,添加动画效果的配置代码,示例代码如下:[html]android:fromAlpha="0.1"android:toAlpha="1.0"android:duration="3000"/android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromXScale="0.0"android:toXScale="1.4"android:fromYScale="0.0"android:toYScale="1.4"android:pivotX="50%"android:pivotY="50%"android:fillAfter="false"android:duration="700" /android:fromXDelta="30"android:toXDelta="-80"android:fromYDelta="30"android:toYDelta="300"android:duration="2000"/android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromDegrees="0"android:toDegrees="+350"android:pivotX="50%"android:pivotY="50%"android:duration="3000" /android:fromAlpha="0.1"android:toAlpha="1.0"android:duration="3000"/android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromXScale="0.0"android:toXScale="1.4"android:fromYScale="0.0"android:toYScale="1.4"android:pivotX="50%"android:pivotY="50%"android:fillAfter="false"android:duration="700" /android:fromXDelta="30"android:toXDelta="-80"android:fromYDelta="30"android:toYDelta="300"android:duration="2000"/android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromDegrees="0"android:toDegrees="+350"android:pivotX="50%"android:pivotY="50%"android:duration="3000" /在Activity中的onCreate()方法中,获取在XML中配置的动画效果,代码如下:[java]Animation animation= AnimationUtils.loadAnimation(this,R.anim.firstanim);Animation animation= AnimationUtils.loadAnimation(this,R.anim.firstanim);如果这个动画效果使用在一个ImageView上,可以参考如下代码:[java]imageView.startAnimation(animation);imageView.startAnimation(animation);2.在Java代码中设置方式:以AlphaAnimation为例,[java]//首先声明Animation的一个对象private Animation alpha;//在Activity的onCreate()方法中实例化这个对象alpha=new AlphaAnimation(0.1f, 1.0f);//设置动画持续时间为3秒alpha.setDuration(3000);//首先声明Animation的一个对象private Animation alpha;//在Activity的onCreate()方法中实例化这个对象alpha=new AlphaAnimation(0.1f, 1.0f);//设置动画持续时间为3秒alpha.setDuration(3000);如果这个动画效果使用在一个ImageView上,可以参考如下代码:[java]imageView.startAnimation(alpha);imageView.startAnimation(alpha);