如上图所示:android动画分类大体有两种一种是
View动画
一种是转场动画
。android帧动画:将图片一张一张按顺序播放,展示出动画效果。canvas
补间动画:实现动画alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)等效果,通常采用xml文件形式。api
属性动画:(重点)它是对于对象属性的动画。补间动画的内容,均可以经过属性动画实现。bash
这里咱们就不讲帧动画
跟补间动画
,这两个你们能够本身百度一下用法。(另外这篇文章中的动画都是在代码中实现的,若是要看xml的使用方法,能够看看Android 动画使用 scale、alpha、translate、rotate、set 这篇其余人写的这篇文章。app
imag_view.animate().run {
translationX(400f) //设置左移
duration = 1000//设置动画运行的时间
setInterpolator(LinearInterpolator()) //设置线性插值器
}
复制代码
移动
、
旋转
、
缩放
、
透明
,看下面的api:
上面的api中能够看到 都存在
绝对
跟相对
(方法后面-by
)的方法,其中绝对的方法以上面的代码为例子,区别是:translationX(400f)
表明将translationX
变成400translationXBy(400f)
表明将translationX
增长400ide
这么多api就不作逐一展现了。oop
- 用 ObjectAnimator.ofXXX() 建立 ObjectAnimator 对象;
- 添加时长、差值器等各类参数
- 用 start() 方法执行动画。
ObjectAnimator.ofFloat(imag_view,View.ROTATION,0f,180f).run {
duration = 1000
interpolator = LinearInterpolator()
start()
}
复制代码
View
进行动画展现,主要方法是
ObjectAnimator.ofFloat(imag_view,View.ROTATION,0f,180f)
第一个参数:传入要进行属性动画的
view
第二个参数:要变化的属性值,这里是传入
View.ROTATION
,也就是
"rotation"
第三个参数:属性起始值 第四个参数:属性结束值
后面还能够加入多个参数值,从第三个参数开始到最后第n个参数,表示属性开始 ->中间值->中间值.... ->结束值
注意一点:并不是全部的属性都是能够有
set
get
方法,能够进行属性动画。
自定义View属性动画以及使用的步骤:动画
- 为要改变的属性添加setter/getter方法
- setter方法中调用
invalidate()
使其从新绘画- 在onDraw()中根据改变的属性绘画出你要的效果
- 使用的时候跟ObjectAnimator基本使用方式一致
大体的模板代码ui
class CircleView : View {
//提供给外面改变的属性值 这里使用kotlin语法 实际上它默认已经实现了setter/getter方法
// 可是这里个set方法咱们要改写一下 记得调用 invalidate()
var progress: Float = 0f
set(value) {
field = value
invalidate()
}
@RequiresApi(Build.VERSION_CODES.M)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
//省略......
canvas!!.drawArc(rectF, 135f, progress * 2.7f, false, paint)
//省略......
canvas.drawText("${progress.toInt()}%", centerX, centerY+40, paint)
}
}
复制代码
真正的实现与调用this
class CircleView : View {
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
val rectF = RectF()
init {
paint.run {
textSize = dpToPixel(50f)
textAlign = Paint.Align.CENTER
}
}
var radius = dpToPixel(120f)
var progress: Float = 0f
set(value) {
field = value
invalidate()
}
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet)
@RequiresApi(Build.VERSION_CODES.M)
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var centerX = width / 2f
var centerY = height / 2f
//画弧形进度条
paint.run {
color = context.getColor(R.color.colorAccent)
style = Paint.Style.STROKE
strokeCap = Paint.Cap.ROUND
strokeWidth = dpToPixel(20f)
}
rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius)
canvas!!.drawArc(rectF, 135f, progress * 2.7f, false, paint)
//画百分比的数值
paint.run {
color = Color.BLACK
style = Paint.Style.FILL
}
canvas.drawText("${progress.toInt()}%", centerX, centerY+40, paint)
}
}
复制代码
//调用代码
ObjectAnimator.ofFloat(circle_view,"progress",0f,80f).run {
duration = 2000
interpolator = OvershootInterpolator() //插值器 超过结束值后再回弹
repeatCount = INFINITE // 重复次数-无限循环
repeatMode = RESTART //重复模式-从新开始
start()
}
复制代码
这里设置了
OvershootInterpolator
插值器,使得它能够超事后回弹回来。这里可使用PropertyValuesHolders.ofKeyframe()
作到相似效果,能够翻到下面对应内容看一下 #####ValueAnimator 这个是ObjectAnimator
的父类,这个我并没怎么接触,后期再补充吧
ObjectAnimator
的监听器//添加中止的监听器
addPauseListener( object : Animator.AnimatorPauseListener{
override fun onAnimationPause(animation: Animator?) {
Log.i("MainActivity","addPauseListener -- onAnimationPause ------------------------------------")
}
override fun onAnimationResume(animation: Animator?) {
Log.i("MainActivity","addPauseListener -- onAnimationResume ------------------------------------")
}
})
//添加更新的监听器
addUpdateListener(object :ValueAnimator.AnimatorUpdateListener{
override fun onAnimationUpdate(animation: ValueAnimator?) {
// Log.i("MainActivity","addUpdateListener -- onAnimationUpdate ------------------------------------")
}
})
//添加监听器
addListener(object : Animator.AnimatorListener{
override fun onAnimationRepeat(animation: Animator?) {
Log.i("MainActivity","AnimatorListener -- onAnimationRepeat------------------------------------")
}
override fun onAnimationEnd(animation: Animator?) {
Log.i("MainActivity","AnimatorListener -- onAnimationEnd------------------------------------")
}
override fun onAnimationCancel(animation: Animator?) {
Log.i("MainActivity","AnimatorListener -- onAnimationCancel------------------------------------")
}
override fun onAnimationStart(animation: Animator?) {
Log.i("MainActivity","AnimatorListener -- onAnimationStart------------------------------------")
}
})
}
复制代码
设置点击事件监听
R.id.start_object_animator ->{
省略...
mObjectAnim.start()
Log.i("MainActivity","点击开始状态 --${!mObjectAnim.isStarted}")
}
R.id.end_object_animator ->{
mObjectAnim.end()
Log.i("MainActivity","点击结束状态 --${!mObjectAnim.isRunning}")
}
R.id.cancel_object_animator ->{
mObjectAnim.cancel()
Log.i("MainActivity","点击取消状态 -- ")
}
R.id.pause_object_animator->{
if (mObjectAnim.isRunning) {
mObjectAnim.pause()
Log.i("MainActivity","点击暂停状态 --${mObjectAnim.isPaused}")
}
}
R.id.reverse_object_animator->{
Log.i("MainActivity","点击反向状态 --")
mObjectAnim.reverse()
}
R.id.resume_object_animator->{
mObjectAnim.resume()
Log.i("MainActivity","点击继续执行 --")
}
复制代码
点击顺序 (这里点击开始按钮是从新初始化动画,请你们不要误解) 开始start -> 暂停pause ->继续执行resume->结束end 开始start->取消cancel 开始start->结束end
MainActivity: AnimatorListener -- onAnimationStart------------------------------------
MainActivity: 点击开始状态 --false
MainActivity: addPauseListener -- onAnimationPause ------------------------------------
MainActivity: 点击暂停状态 --true
MainActivity: addPauseListener -- onAnimationResume ------------------------------------
MainActivity: 点击继续执行 --
MainActivity: AnimatorListener -- onAnimationEnd------------------------------------
MainActivity: 点击结束状态 --true
MainActivity: AnimatorListener -- onAnimationStart------------------------------------
MainActivity: 点击开始状态 --false
MainActivity: AnimatorListener -- onAnimationCancel------------------------------------
MainActivity: AnimatorListener -- onAnimationEnd------------------------------------
MainActivity: 点击取消状态 --
MainActivity: AnimatorListener -- onAnimationStart------------------------------------
MainActivity: 点击开始状态 --false
MainActivity: AnimatorListener -- onAnimationEnd------------------------------------
MainActivity: 点击结束状态 --true
复制代码
上面是点击产生的log
日志,其中有个addUpdateListener
监听我没有打印信息,由于动画运行中就会不停打印出来,因此就没有打印出来了。 能够看到全部的状态都是能够有回调方法监听的。
这里有个方法要注意一下 cancel()
跟end(
)这个两个方法。
若是动画是已经结束了
end()
的时候,就不会有回调onAnimationCancel
跟onAnimationEnd
两个监听方法了,看一下ValueAnimator
源码中有体现了
@Override
public void cancel() {
关键代码1
if (mAnimationEndRequested) {
return;
}
if ((mStarted || mRunning) && mListeners != null) {
if (!mRunning) {
notifyStartListeners();
}
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
关键代码2
listener.onAnimationCancel(this);
}
}
关键代码3
endAnimation();
}
private void endAnimation() {
关键代码4
mAnimationEndRequested = true;
省略......
for (int i = 0; i < numListeners; ++i) {
关键代码5
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
省略......
}
复制代码
关键代码1:mAnimationEndRequested == true
则不走下面的逻辑,设置true
是在关键代码4
中设置的,也就是说当动画结束调用了endAnimation()
就不会调用到两个回调onAnimationCancel
跟onAnimationEnd
。 关键代码2跟3跟5:就是动画未结束,因此调用了回调onAnimationCancel()
再调用回调onAnimationEnd()
小结:当调用cancel()的时候,动画未结束时则回调
onAnimationCancel()
跟onAnimationEnd()
,当动画结束时,则不会调用任何监听回调方法
end
方法有时候会回调两个回调 分别是onAnimationStart
跟onAnimationEnd
,看一下ValueAnimator
源码中的逻辑
public void end() {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
if (!mRunning) {
// Special case if the animation has not yet started; get it ready for ending
startAnimation();
mStarted = true;
} else if (!mInitialized) {
initAnimation();
}
animateValue(shouldPlayBackward(mRepeatCount, mReversing) ? 0f : 1f);
endAnimation();
}
复制代码
从上面的逻辑能够看到,当调用到!mRunning == true
的时候,会调用startAnimation()
致使回调多一个onAnimationStart
方法。
小结: 当调用
end()
的时候,若是动画处于运行中,则回调onAnimationEnd
,若是不处于运行中,则回调onAnimationStart
跟onAnimationEnd
。
ViewPropertyAnimator
的监听器ObjectAnimator
的监听器,这里的
ViewPropertyAnimator
多了两个回调方法
withStartAction
跟withEndAction
分别在开始跟结束调用,可是只会被调用一次。setListener
中的onAnimationRepeat
回调不会被调用,由于ViewPropertyAnimator
不支持重复 其它的回调跟ObjectAnimator
一致
改变尺寸同时改变透明度,下面这种写法是几种动画一块运行的
R.id.btn_viewProperty_mulity->{
if (isSelect) {
imag_view.animate().scaleX(1.5f).scaleY(1.5f).alpha(0f).duration =2000
}else{
imag_view.animate().scaleX(1.0f).scaleY(1.0f).alpha(1f).duration =2000
}
isSelect = !isSelect
}
复制代码
ObjectAnimator
使用时则是要经过PropertyValuesHolder
来实现 上面的代码能够用
R.id.btn_viewProperty_mulity->{
if (isSelect) {
var propertyValueHolder1 = PropertyValuesHolder.ofFloat("scaleX",1f,1.5f)
var propertyValueHolder2 = PropertyValuesHolder.ofFloat("scaleY",1f,1.5f)
var propertyValueHolder3 = PropertyValuesHolder.ofFloat("alpha",1f,0f)
ObjectAnimator.ofPropertyValuesHolder(imag_view,propertyValueHolder1,propertyValueHolder2,propertyValueHolder3)
.setDuration(2000).start()
}else{
var propertyValueHolder1 = PropertyValuesHolder.ofFloat("scaleX",1.5f,1f)
var propertyValueHolder2 = PropertyValuesHolder.ofFloat("scaleY",1.5f,1f)
var propertyValueHolder3 = PropertyValuesHolder.ofFloat("alpha",0f,1f)
ObjectAnimator.ofPropertyValuesHolder(imag_view,propertyValueHolder1,propertyValueHolder2,propertyValueHolder3)
.setDuration(2000).start()
}
isSelect = !isSelect
}
复制代码
ofKeyframe
(关键帧),能够把同一个动画属性拆分红多个阶段
var keyframe = Keyframe.ofFloat(0f,0f)//关键帧 0刚才开时的时候
var keyframe1 = Keyframe.ofFloat(0.5f,95f)//关键帧 0.5进行到一半的时候
var keyFrame2 = Keyframe.ofFloat(1f,80f)//关键帧1最后的时候
var holder = PropertyValuesHolder.ofKeyframe("progress", keyframe, keyframe1, keyFrame2)
ObjectAnimator.ofPropertyValuesHolder(circle_view,holder).setDuration(3000).start()
复制代码
interpolator = OvershootInterpolator()
插值器超过结束值后再回弹的效果
也是组合动画的,可让多个动画配合执行,不过它可让动画前后有序执行~~ playTogether
(同时执行)、playSequentially
(顺序执行) 精确配置顺序with()
,before()
,after()
示例代码:
var animator1 = ObjectAnimator.ofFloat(imag_view,"alpha",0f,1f)
animator1.interpolator = AccelerateDecelerateInterpolator()
var animator3 = ObjectAnimator.ofFloat(imag_view,"scaleX",0f,1f)
var animator2 = ObjectAnimator.ofFloat(imag_view,"scaleY",0f,1f)
var animator4 = ObjectAnimator.ofFloat(imag_view,"translationX",0f,200f)
animator4.interpolator = LinearInterpolator()
AnimatorSet().apply {
playTogether(animator2,animator3)
playSequentially(animator1,animator4)
duration = 2000
start()
}
复制代码
AnimatorSet
将多个
ObjectAnimator
放在一块运行,而且
playTogether(animator2,animator3)
表明一块儿运行
playSequentially(animator1,animator4)
表明前后顺序运行 因此就有了下面的效果图 (x轴、y轴方向放大跟透明度由0-1)是一块儿的,而后在x轴方向移动。
上面的播放逻辑也可使用精确配置顺序with(),before(),after()来实现
AnimatorSet().apply {
play(animator1).with(animator2).with(animator3).before(animator4)
duration = 2000
start()
}
复制代码
注意:每一个传入给
AnimatorSet
的animator
能够本身定义本身的动画运行时间、差值器等,可是若是在AnimatorSet
设置了运行时间的话则以在AnimatorSet
设置的为准。