面试准备(九)-- 补间动画、逐帧动画、属性动画
动画
参考:https://www.jianshu.com/p/2412d00a0ce4
分为三种:补间动画、逐帧动画、属性动画
一、补间动画(Tweened Animation)
作用对象:视图控件View
原理: 通过确定开始的视图样式以及结束的视图样式,中间的动画变化过程有系统补全来确定一个动画
优点:简单、方便,已封装好基础动画效果
缺点:仅控制整体实体效果,无法控制属性
动画样式: 平移动画(Translate)缩放动画(scale)旋转动画(rotate)透明度动画(alpha)
应用场景:视图中,标准、基础的动画效果。Activity,Fragment的切换效果。ViewGroup中子元素的出场效果
1.平移动画(Translate)
TranslateAnimation类
示例代码:
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration = "3000"
android:fromXDelta="0"
android:toXDelta="300"
android:fillBefore = "false"
android:fillAfter = "true"
/>
如上代码中,虽然通过fillBefore,fillAfter设置了停留在动画之后,但是控件还是在原来位置,点击动画后的位置无反应。如果不设置fillBefore,fillAfter,动画结束后会还是显示在动画前的位置
Java代码中使用
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
var translateAnim = AnimationUtils.loadAnimation(this,R.anim.view_anim)
tv_content.startAnimation(translateAnim)
tv_content.setOnClickListener {
Toast.makeText(this, "好的", Toast.LENGTH_SHORT).show()
}
}
纯Java代码:
var translateAnim = TranslateAnimation(0f,500f,0f,0f)
translateAnim.duration = 3000
tv_content.startAnimation(translateAnim)
2.缩放动画(Scale)
ScaleAnimation类
xml定义
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="0.0"
android:toXScale="1.4"
android:fromYScale="0.0"
android:toYScale="1.4"
android:pivotX="50%"
android:pivotY="50%"
android:duration = "3000">
</scale>
注意:fromXScale和fromYScale都要写,pivotX只是改变开始缩放的始点,不加缩放的始点是view的左上角,
android:pivotX="50"是控件view的x方向上加50像素
android:pivotX="50%"是控件view的x方向上加view控件宽的50%
android:pivotX="50%p"是控件view的x方向上加父布局控件宽的50%
3.旋转动画(Rotate)
RotateAnimation类
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="180"
android:duration = "2000"
android:pivotX="50%"
android:pivotY="50%"
/>
android:fromDegrees="0" // 动画开始时 视图的旋转角度(正数 = 顺时针,负数 = 逆时针)
4.透明度动画(Alpha)
AlphaAnimation类
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration = "2000">
</alpha>
Activity的切换效果
实现效果从右向左滑动
in_from_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration = "500"
android:fromXDelta="100%p"
android:toXDelta="0%p"
/>
</set>
out_to_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
>
<translate
android:duration="500"
android:fromXDelta="0%p"
android:toXDelta="-100%p"
/>
</set>
一般用在Activity的成对出现,但至少保持动画时间是一样的
首先要了解Activity的位置
x = 0%p是代表Activity刚好在屏幕中间
x = 100%p是代表Activity在屏幕的右侧
x = -100%p是代表Activity在屏幕的左侧
Fragment的切换效果(几乎用不到)
// 采用`FragmentTransavtion`的 setCustomAnimations()进行设置
FragmentTransaction fragmentTransaction = mFragmentManager
.beginTransaction();
fragmentTransaction.setCustomAnimations(
R.anim.in_from_right,
R.anim.out_to_left);
// 此处的自定义动画效果同Activity,此处不再过多描述
组合动画
采用< Set/>标签
监听动画
anim.addListener(new AnimatorListenerAdapter() {
// 向addListener()方法中传入适配器对象AnimatorListenerAdapter()
// 由于AnimatorListenerAdapter中已经实现好每个接口
// 所以这里不实现全部方法也不会报错
@Override
public void onAnimationStart(Animator animation) {
// 如想只想监听动画开始时刻,就只需要单独重写该方法就可以
}
});
二、逐帧动画(Frame Animation)
作用对象:视图控件View
原理:将动画拆分为帧的形式,且定义每一帧是一张图片,按顺序播放
特点:优点:简单、方便
缺点:容易引起OOM,因会用大量以及尺寸较大的图片资源
应用场景: 较为复杂的个性化动画效果
三、属性动画(Property Animation)
作用对象:任意Java对象
原理: 在一定时间间隔内,通过不断改变值以及赋值给对象的属性,从而实现该对象在该属性上的动画效果
特点:优点:作用对象进行了拓展:不只是View对象,甚至无对象也可以,不只是4种基本变换,还有其他动画效果
缺点:用起来稍微复杂点
应用场景: 与属性相关、更加复杂的动画效果,比如通过改变View的颜色属性达到的动画效果
逐帧动画和补间动画存在一定的缺点:
1:作用对象局限于View
即补间动画只能作用View上,没法对非View的对象进行操作。有些情况的动画效果只是视图的某个属性,比如通过视图的颜色动态变化,从而实现动画效果,而不是针对整个View视图进行动画操作
2:没有改变View的属性,只是改变视觉效果
如移动View,虽然看起来View的位置变了,但实际没变,还是原来的
3:动画效果单一
补间动画只能实现平移、旋转、缩放 & 透明度这些简单的动画需求,一旦遇到相对复杂的动画效果,即超出了上述4种动画效果,那么补间动画则无法实现
ValueAnimator类
原理:通过不断控制值的变化,再不断手动赋给对象的属性,从而实现动画效果
Java代码:正常流程
Log.i("tv_content",tv_content.width.toString()+"\t"+tv_content.layoutParams.width)
//value动画
val valueAnimator = ValueAnimator.ofInt(tv_content.layoutParams.width,500)
valueAnimator.duration = 2000
valueAnimator.addUpdateListener {
val currentValue:Int = it.animatedValue as Int
println(currentValue)
//获取不断改变的值给view相应的属性
tv_content.layoutParams.width = currentValue
tv_content.requestLayout()
}
valueAnimator.start()
在xml中定义
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType"
android:duration="2000"
/>
java用xml中定义的
//用xml的value动画
var valueAnimator = AnimatorInflater.loadAnimator(this,R.animator.value_anim)
valueAnimator.setTarget(tv_content)
valueAnimator.start()
注:有点坑地方补间动画xml的文件夹名是anim,属性动画是animator。第二单纯这样setTarget(view)是没用的,它不知道改变什么属性。
所以用属性动画还是用java代码定义写方便
对象:ValueAnimator.ofObject()
作用:将初始值以对象的形式过渡到结束值,即通过操作对象实现动画效果
需我们自定义TypeEvaluator来告知系统如何进行从初始对象过渡到结束对象的逻辑
示例PointEvaluator.java
// 实现TypeEvaluator接口
public class PointEvaluator implements TypeEvaluator {
// 复写evaluate()
// 在evaluate()里写入对象动画过渡的逻辑
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
Point startPoint = (Point) startValue;
Point endPoint = (Point) endValue;
// 根据fraction来计算当前动画的x和y的值
float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
// 将计算后的坐标封装到一个新的Point对象中并返回
Point point = new Point(x, y);
return point;
}
}
估值器(TypeEvaluator)
作用:设置动画 如何从初始值 过渡到 结束值 的逻辑
ObjectAnimator类
是ValueAnimator的子类
是通过反射找到相应的set函数
原理:通过不断控制值的变化,再不断自动赋给对象的属性,从而实现动画效果
示例代码:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotationX",0,270,0);
animator.setDuration(2000);
animator.start();
怎么知道哪些属性是能赋值改变形成动画的呢
1、要使用ObjectAnimator来构造对画,要操作的控件中,必须存在对应的属性的set方法
2、setter 方法的命名必须以骆驼拼写法命名,即set后每个单词首字母大写,其余字母小写,即类似于setPropertyName所对应的属性为propertyName
简单来说:先找View的set相应属性试试,有,就把属性开头大写改为小写即可
自定义ObjectAnimator属性
public class MyPointView extends View {
private PointRadius mPoint = new PointRadius(100);
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mPoint != null) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300, 300, mPoint.getRadius(), paint);
}
}
void setPointRadius(int radius) {
mPoint.setRadius(radius);
invalidate();
}
}
关键setPointRadius方法
使用:
val objectAnimator = ObjectAnimator.ofInt(my_point_view,"pointRadius",0,300,200)
objectAnimator.duration = 2000
objectAnimator.start()