Android动画小结
来源:互联网 发布:c51单片机引脚图 编辑:程序博客网 时间:2024/05/16 16:06
属性动画总结
Property Animation:属性动画是针对于3.0(API 11)以上版本的系统有效;
动画特点:可以设置给任意的Object,包括还没有渲染到屏幕上的对象;
可扩展性:自定义任何类型和属性的动画;
常用的View属性成员:
- translationX,translationY:控制View的位置,值是相对于View容器左上角坐标的偏移;
- rotationX,rotationY:控制相当于轴心得旋转;
- x,y:控制View在在容器中的位置,即左上角坐标加上translationX和translationY的值;
- alpha:控制View对象的alpha透明度值;
属性动画概述:
/** * This is the superclass for classes which provide basic support for animations which can be * started, ended, and have <code>AnimatorListeners</code> added to them. */public abstract class Animator implements Cloneable { ......}
所有属性动画的BaseClass就是Animator这个抽象类;
由Animator派生出来的类之间的关系:(我们能看到:AnimatorSet 和ValueAnimator是直接子类,实际我们使用的单个动画效果的时候使用的是ObjectAnimator,ObjectAnimator是ValueAnimator的直接子类,关于ValueAnimator的作用和使用稍后分析,关于TimeAnimator本人使用比较少)
属性动画的属性:
- Duration:动画的持续时间;
- TimeInterpolation:定义动画变化速率的接口,这个接口是所有插值器的规范,像线性插值器、非线性插值器都得实现此接口。
- TypeEvaluator:用于定义属性值计算方式的接口,有int、float、color类型,根据属性的起始、结束值、和差值一起计算出当前时间的属性值。
以上扯了那么多用的,其实属性动画就是Animator,补间动画就是Animation先记住这么多区别熟练运用之后再去尝试理解它的设计模式,底层实现方式
/** * 旋转 * @param view */ public void rotateAnimator(View view){ ObjectAnimator.ofFloat(view,"rotationX",50f,95f) .setDuration(3000) .start();//x轴的话默认的就是出于0度,往屏幕里面的方向转的 }
对于ObjectAnimator:提供的几个方法,提供了ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束~~~
动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法~
所以对于我们自定以的View或者说对于控件的设置某一个属性值的方法中并没有重新绘制View的操作,我们就需要自己写代码去更新视图的操作:
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { view.invalidate(); view.postInvalidate(); } })
在不是用AnimatorSet的情况下如何设置多个属性同时变化:
public void rotateAnimator(final View view){ ObjectAnimator anim = ObjectAnimator.ofFloat(view,"abc",1.0f,0f) .setDuration(1000);//这里的abc属性是随便写的 ,因为我们要使用的是ValueAnimator //每一个ObjectAnimator都有一个ValueAnimator,很明先就是指值的变化 anim.start(); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float cVal = (float) valueAnimator.getAnimatedValue(); view.setAlpha(cVal); view.setScaleX(cVal); view.setScaleY(cVal); } }); }//从之前的解释我们知道,属性动画是不断的刷新来重新为View设置新的值来达到动画的效果的
实现一个动画更改多个效果:使用propertyValuesHolder
public void rotateAnimator(final View view){ PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha",1f,0f,1f);//这里后面的值是可以赋多个值的, PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX",1f,0f,1f); PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY",1f,0f,1f); ObjectAnimator.ofPropertyValuesHolder(view,pvhX,pvhY,pvhZ).setDuration(1000).start();//同理这里的holder中也可以同时放进去多个holder }
接下来看一下和ValueAnimator的用法,简单看一下用view垂直移动的动画代码
public void rotateAnimator(final View view){ ValueAnimator animator = ValueAnimator.ofFloat(1.0f,0f); animator.setTarget(view); animator.setDuration(1000); animator.start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { view.setScaleY((Float) valueAnimator.getAnimatedValue()); } }); }//这里的和ObjectAnimator的区别就是没有设置属性值,属性值的更改是在监听事件中进行的,所以好处就是我们不需要关心这个属性值是够有get和set方法
TypeEvaluator的用法
public void rotateAnimator(final View view){ ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration(3000); valueAnimator.setObjectValues(new PointF(0,0)); valueAnimator.setInterpolator(new LinearInterpolator()); valueAnimator.setEvaluator(new TypeEvaluator<PointF>(){ @Override public PointF evaluate(float v, PointF pointF, PointF t1) { Log.i("TAG",200 * v * 3+""); PointF point = new PointF(); point.x = 200 * v * 3;//此时x的值就是在0-600之间均匀的增加的 至于速率是多少我也暂时不知道 point.y = 0.5f * 200 * (v * 3) * (v * 3);//这里的v就相当于是自变量x从0-1变化所以y的值就类似于900*x的平方 return point; } }); valueAnimator.start(); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { PointF point = (PointF) valueAnimator.getAnimatedValue(); //这里可以获取得到PointF中的值x和y对要操作的控件进行赋值操作了 } }); } //这里我们为什么要使用这种方式也很明显 因为x和y的值要同时变化,ofInt和ofFloat就不能使用,所以我们自定义一种TypeEvaluator,如果两个参数仍然不够使用的话我们可以自定义一个Bean作为泛型传递进去就ok
动画的监听事件:
ObjectAnimator animator = ObjectAnimator.ofFloat(view,"alpha",0.5f);//只有一个参数的就是代表初始值 animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { //动画开始的时候 } @Override public void onAnimationEnd(Animator animation) { //动画结束的时候 } @Override public void onAnimationCancel(Animator animation) { //动画取消 } @Override public void onAnimationRepeat(Animator animation) { //动画取消的 } }); //对于之上的方法太长 要复写所有的方法,我们可以使用AnimatorListenerAdapter 去实现其中的一个方法就可以
AnimatorSet的使用
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view,"alpha",0.5f); ObjectAnimator animator2 = ObjectAnimator.ofFloat(view,"translateX",0.5f); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animator1,animator2); //或者 animatorSet.play(animator1).with(animator2); animatorSet.setDuration(1000); animatorSet.setInterpolator(new LinearInterpolator()); animatorSet.start();
以上是使用代码来实现动画,下面是使用xml文件加载出来动画
1.文件路径 res/animator/动画xml文件
先看一个最简单的:
<?xml version="1.0" encoding="utf-8"?><objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="2.0" android:valueType="floatType"></objectAnimator> Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scalex); animator.setTarget(view); animator.start();
动画集合的xml实现
<?xml version="1.0" encoding="utf-8"?><set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="together"> <objectAnimator android:duration="1000" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="0.5" android:valueType="floatType"/> <objectAnimator android:duration="1000" android:propertyName="scaleY" android:valueFrom="1.0" android:valueTo="0.5" android:valueType="floatType"/></set>
在Activity中的实现了上面没什么区别
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.myset); animator.setTarget(view); animator.start();
关于布局动画的实现方式:主要是通过LayoutTransition来实现
- APPEARING ——元素在容器中显示时需要动画显示
- CHANGE_APPEARING——由于容器中要显示一个新的元素,其他元素的变化需要动画显示
- DISAPPERARING——元素在容器中消失时需要动画
- CHANGE_DISAPPERARING——由于容器中某个元素要消失,其他元素的变化需要动画显示
一个布局动画的Demo提供参考
package com.example.administrator.animatortest;import android.animation.Animator;import android.animation.AnimatorInflater;import android.animation.AnimatorListenerAdapter;import android.animation.AnimatorSet;import android.animation.Keyframe;import android.animation.LayoutTransition;import android.animation.ObjectAnimator;import android.animation.PropertyValuesHolder;import android.animation.TypeEvaluator;import android.animation.ValueAnimator;import android.graphics.Color;import android.graphics.PointF;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.animation.LinearInterpolator;import android.widget.Button;import android.widget.GridLayout;import android.widget.LinearLayout;public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private Button mButtonAdd; private Button mButtonReset; private GridLayout mGridLayout; private int buttonNumbers = 1; private LayoutTransition mLayoutTransition; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButtonAdd = (Button) findViewById(R.id.layout_animator_addbutton); mButtonAdd.setOnClickListener(this); mButtonReset = (Button) findViewById(R.id.layout_animator_resetbutton); mButtonReset.setOnClickListener(this); mGridLayout = (GridLayout) findViewById(R.id.layout_animator_gridview); //为GridView设置LayoutTransition对象 mLayoutTransition = new LayoutTransition(); mGridLayout.setLayoutTransition(mLayoutTransition); mLayoutTransition.setDuration(300); mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING,30); mLayoutTransition.setStagger(LayoutTransition.CHANGE_DISAPPEARING,30); //和setStagger的区别就是setStagger是通过第一个参数来作为条件单独为这种情况下设置动画时间 //所以duration是要放在前面的说 不然放在后面 就把之前的属性重新覆盖了 //初始化自定义的动画效果 customLayoutTransition(); } /** * 自定义效果 */ private void customLayoutTransition() { //这里的话是为出现的View设置的动画效果 ObjectAnimator mAnimatorAppearing = ObjectAnimator. ofFloat(null,"rotationY",90.0f,0.0f) .setDuration(mLayoutTransition.getDuration(LayoutTransition.APPEARING));//添加的动画时间 //为LayoutTransition设置动画和类型 mLayoutTransition.setAnimator(LayoutTransition.APPEARING,mAnimatorAppearing); /** * Add Button * LayoutTransition.CHANGE_APPEARING * 当增加一个Button时,设置其他Button的动画效果 */ PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1); PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1); PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1); PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1); PropertyValuesHolder mHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f,0.0f,1.0f); PropertyValuesHolder mHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f,0.0f,1.0f); ObjectAnimator mObjectAnimatorChangeAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft, pvhTop,pvhRight,pvhBottom,mHolderScaleX,mHolderScaleY).setDuration(mLayoutTransition .getDuration(LayoutTransition.CHANGE_APPEARING)); mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, mObjectAnimatorChangeAppearing); mObjectAnimatorChangeAppearing.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // TODO Auto-generated method stub super.onAnimationEnd(animation); View view = (View) ((ObjectAnimator) animation).getTarget(); view.setScaleX(1f); view.setScaleY(1f); } }); /** * Delete Button * LayoutTransition.DISAPPEARING * 当删除一个Button时,设置该Button的动画效果 */ ObjectAnimator mObjectAnimatorDisAppearing = ObjectAnimator.ofFloat(null, "rotationX", 0.0f,90.0f) .setDuration(mLayoutTransition.getDuration(LayoutTransition.DISAPPEARING)); mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, mObjectAnimatorDisAppearing); mObjectAnimatorDisAppearing.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // TODO Auto-generated method stub super.onAnimationEnd(animation); View view = (View) ((ObjectAnimator) animation).getTarget(); view.setRotationX(0.0f); } }); /** * Delete Button * LayoutTransition.CHANGE_DISAPPEARING * 当删除一个Button时,设置其它Button的动画效果 */ //Keyframe 对象中包含了一个时间/属性值的键值对,用于定义某个时刻的动画状态。 Keyframe mKeyframeStart = Keyframe.ofFloat(0.0f, 0.0f); Keyframe mKeyframeMiddle = Keyframe.ofFloat(0.5f, 180.0f); Keyframe mKeyframeEndBefore = Keyframe.ofFloat(0.999f, 360.0f); Keyframe mKeyframeEnd = Keyframe.ofFloat(1.0f, 0.0f); PropertyValuesHolder mPropertyValuesHolder = PropertyValuesHolder.ofKeyframe("rotation", mKeyframeStart,mKeyframeMiddle,mKeyframeEndBefore,mKeyframeEnd); ObjectAnimator mObjectAnimatorChangeDisAppearing = ObjectAnimator.ofPropertyValuesHolder(this, pvhLeft,pvhTop,pvhRight,pvhBottom,mPropertyValuesHolder) .setDuration(mLayoutTransition.getDuration(LayoutTransition.CHANGE_DISAPPEARING)); mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, mObjectAnimatorChangeDisAppearing); mObjectAnimatorChangeDisAppearing.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // TODO Auto-generated method stub super.onAnimationEnd(animation); View view = (View) ((ObjectAnimator) animation).getTarget(); view.setRotation(0.0f); } }); } /** * 点击事件 * @param v */ @Override public void onClick(View v) { switch (v.getId()){ case R.id.layout_animator_addbutton: addButton(); break; case R.id.layout_animator_resetbutton: resetButton(); break; default: break; } } /** * 删除所有的View重置GridLayout */ private void resetButton() { mGridLayout.removeAllViews(); buttonNumbers=1; } /** * 增加一个Button */ private void addButton() { Button mButton = new Button(this); LinearLayout.LayoutParams mLayoutParams = new LinearLayout.LayoutParams(50,50); mButton.setLayoutParams(mLayoutParams); mButton.setText(String.valueOf(buttonNumbers++)); mButton.setTextColor(Color.rgb(0,0,0)); if(buttonNumbers % 2 ==1){ mButton.setBackgroundColor(Color.rgb(45,118,87)); }else { mButton.setBackgroundColor(Color.rgb(225,24,0)); } mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mGridLayout.removeView(v); } }); mGridLayout.addView(mButton,Math.min(1,mGridLayout.getChildCount()));//这里使用的是插入的方法 所以添加的话也会导致 其他view的变化 }}布局文件
这个Dmeo 基本上涵盖了属性动画的用法和布局动画的用法,手动敲一下还是能更深入的理解的
- Android 动画学习小结
- Android动画实现小结
- Android-动画小结
- android属性动画小结
- Android动画小结
- 学习Android动画小结
- Android动画小结
- Android动画小结
- Android动画小结
- Android动画小结
- Android动画小结
- Android 属性动画总小结
- Android属性动画个人小结
- 替换Android手机的开机动画小结
- 替换Android手机的开机动画小结
- Android动画学习笔记(三)—基础动画小结
- 动画小结
- Android之动画ViewPager小结(QQ续三)
- 软件爱 设计模式--工厂模式(简单工厂,工厂方法,抽象工厂)
- 怎么开发一款区块链
- 魂系列游戏机制分析
- 【HDU】5604 merge【set练习题】
- GoDemo:给定数字字符串求可能的ip组合,数字顺序不变
- Android动画小结
- 正态分布中normpdf和normcdf的区别
- 数据库基础与索引
- OpenGL中常用函数
- net.mindview.util.print.*包不存在的解决方法
- 谈论指针(一)
- sql语句where子句like的用法详解
- java 和C++语法区别
- 理解矩阵乘法