好看的界面离不开好看、流畅的动画,Android系统提供2大动画系统:传动动画和属性动画。
启动传动动画又分为帧动画和补间动画;
帧动画:即图形以一帧一帧的形式播放的动画,像gif一样的效果;
补间动画:即我们常见的alpha(透明度),translate(位移),scale(缩放)、rotate(旋转)动画。
下面我们开始分别来介绍:
帧动画:
效果如上所示,下面来看看怎么实现的。
首先我们需要新建一个drawable文件 animation :
[code]<?xml version=\"1.0\" encoding=\"utf-8\"?><animation-list xmlns:android=\"http://schemas.android.com/apk/res/android\"><itemandroid:drawable=\"@drawable/an_1\"android:duration=\"100\" /><itemandroid:drawable=\"@drawable/an_2\"android:duration=\"100\" />……总共31张图片 an_1到an_31</animation-list>
然后我们在xml中定义个ImageView并且设置src为刚刚定义的drawable:
[code]<?xml version=\"1.0\" encoding=\"utf-8\"?><LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"xmlns:tools=\"http://schemas.android.com/tools\"android:layout_width=\"match_parent\"android:layout_height=\"match_parent\"android:orientation=\"vertical\"tools:context=\".AActivity\"><ImageViewandroid:id=\"@+id/acMainImgAnimation\"android:layout_width=\"wrap_content\"android:layout_height=\"wrap_content\"android:src=\"@drawable/animation\" /></LinearLayout>
最后只需要在代码中启动动画即可:
[code]val an = acMainImgAnimation.drawable as AnimationDrawablean.start()
先获取图片的drawable,然后转为AnimationDrawable类,看名字就知道它是一个drawable的动画类,然后调用start启动即可。
补间动画:
补间动画的创建分xml和代码创建,我们先来看看xml怎么创建:
现在res目录创建anim目录,然后在此目录下创建动画文件
我们直接来看set.xml,即所有动画的集合,对,没错,上面提到的4种动画是可以同时设置的,任意组合。
[code]<?xml version=\"1.0\" encoding=\"utf-8\"?><set xmlns:android=\"http://schemas.android.com/apk/res/android\"android:duration=\"1000\"android:interpolator=\"@android:anim/accelerate_decelerate_interpolator\"android:repeatCount=\"infinite\"android:repeatMode=\"reverse\"android:shareInterpolator=\"true\"><alphaandroid:duration=\"1000\"android:fromAlpha=\"0\"android:toAlpha=\"1\" /><rotateandroid:duration=\"1000\"android:fromDegrees=\"0\"android:pivotX=\"50%\"android:pivotY=\"50%\"android:repeatCount=\"infinite\"android:repeatMode=\"reverse\"android:startOffset=\"500\"android:toDegrees=\"360\" /><scaleandroid:duration=\"1000\"android:fromXScale=\"0\"android:fromYScale=\"0\"android:pivotX=\"50%\"android:pivotY=\"50%\"android:repeatCount=\"infinite\"android:repeatMode=\"reverse\"android:toXScale=\"1\"android:toYScale=\"1\" /><translateandroid:duration=\"1000\"android:fromXDelta=\"0%\"android:fromYDelta=\"0%\"android:repeatCount=\"infinite\"android:repeatMode=\"reverse\"android:toXDelta=\"200%\"android:toYDelta=\"0%\" /></set>
下面来分别介绍各个标签的意思:
duration:动画持续时间
interpolator:差值器,即动画在执行的时间里按照一定的速率变化。如匀速、加速、减速等等。
repeatCount:正数就是动画执行多少次,infinite无限循环。
repeatMode:重复执行的时候以什么样式reverse:倒叙,如一个从左往右的动画,第二次的时候就是从右往左。restart:重新开始,如从左往右,下一次控件会回到原始位置再从左往右。
shareInterpolator:包含的所有动画是否统一使用一个差值器。
startOffset:开始之前延时多少毫秒
关于坐标:旋转、缩放、位移动画中都会有关于x、y轴位置的设置,如果直接写数字那么就是指定的屏幕坐标,数字后面加%则是相对于自身的坐标,最后面加p则是相对于父控件。如上面最后的translate fromXDelta=“0%” 是指的X轴开始位置是自身的0坐标。
alpha 透明度:
fromAlpha:开始的透明度(0——1)
toAlpha:介绍时的透明度(0——1)
rotate 旋转:
pivotX:X轴的中心点
pivotY:Y轴中心点
fromDegrees:开始的角度
toDegrees:结束角度
scale 缩放:
fromXScale:X轴开始的比例(0——1)
fromYScale:Y轴开始的比例(0——1)
toXScale:X轴结束的比例(0——1)
toYScale:Y轴结束的比例(0——1)
translate 位移:
fromXDelta:X轴开始的位置
fromYDelta:Y轴开始的位置
toXDelta:X轴结束的位置
toYDelta:Y 轴结束的位置
[code]//从xml加载动画val alphaAnimation = AnimationUtils.loadAnimation(this, R.anim.alpha)acMainViewAnAlpha.startAnimation(alphaAnimation)val translateAnimation = AnimationUtils.loadAnimation(this, R.anim.translate)acMainViewAnTranslate.startAnimation(translateAnimation)val scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.scale)acMainViewAnScale.startAnimation(scaleAnimation)val rotateAnimation = AnimationUtils.loadAnimation(this, R.anim.rotate)acMainViewAnRotate.startAnimation(rotateAnimation)val animation = AnimationUtils.loadAnimation(this, R.anim.set)acMainViewAnSet.startAnimation(animation)
下面来看看代码怎么创建:
先新建一个AnimationTools
[code]object AnimationTools {/*** 获取透明度动画*/fun getAlpha(): Animation {//第一个参数为开始透明度,第二个为结束透明度val animation = AlphaAnimation(0f, 1f)animation.duration = 1000animation.repeatMode = Animation.REVERSEanimation.repeatCount = Animation.INFINITEreturn animation}fun getScale(): Animation {/*第一个X开始位置第二个X结束位置第三个Y开始位置第四个Y结束位置第五个缩放的相对位置类型,这里设置的相对空间自身,跟xml里面写%一样第六个为X轴缩放中心点第七第八同理为Y轴*/val animation = ScaleAnimation(0f, 1f, 0f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)animation.duration = 1000animation.repeatMode = Animation.REVERSEanimation.repeatCount = Animation.INFINITEanimation.interpolator = AccelerateDecelerateInterpolator()return animation}fun getRotate(): Animation {/**第一和第二位开始角度和结束角度* 后面几个参数同上同理*/val animation = RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)animation.duration = 1000animation.repeatMode = Animation.REVERSEanimation.repeatCount = Animation.INFINITEanimation.interpolator = AccelerateInterpolator(0.9f)return animation}fun getTranslate(): Animation {/*第一个为X轴开始移动相对坐标类型,同上同理为自身或者父控件第二个为X轴开始位置第三第四位X轴结束位置类型和坐标后四个同理为Y轴*/val animation = TranslateAnimation(Animation.RELATIVE_TO_PARENT,0f,Animation.RELATIVE_TO_SELF,2f,Animation.RELATIVE_TO_SELF,0f,Animation.RELATIVE_TO_SELF,0f)animation.duration = 1000animation.repeatMode = Animation.REVERSEanimation.repeatCount = Animation.INFINITEanimation.interpolator = DecelerateInterpolator(0.5f)return animation}fun getSet(): Animation {/*AnimationSet通过add方法添加各种其他动画,动画之间是可以通过startOffset来添加延时错开的*/val animation = AnimationSet(true)animation.addAnimation(getAlpha())animation.addAnimation(getScale())animation.addAnimation(getRotate())animation.addAnimation(getTranslate())return animation}}
效果跟之前的是一样的。
下面我们来看看属性动画:
我们先来看一下用法:
[code]//这里定义一个objectAnimator// 第一个参数是我们需要显示动画的控件//第二个参数是要修改的属性,需要注意的是这个属性必须是控件里面有的属性,并且可以修改的,即View是有setXXX方法的。比如我们介绍的这4种动画,这里修改的是透明度//后面的参数跟我们用xml或者代码创建的就是一样的了val alphaAnimation = ObjectAnimator.ofFloat(acMainViewAnAlpha, \"alpha\", 0f, 1f)//同样设置时间和差值器alphaAnimation.duration = 1000alphaAnimation.interpolator = LinearInterpolator()//这里添加了一个动画更新的监听器alphaAnimation.addListener(object : Animator.AnimatorListener {//动画从新开始override fun onAnimationRepeat(animation: Animator?) {}//动画结束override fun onAnimationEnd(animation: Animator?) {}//动画取消override fun onAnimationCancel(animation: Animator?) {}//动画开始override fun onAnimationStart(animation: Animator?) {}})//这里添加一个动画更新的监听器,通过参数可以获取到当前的值是多少,// 比如刚刚设置透明度是0到1,这里就会实时的更新获取到0到1的小数alphaAnimation.addUpdateListener {Log.d(\"ZLog AActivity\", \"onCreate: ${it.animatedValue}\")}//开始运行alphaAnimation.start()
属性动画的运行模式就是通过不断的设置我们需要修改的属性值,然后重绘View来达到动画的效果。
那么我们就可以非常方便的来设置想要的动画了,很多自定义的控件我们就可以通过它来控制动画。
ObjectAnimator类有很多方法可以使用,它的核心是通过设置的持续时间、差值器、开始、结束的值来持续的产生数字,拿到这些数字之后再来更新View。
来看一下ObjectAnimator的父类:
ValueAnimator这个类就是专门用来负责产生持续的数字的。
[code]val animation = ValueAnimator.ofInt(0, 255)animation.duration = 1000animation.addUpdateListener {Log.d(\"ZLog AActivity\", \"updateListener: ${it.animatedValue}\")}animation.start()
上面这段代码就会在1秒之内持续的参数0到255的整数,通过这种动画的形式获取到数字后我们就可以方便的重写绘制View来达到动画效果了。
[code]class ProgressTestView : View {private val paint = Paint()var progressValue = 0fconstructor(context: Context) : this(context, null)constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)constructor(context: Context, attributeSet: AttributeSet?, style: Int) : super(context, attributeSet, style) {paint.strokeWidth = 30fpaint.color = Color.REDstartAnimation()}/*** 核心代码,开始动画*/private fun startAnimation() {val animation = ValueAnimator.ofFloat(0f, 1f)animation.duration = 5000animation.interpolator = DecelerateInterpolator(0.8f)animation.repeatCount = ValueAnimator.INFINITEanimation.repeatMode = ValueAnimator.REVERSE//上面都是基本的一些动画设置,在这里添加更新的回调animation.addUpdateListener {//通过回调参数拿到当前值progressValue = it.animatedValue as Float//触发重绘invalidate()}animation.start()}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)canvas?.let {//这里更近当前进度值绘制一条横线val w = width * progressValueit.drawLine(0f, 20f, w, 20f, paint)}}}
通过上面的代码我们能够进一步的理解ValueAnimator。刚刚的View里面控制进度或者说能进行动画效果的属性是progressValue,那么我们能不能使用之前的ObjectAnimator来处理呢?
首先我们在ProgresTextView里面取消动画的执行
[code]constructor(context: Context, attributeSet: AttributeSet?, style: Int) : super(context, attributeSet, style) {paint.strokeWidth = 30fpaint.color = Color.RED// startAnimation()}
然后对progressValue属性的设置方法做一下修改:
[code] var progressValue = 0fset(value) {field = valueinvalidate()}
就是在设置的时候进行重绘。
最后在Activity的代码中加入下面的代码:
[code] override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val animation = ObjectAnimator.ofFloat(acMainView, \"progressValue\", 0f, 1f)animation.duration = 5000animation.interpolator = DecelerateInterpolator(0.8f)animation.repeatCount = ValueAnimator.INFINITEanimation.repeatMode = ValueAnimator.REVERSEanimation.start()}
这样动画就能跟刚才一样执行了
好了,动画相关的内容就先讲到这里,上面关于自定义View的内容将会在后面分几个部分来介绍。因为它涉及到测量、布局、重绘和点击事件(Android系统点击事件分发机制)这些内容,相对比较复杂,所以会分几个模块来介绍。