Android 动画机制与使用技巧
2016-08-05 16:59
519 查看
动画效果一直是人机交互中非常重要的部分,与死板、突兀的显示效果不同,动画效果的加入,让交互变得更加友好,特别是在提示、引导类的场景中,合理地使用动画能让用户获得更加愉悦的使用体验
实现原理:
每次绘制View时,ViewGroup中的drawChild函数获取该view的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix())
通过矩阵运算完成帧动画,如果动画没有完成,就继续调用invalidate() 函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
一般手机的开机动画,应用的等待动画等都是帧动画,因为只需要几张图片轮播,极其节省资源,如果真的设计成动画,那么是很耗费资源的事。
在res目录下新建一个drawable文件夹并定义xml文件,子节点为 animation-list,在这里定义要显示的图片和每张图片的显示时长。
在屏幕上播放帧动画,需要布局文件有一个ImageView来显示动画图片
主要是在Android 3.0之前
最大的缺陷就是不具备交互性
位移、旋转、缩放、透明
属性动画是组件的位置发生了真实的改变,而且在动画的过程中组件的位置是实时改变的,可以相应组件事件。
使用最多的就是 AnimatorSet和ObjectAnimator配合:
使用ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值
使多个ObjectAnimator组合到AnimatorSet形成一个动画
ObjectAnimator可以自动驱动:
调用setFrameDelay()设置动画帧之间的间隙时间,调整帧率,减少动画绘制过程中频繁绘制,在不影响动画效果的情况下减少CPU资源消耗
属性动画基本可以实现左右动画
但是View的该属性一定要具有 set和get 方法
一些常用的可以直接使用的属性:
translationX 和translationY: 控制View从父容器的左上角的偏移
rotation、rotationX、rotationY : 控制View围绕 “支点”进行2D和3D旋转
scaleX、scaleY : 控制View围绕 “支点”进行2D缩放
pivotX、pivotY : 控制View的“支点”位置,默认为View的中心点
x、y : 这两个属性描述了View对象在父容器中的最终位置,它是最初左上角坐标和translationX 、translationY值的累计和
alpha : 透明度
可以用xml配置属性动画只需要在res目录下创建一个property animator属性动画文件
方案二: 通过ValueAnimator实现
ValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数字,调用者通过这个过程来执行自己特定的动画逻辑
这里边有一个重要的思想,如果一个接口需要实现的方法比较多,而通常时候又不需要实现那么多方法,导致代码乱乱的,这个时候,可以写一个抽象类来实现这个接口的所有方法,由于接口的实现类也是接口类型,所以使用的时候就可以只复写这个抽象类中感兴趣的方法就好了
最简单的布局动画是在ViewGroup的xml中使用如下属性打开布局动画
但是这个默认的动画效果无法替换
使用LayoutAnimationController类来自定义一个过渡效果
AccelerateDecelerateInterpolator开始与结束的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator开始的时候向后然后向前甩
AnticipateOvershootInterpolator开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator动画结束的时候弹起
CycleInterpolator循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator在开始的地方快然后慢
创建的时候,可以传factor值,如DecelerateInterpolator(2f):
LinearInterpolator以常量速率改变
OvershootInterpolator向前甩一定值后再回到原来位置
创建的时候,可以传tension值,OvershootInterpolator(0.8f):
定义用于网络的基于矢量的图形
使用XML格式定义图形
图像在放大或改变尺寸的情况下其图形质量不会有损失
与Bitmap对比,SVG最大的优点就是方法不失真,而且不需要为不同分辨率设计多套图标
一、Android View动画框架
Animation框架定义了透明度、旋转、缩放、位移等几种常见的动画实现原理:
每次绘制View时,ViewGroup中的drawChild函数获取该view的Animation的Transformation值,然后调用canvas.concat(transformToApply.getMatrix())
通过矩阵运算完成帧动画,如果动画没有完成,就继续调用invalidate() 函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。
二、帧动画
帧动画就是一张张图片不同的切换,形成的动画效果。一般手机的开机动画,应用的等待动画等都是帧动画,因为只需要几张图片轮播,极其节省资源,如果真的设计成动画,那么是很耗费资源的事。
在res目录下新建一个drawable文件夹并定义xml文件,子节点为 animation-list,在这里定义要显示的图片和每张图片的显示时长。
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"><!-- false表示循环播放,true表示只播放一次 --> <item android:drawable="@drawable/g1" android:duration="200" /> <item android:drawable="@drawable/g2" android:duration="200" /> <item android:drawable="@drawable/g3" android:duration="200" /> <item android:drawable="@drawable/g4" android:duration="200" /> <item android:drawable="@drawable/g5" android:duration="200" /> <item android:drawable="@drawable/g6" android:duration="300" /> <item android:drawable="@drawable/g7" android:duration="400" /><!-- 慢动作 --> <item android:drawable="@drawable/g8" android:duration="500" /> <item android:drawable="@drawable/g9" android:duration="200" /> <item android:drawable="@drawable/g10" android:duration="200" /> <item android:drawable="@drawable/g11" android:duration="200" /> </animation-list>
在屏幕上播放帧动画,需要布局文件有一个ImageView来显示动画图片
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ImageView iv = (ImageView) findViewById(R.id.iv); //把动画文件设置为imageView的背景 iv.setBackgroundResource(R.drawable.frameanimation); AnimationDrawable ad = (AnimationDrawable) iv.getBackground(); //播放动画 ad.start(); } }
三、补间动画(视图动画)
组件由原始状态向终极状态转变时,为了让过渡更自然,而自动生成的动画叫做补间动画。主要是在Android 3.0之前
最大的缺陷就是不具备交互性
位移、旋转、缩放、透明
public class MainActivity extends Activity { private ImageView iv; private TranslateAnimation ta; private RotateAnimation ra; private ScaleAnimation sa; private AlphaAnimation aa; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); } //位移动画 public void translate(View v){ //创建位移动画的对象,设置动画的初始位置和结束位置 //10,100表示,从imageview的真实坐标的左上角 x+10,移动到 x+100的位置 //20,200表示,从imageview的真实坐标的左上角 y+20,移动到 y+200的位置 //ta = new TranslateAnimation(10, 100, 20, 200); //Animation.RELATIVE_TO_SELF相对于自己 //对于x,表示,相对于自己,起点是 imageview的真是坐标的左上角 x+ 0.5*iv的宽度 到 x+ 2*iv的宽度 //y 就是乘以 iv 的高度,也可以相对于父控件,但是用的比较少 ta = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2); //设置播放时间 ta.setDuration(2000); //设置重复次数,播放一次,重复一次 ta.setRepeatCount(1); //设置重复放的模式 ta.setRepeatMode(Animation.REVERSE); //动画播放完毕后,组件停留在动画结束的位置上 ta.setFillAfter(true); //播放动画 iv.startAnimation(ta); } //旋转动画 public void rotate(View v){ //20表示开始的角度,180表示结束的角度,默认的旋转圆心在iv左上角 //ra = new RotateAnimation(20, 180); //指定圆心坐标,相对于自己, iv真实的坐标左上角 x+ 0.5*iv宽度, y+0.5*iv高度 ra = new RotateAnimation(20, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); ra.setDuration(2000); ra.setRepeatCount(1); ra.setRepeatMode(Animation.REVERSE); iv.startAnimation(ra); } //缩放动画 public void scale(View v){ //sa = new ScaleAnimation(fromX, toX, fromY, toY); //改变缩放的中心点,相对于自己的中心坐标,iv的真是坐标左上角 x+0.5*iv宽度, y+0.5*iv高度 sa = new ScaleAnimation(0.5f, 2, 0.1f, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); sa.setDuration(2000); sa.setRepeatCount(1); sa.setRepeatMode(Animation.REVERSE); iv.startAnimation(sa); } //透明动画 public void alpha(View v){ //0为完全透明,1为完全透明 aa = new AlphaAnimation(0, 1); aa.setDuration(2000); aa.setRepeatCount(1); aa.setRepeatMode(Animation.REVERSE); iv.startAnimation(aa); } //所有动画一起飞 public void fly(View v){ //创建动画集合 false表示每个动画的时间校准有动画自己决定, true表示有动画集合决定 AnimationSet set = new AnimationSet(false); set.addAnimation(aa); set.addAnimation(ra); set.addAnimation(sa); set.addAnimation(ta); iv.startAnimation(set); } }
四、属性动画
补间动画,只是一个动画效果,组件其实还在原来的位置上,xy没有改变。属性动画是组件的位置发生了真实的改变,而且在动画的过程中组件的位置是实时改变的,可以相应组件事件。
使用最多的就是 AnimatorSet和ObjectAnimator配合:
使用ObjectAnimator进行更精细化控制,只控制一个对象的一个属性值
使多个ObjectAnimator组合到AnimatorSet形成一个动画
ObjectAnimator可以自动驱动:
调用setFrameDelay()设置动画帧之间的间隙时间,调整帧率,减少动画绘制过程中频繁绘制,在不影响动画效果的情况下减少CPU资源消耗
属性动画基本可以实现左右动画
但是View的该属性一定要具有 set和get 方法
1.ObjectAnimator
内部是通过反射机制实现的,所以该属性一定要具有 set和get方法一些常用的可以直接使用的属性:
translationX 和translationY: 控制View从父容器的左上角的偏移
rotation、rotationX、rotationY : 控制View围绕 “支点”进行2D和3D旋转
scaleX、scaleY : 控制View围绕 “支点”进行2D缩放
pivotX、pivotY : 控制View的“支点”位置,默认为View的中心点
x、y : 这两个属性描述了View对象在父容器中的最终位置,它是最初左上角坐标和translationX 、translationY值的累计和
alpha : 透明度
public class MainActivity extends Activity { private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); iv.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "点不到我", 0).show(); } }); } public void translate(View v){ //target:动画作用于哪个组件 //protertyName指定要改变组件的哪个属性, //属性动画是真正的改变组件的属性的,每个组件对应一个java类,所以,protertyName是指组件Java类中具有get,set方法的成员变量 //iv.setXXX //values 是可变参数,就是赋予属性的新的值,可以往回走 //ObjectAnimator oa = ObjectAnimator.ofFloat(target, propertyName, values); ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 10, 70, 20, 100); oa.setDuration(2000); oa.setRepeatCount(1); oa.setRepeatMode(ValueAnimator.REVERSE); oa.start(); } public void scale(View v){ ObjectAnimator oa = ObjectAnimator.ofFloat(iv,"scaleX", 1, 1.6f, 1.2f, 2); oa.setDuration(2000); oa.start(); } public void alpha(View v){ ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "alpha", 0, 0.6f, 0.2f, 1); oa.setDuration(2000); oa.start(); } public void rotate(View v){ ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "rotationY", 0, 180, 90, 360); oa.setDuration(2000); oa.setRepeatCount(1); oa.setRepeatMode(ValueAnimator.REVERSE); oa.start(); } public void fly(View v){ AnimatorSet set = new AnimatorSet(); ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 10, 70, 20, 100); oa1.setDuration(2000); oa1.setRepeatCount(1); oa1.setRepeatMode(ValueAnimator.REVERSE); ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "translationY", 10, 70, 20, 100); oa2.setDuration(2000); oa2.setRepeatCount(1); oa2.setRepeatMode(ValueAnimator.REVERSE); ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1, 1.6f, 1.2f, 2); oa3.setDuration(2000); oa3.setRepeatCount(1); oa3.setRepeatMode(ValueAnimator.REVERSE); ObjectAnimator oa4 = ObjectAnimator.ofFloat(iv, "rotation", 0, 180, 90, 360); oa4.setDuration(2000); oa4.setRepeatCount(1); oa4.setRepeatMode(ValueAnimator.REVERSE); //设置挨个飞 //set.playSequentially(oa1, oa2, oa3, oa4); //设置一起飞 set.playTogether(oa1, oa2, oa3, oa4); set.start(); } //使用xml文件配置属性动画 public void xml(View v){ //加载属性动画文件 Animator animator = AnimatorInflater.loadAnimator(this, R.animator.objanimator); //设置作用于哪个组件 animator.setTarget(iv); animator.start(); } }
可以用xml配置属性动画只需要在res目录下创建一个property animator属性动画文件
<set xmlns:android="http://schemas.android.com/apk/res/android" > <objectAnimator android:propertyName="translationX" android:duration="200" android:repeatCount="1" android:repeatMode="reverse" android:valueFrom="-100" android:valueTo="100" > </objectAnimator> </set>
2. 如果属性没有set 和 get方法的解决方法
方案一: 通过自定义一个属性类或者包装类,类间接的给这个属性增加 get、set方法/** * Created at: 2016/8/5 14:21. * by author: mwp * 描述:使用属性动画时,给没有set和get方法的属性包装工具 */ public class WrapperView { private View mTarget; public WrapperView(View target) { this.mTarget = target; } /**包装宽度属性*/ public int getWidth(){ return mTarget.getLayoutParams().width; } public void setWidth(int width){ mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } //使用方法 public void use(){ WrapperView wrapper = new WrapperView(mButton); ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start(); } }
方案二: 通过ValueAnimator实现
3. ValueAnimator(值动画)
ObjectAnimator 也是集成自ValueAnimatorValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数字,调用者通过这个过程来执行自己特定的动画逻辑
ValueAnimator animator = ValueAnimator.ofInt(0,100); animator.setTarget(view); animator.setDuration(1000).start(); //最核心的方法 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 进度百分比 float value = animation.getAnimatedFraction(); } });
4. 动画监听
监听动画的过程执行一些操作ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha",0.5f); anim.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) { } }); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { super.onAnimationEnd(animation); } });
这里边有一个重要的思想,如果一个接口需要实现的方法比较多,而通常时候又不需要实现那么多方法,导致代码乱乱的,这个时候,可以写一个抽象类来实现这个接口的所有方法,由于接口的实现类也是接口类型,所以使用的时候就可以只复写这个抽象类中感兴趣的方法就好了
5. View的animate方法
在3.0之后,Google给View增加了animate方法来直接驱动属性动画view.animate() .alpha(0) .scaleX(1) .x(300) .y(200) .setDuration(1000) .withStartAction(new Runnable() { @Override public void run() { //动画开始 } }) .withEndAction(new Runnable() { @Override public void run() { //动画结束 runOnUiThread(new Runnable() { @Override public void run() { //执行一些主线程方法 } }); } }) .start();
6. Android 布局动画
布局动画是指作用在ViewGroup上,给ViewGroup增加View时添加一个动画过渡效果最简单的布局动画是在ViewGroup的xml中使用如下属性打开布局动画
android:animateLayoutChanges="true"
但是这个默认的动画效果无法替换
使用LayoutAnimationController类来自定义一个过渡效果
LinearLayout ll = (LinearLayout)findViewById(R.id.ll); //设置过渡动画 ScaleAnimation sa = new ScaleAnimation(0,1,0,1); sa.setDuration(2000); //设置布局动画的显示属性,第二个参数是每个子View显示的delay时间 LayoutAnimationController lac = new LayoutAnimationController(sa,0.5f); lac.setOrder(LayoutAnimationController.ORDER_NORMAL);//顺序,随机,倒序 //为ViewGroup设置布局动画 ll.setLayoutAnimation(lac);
7. Interpolators(插值器 )
插值器是动画中一个非常重要的概念,通过插值器可以定义动画变换速率,其作用主要是控制目标变量的变化值进行对应的变化AccelerateDecelerateInterpolator开始与结束的地方速率改变比较慢,在中间的时候加速
AccelerateInterpolator开始的地方速率改变比较慢,然后开始加速
AnticipateInterpolator开始的时候向后然后向前甩
AnticipateOvershootInterpolator开始的时候向后然后向前甩一定值后返回最后的值
BounceInterpolator动画结束的时候弹起
CycleInterpolator循环播放特定的次数,速率改变沿着正弦曲线
DecelerateInterpolator在开始的地方快然后慢
创建的时候,可以传factor值,如DecelerateInterpolator(2f):
LinearInterpolator以常量速率改变
OvershootInterpolator向前甩一定值后再回到原来位置
创建的时候,可以传tension值,OvershootInterpolator(0.8f):
五、自定义动画
就是现有的透明度,旋转,平移,缩放等行为组合起来仍然不能满足你的话,可以自定义一些更炫的动画public class CustomAnim extends Animation { private int mCenterWidth; private int mCenterHeight; private Camera mCamera = new Camera(); private float mRotateY = 0.0f; @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); // 设置默认时长 setDuration(2000); // 动画结束后保留状态 setFillAfter(true); // 设置默认插值器 setInterpolator(new BounceInterpolator()); mCenterWidth = width / 2; mCenterHeight = height / 2; } // 暴露接口-设置旋转角度 public void setRotateY(float rotateY) { mRotateY = rotateY; } @Override protected void applyTransformation( float interpolatedTime, Transformation t) { final Matrix matrix = t.getMatrix(); mCamera.save(); // 使用Camera设置旋转的角度 mCamera.rotateY(mRotateY * interpolatedTime); // 将旋转变换作用到matrix上 mCamera.getMatrix(matrix); mCamera.restore(); // 通过pre方法设置矩阵作用前的偏移量来改变旋转中心 matrix.preTranslate(mCenterWidth, mCenterHeight); matrix.postTranslate(-mCenterWidth, -mCenterHeight); } }
六、Android 5.X SVG 矢量动画机制
可伸缩矢量图形(Scalable Vector Graphics)定义用于网络的基于矢量的图形
使用XML格式定义图形
图像在放大或改变尺寸的情况下其图形质量不会有损失
与Bitmap对比,SVG最大的优点就是方法不失真,而且不需要为不同分辨率设计多套图标
七、点击view显示隐藏其他View带动画的一个小例子
public class DropTest extends Activity { private LinearLayout mHiddenView; private float mDensity; private int mHiddenViewMeasuredHeight; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.drop); mHiddenView = (LinearLayout) findViewById(R.id.hidden_view); // 获取像素密度 mDensity = getResources().getDisplayMetrics().density; // 获取布局的高度 mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5); } public void llClick(View view) { if (mHiddenView.getVisibility() == View.GONE) { // 打开动画 animateOpen(mHiddenView); } else { // 关闭动画 animateClose(mHiddenView); } } private void animateOpen(final View view) { view.setVisibility(View.VISIBLE); ValueAnimator animator = createDropAnimator( view, 0, mHiddenViewMeasuredHeight); animator.start(); } private void animateClose(final View view) { int origHeight = view.getHeight(); ValueAnimator animator = createDropAnimator(view, origHeight, 0); animator.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { view.setVisibility(View.GONE); } }); animator.start(); } private ValueAnimator createDropAnimator( final View view, int start, int end) { ValueAnimator animator = ValueAnimator.ofInt(start, end); animator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int value = (Integer) valueAnimator.getAnimatedValue(); ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); layoutParams.height = value; view.setLayoutParams(layoutParams); } }); return animator; } }
相关文章推荐
- Android动画机制与使用技巧(三)Android动画特效
- 第七章Android动画机制与使用技巧(Android群英传)
- Android群英传学习——第七章、Android动画机制与使用技巧
- Android动画机制与使用技巧(一)属性动画分析
- Android动画机制与使用技巧
- Android动画机制与使用技巧(四)Android 5.X SVG矢量动画机制
- Android群英传笔记——第七章:Android动画机制和使用技巧
- Android动画机制与使用技巧(五)——Android 5.X SVG 矢量动画机制
- Android动画机制与使用技巧(一)——Android View动画框架
- Android动画机制与使用技巧
- 《Android群英传》读书笔记(8)第七章:Android动画机制与使用技巧
- Android动画机制与使用技巧(四)——Android动画特效
- Android动画机制与使用技巧(二)布局动画、插值器、自定义动画
- Android群英传知识点回顾——第七章:Android动画机制与使用技巧
- Android动画机制与使用技巧(二)——属性动画分析
- Android动画机制与使用技巧(三)——动画补充知识
- Android 动画机制与使用技巧
- Android群英转读书笔记第七章(Android动画机制与使用技巧)
- Android群英传笔记——第七章:Android动画机制和使用技巧
- Android群英传》读书笔记 (3) 第六章 Android绘图机制与处理技巧 + 第七章 Android动画机制与使用技巧