【HenCoder Android 开发进阶】自定义 View 1-7:属性动画(进阶篇)
Android 属性动画:这是一篇很详细的 属性动画 总结&攻略
属性动画需要了解的知识点:
ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback
ObjectAnimator extends ValueAnimator
AnimatorSet extends Animator
View的anim
public static Keyframe ofFloat(float fraction, float value)
fraction:表示当前的显示进度,即从加速器中getInterpolation()函数的返回值;
value:表示当前应该在的位置
第一步:生成Keyframe对象;
第二步:利用PropertyValuesHolder.ofKeyframe()生成PropertyValuesHolder对象
第三步:ObjectAnimator.ofPropertyValuesHolder()生成对应的Animator
同一个动画中改变多个属性:多个动画同时执行
方式一:ViewPropertyAnimator
view.animate()
.scaleX(1)
.scaleY(1)
.alpha(1);
方式二:PropertyValuesHolder
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();
方式三:AnimatorSet (可指定动画顺序)
ObjectAnimator animator1 = ObjectAnimator.ofFloat(...);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(...);
animator2.setInterpolator(new DecelerateInterpolator());
AnimatorSet animatorSet = new AnimatorSet();
// 两个动画依次执行
animatorSet.playSequentially(animator1, animator2);
// 两个动画同时执行
animatorSet.playTogether(animator1, animator2);
// 使用 AnimatorSet.play(animatorA).with/before/after(animatorB)
// 的方式来精确配置各个 Animator 之间的关系
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
PropertyValuesHolder的使用
通过设置 Keyframe (关键帧),把同一个动画属性拆分成多个阶段。
//1、设置关键帧
//参数:动画进度百分比,动画属性值
Keyframe.ofFloat(float fraction, float value)
Keyframe.ofInt(float fraction, int value)
Keyframe.ofObject(float fraction, Object value)
//2、将关键帧添加到PropertyValuesHolder
//参数:属性名(需要set/get方法),关键帧Keyframe
PropertyValuesHolder.ofKeyframe(String propertyName, Keyframe... values)
//参数:属性值、属性值
PropertyValuesHolder.ofFloat(String propertyName, float... values)
//参数:属性值、属性值
PropertyValuesHolder.ofInt(Property<?, Integer> property, int... values);
//参数:属性值、TypeEvaluator、属性值
PropertyValuesHolder.ofObject(String propertyName, TypeEvaluator evaluator, Object... values);
//3、添加到ObjectAnimator
//参数:执行动画的对象,属性值Holder
ObjectAnimator.ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)
PropertyValuesHolder使用示例:
KeyframeView
public class KeyframeView extends View {
final float radius = dpToPixel(80);
float progress = 0;
RectF arcRectF = new RectF();
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public KeyframeView(Context context) {
super(context);
}
public KeyframeView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public KeyframeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
{
paint.setTextSize(dpToPixel(40));
paint.setTextAlign(Paint.Align.CENTER);
}
public float getProgress() {
return progress;
}
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
float centerX = getWidth() / 2;
float centerY = getHeight() / 2;
paint.setColor(Color.parseColor("#E91E63"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(dpToPixel(15));
arcRectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.FILL);
canvas.drawText((int) progress + "%", centerX, centerY - (paint.ascent() + paint.descent()) / 2, paint);
}
}
Utils
public class Utils {
public static float dpToPixel(float dp) {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
return dp * metrics.density;
}
}
布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="执行动画" />
<Button
android:id="@+id/firstview"
android:layout_width="200dp"
android:layout_height="200dp"
android:text="我是一个view" />
<com.joanzapata.android.KeyframeView
android:id="@+id/secondview"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_below="@+id/firstview"
android:layout_marginTop="20dp" />
</LinearLayout>
代码:
public class MyActivityI extends AppCompatActivity implements View.OnClickListener {
private Button mButton;
private Button view1;
private KeyframeView view2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_anim);
mButton = (Button) findViewById(R.id.btn);
view1 = (Button) findViewById(R.id.firstview);
view2 = (KeyframeView) findViewById(R.id.secondview);
mButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
anim1();
anim2();
}
private void anim2() {
// 在 0% 处开始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);
// 时间经过 50% 的时候,动画完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);
// 时间见过 100% 的时候,动画完成度倒退到 80%,即反弹 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view2, holder);
animator.setDuration(5000);
animator.start();
}
/**
* PropertyValuesHolder这个类可以先将动画属性和值暂时的存储起来,后一起执行,在有些时候可以使用替换掉AnimatorSet,减少代码量
*/
private void anim1() {
//keyframe
Keyframe keyframe1 = Keyframe.ofFloat(0.0f, 0);
Keyframe keyframe2 = Keyframe.ofFloat(0.25f, -30);
Keyframe keyframe3 = Keyframe.ofFloat(0.5f, 30);
Keyframe keyframe4 = Keyframe.ofFloat(1.0f, 0);
PropertyValuesHolder rotation = PropertyValuesHolder.ofKeyframe(View.ROTATION, keyframe1, keyframe2, keyframe3, keyframe4);
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.2f, 1.0f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.2f, 1.0f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.2f, 1.0f);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view1, alpha, scaleX, scaleY, rotation);
animator.setInterpolator(new OvershootInterpolator());
animator.setDuration(5000).start();
}
}