💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
Android群英传读书笔记(第七章) 本章主要介绍的是Android动画机制和使用技巧 # 1.Android视图动画分析 ## 透明度动画: ~~~ AlphaAnimation aa=new AlphaAnimation(0,1); aa.setDuration(1000); view.startAnimation(aa); ~~~ ## 旋转动画: ~~~ RotateAnimation ra=new RotateAnimation(0,360,100,100);//参数分别为起始角度,旋转到角度,中心xy坐标 ra.setDuration(1000); view.startAnimation(ra); ~~~ 也可以绕自身中心旋转: ~~~ RotateAnimation ra=new RotateAnimation(0,360,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f); ra.setDuration(1000); view.startAnimation(ra); ~~~ ## 位移动画: ~~~ TranslateAnimation ta=new TranslateAnimation(0,300,0,200); ta.setDuration(1000); view.startAnimation(ta); ~~~ ## 缩放动画: ~~~ ScaleAnimation sa=new ScaleAnimation(0,2,0,2); sa.setDuration(1000); view.startAnimation(sa); ~~~ 和旋转动画一样,缩放也能以中心点进行: ~~~ ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); sa.setDuration(1000); view.startAnimation(sa); ~~~ ## 动画集合: ~~~ AnimationSet as=new AnimationSet(true); as.setDuration(1000); AlphaAnimation aa=new AlphaAnimation(0,1); aa.setDuration(1000); as.addAnimation(aa); TranslateAnimation ta=new TranslateAnimation(0,100,0,200); ta.setDuration(1000); as.addAnimation(ta); view.startAnimation(as); ~~~ ## 动画回调: ~~~ as.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { //动画开始时回调 } @Override public void onAnimationEnd(Animation animation) { //动画结束时回调 } @Override public void onAnimationRepeat(Animation animation) { //重复时回调 } }); ~~~ * 总结:视图动画的效果比较局限,并且view本身是不会跟着平移动画移动的,只有在实现简单效果并且不需和用户发生交互时建议使用。 # 2.Android属性动画分析 属性动画是在3.0以后推出的,如果需兼容之前的版本需使用开源项目:NineOldAndroids(可以通过谷歌查询其用法,此处不做介绍) 属性动画通过调用属性的set、get方法来真实的控制一个View的属性,还可以调用setFrameDelay()设置动画帧之间的间隙时间,减少动画过程中频繁绘制界面。 ## * ObjectAnimator: ~~~ ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(view,"translationX",300); objectAnimator.setDuration(1000); objectAnimator.start();//属性动画的简单使用。 ~~~ 下面是些常用的属性: * translationX和translationY:作为一种增量来控制着View对象从它布局容器左上角坐标的偏移位置。 * rotation、rotationX和rotationY:这三个属性控制View对象围绕支点进行2D和3D的旋转。 * scaleX和scaleY:这两个属性控制着View对象围绕它的支点进行2D缩放。 * pivotX和pivotY:这两个属性控制着View对象的支点位置,围绕这个支点进行旋转缩放,默认情况下是View的中心点。 * x和y:它描述了View对象在它容器中的最终位置,它是最初的左上角坐标的translationX和translationY值的累积和。 * alpha:它表示View对象的透明度,1是不透明,0为全透明。 如果一个属性没有get、set方法时就需要用其他解决方案了,Google提供了两种方法来解决这个问题。 ①通过一个自定义类来间接的提供这个属性的get、set方法: ~~~ private static class WrapperView{ //使用时就new一个WrapperView对象即可 private View mTarget; public WrapperView(View target){ mTarget=target; } public int getWidth(){ return mTarget.getLayoutParams().width; } public void setWidth(int width){ mTarget.getLayoutParams().width=width; mTarget.requestLayout(); } } ~~~ ②使用ValueAnimator来实现:本文后面会介绍。 ## PropertyValuesHolder: 类似于视图动画中的AnimationSet。这里直接上代码 ~~~ PropertyValuesHolder holder0 = PropertyValuesHolder.ofFloat("translationX", 300); PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f); PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("translationY", 300); ObjectAnimator.ofPropertyValuesHolder(view, holder0, holder1, holder2).setDuration(1000); ~~~ ## ValueAnimator: ObjectAnimator继承自ValueAnimator。其本身不提供任何动画效果,它更像是一个数值发生器。 ~~~ ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,100); valueAnimator.setTarget(view); valueAnimator.setDuration(1000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //监听数值的变换,从而完成动画的变换 Float value= (Float) animation.getAnimatedValue(); } }); ~~~ ## 动画事件的监听: ~~~ ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 0.5f); objectAnimator.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。 objectAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); } }); objectAnimator.start(); ~~~ ## AnimatorSet: AnimatorSet不仅可以实现PropertyValuesHolder这样的效果(同时执行多个动画操作),它还能实现更为精确的顺序控制。(注意这里是AnimatorSet,而不是AnimationSet) ~~~ ObjectAnimator objectAnimator0=ObjectAnimator.ofFloat(view,"scaleX",1f,0,1f); ObjectAnimator objectAnimator1=ObjectAnimator.ofFloat(view,"scaleY",1f,0,1f); AnimatorSet set=new AnimatorSet(); set.setDuration(1000); set.playTogether(objectAnimator0,objectAnimator1); set.start(); ~~~ 控制顺序有以下方法: * playTogether():一起执行。 * playSequentially():上个动画结束后下个才会执行。 * animSet.play(anim0).with(anim1):anim0和anim1一起执行。 * animSet.play(anim0).before(anim1):anim0在anim1之前执行。 * animSet.play(anim0).after(anim1):anim0在anim1之后执行。 ## 在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" android:valueTo="0.5"> </objectAnimator> ~~~ 在代码中的使用: ~~~ Animator animator= AnimatorInflater.loadAnimator(getContext(),R.anim.test); animator.setTarget(view); animator.start(); ~~~ View的animate方法: 在3.0后,google公司给View增加了animate方法来直接驱动属性动画,代码如下: ~~~ //withStartAction和withEndAction在api16以上才有。 view.animate().alpha(0).y(300).setDuration(1900).withStartAction(new Runnable() { @Override public void run() { } }).withEndAction(new Runnable() { @Override public void run() { } }).start(); ~~~ 3.Android布局动画 所谓布局动画是指作用在ViewGroup上,给ViewGroup增加View时添加的一个动画过渡效果。 最简单的布局动画是在ViewGroup的xml中,使用以下代码来打开布局动画: ` android:animateLayoutChanges="true" //此为固定默认效果` 当然我们也能够通过LayoutAnimationController类来自定义一个子View的过渡效果。 ~~~ LinearLayout ll=new LinearLayout(getContext()); //创建动画 ScaleAnimation sa=new ScaleAnimation(0,1,0,1); sa.setDuration(1000); //设置布局动画的显示属性 //第二个参数是每个子View显示的delay时间。当delay不为0时可以设置子View显示顺序 //LayoutAnimationController.ORDER_NORMAL 顺序 //LayoutAnimationController.ORDER_RANDOM 随机 //LayoutAnimationController.ORDER_REVERSE 反序 LayoutAnimationController controller=new LayoutAnimationController(sa,0.5f); controller.setOrder(LayoutAnimationController.ORDER_NORMAL); ll.setLayoutAnimation(controller); ~~~ # 4.Interpolators(插值器) 通过插值器,可以定义动画变换的速率,类似于物理中的加速度。 * AccelerateDecelerateInterpolator --- 在动画开始与结束的地方速率改变比较慢,在中间的时候加速 * AccelerateInterpolator --- 在动画开始的地方速率改变比较慢,然后开始加速 * AnticipateInterpolator --- 开始的时候向后然后向前甩 * AnticipateOvershootInterpolator --- 开始的时候向后然后向前甩一定值后返回最后的值 * BounceInterpolator --- 动画结束的时候弹起 * CycleInterpolator --- 动画循环播放特定的次数,速率改变沿着正弦曲线 * DecelerateInterpolator --- 在动画开始的地方快然后慢 * LinearInterpolator --- 以常量速率改变 * OvershootInterpolator --- 向前甩一定值后再回到原来位置 以上基本能满足常用需求,也可以自定义一个插值器。 ~~~ public class MyInterpolator implements Interpolator { private float mFactor; @Override public float getInterpolation(float input) { //通过操作input来改变速率 return mFactor; } } ~~~ # 5.自定义动画 创建自定义动画非常简单,只需要实现它的applyTransformation的逻辑,通常情况下还需要覆盖父类的initialize方法来实现一些初始化工作。 ~~~ public class MyAnim extends Animation { private int mWidth; private int mHeight; @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { super.initialize(width, height, parentWidth, parentHeight); mWidth = width; mHeight = height; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { //这个方法主要就是通过操作transformation中的矩阵对象来呈现动画效果 final Matrix matrix = t.getMatrix(); matrix.preScale(1, 1 - interpolatedTime, mWidth / 2, mHeight / 2); } } ~~~ # 6.Android 5.X SVG矢量动画机制 * SVG是什么: * 可伸缩矢量图形(Scalable Vector Graphics) * 定义用于网络的基于矢量的图形 * 使用xml格式定义图形 * 图像在放大或者改变尺寸的情况下其图形质量不会有损失 * 万维网联盟的标准 * 与诸如DOM和XSL之类的W3C标准是一个整体 * <path>标签: 使用<path>标签创建SVG,就像用指令的方式来控制一只画笔。标签所支持的指令有以下几种: * M=moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制。 * L=lineto(L X,Y):画直线到指定的坐标位置。 * H=horizontal lineto(H X):画水平线到指定的X坐标位置。 * V=vertical lineto(V Y):画水平线到指定的Y坐标位置。 * C=curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝塞尔曲线。 * S=smooth curveto(S X2,Y2,ENDX,ENDY):三次贝塞尔曲线。 * Q=quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝塞尔曲线。 * T=smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路径后的终点。 * A=elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧形。RX、RY为半轴大小,XROTATION指椭圆的X轴水平方向顺时针方向夹角,FLAG1为1时表示大角度弧形,为0表小角度,FLAG2为1时表顺时针,0位逆时针,X、Y为终点坐标 * Z=closepath():关闭路径。 注意: ① 坐标轴以(0,0)为中心,X轴水平向右,Y轴水平向下。 ② 所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系。 ③ 指令和数据间的空格可以省略。 ④ 同一指令出现多次可以只用一个。 * SVG编辑器: 书中介绍了个Inkscape,有兴趣的可以尝试下。 * Android中使用SVG: 谷歌在Android 5.X中提供了VectorDrawable、AnimatedVectorDrawable来帮助支出SVG。 VectorDrawable: ~~~ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="200dp" android:height="200dp"//控制SVG图形的具体大小,height与width的比例需和viewportHeight与viewportWidth的相同,不然会变形 android:viewportHeight="100" android:viewportWidth="100">//图形划分的比例,这里指划分100*100,绘制图形使用坐标(50,50)即为中心点 <group android:name="test" android:rotation="0"> <path android:fillColor="@android:color/holo_blue_light" android:pathData="M 25 50 a 25 25 0 1 1 50 0" /> </group> </vector> ~~~ * AnimatedVectorDrawable: AnimatedVectorDrawable就是给VectorDrawable提供动画效果,通过它来连接静态的VectorDrawable和动态的objectAnimator。 ~~~ <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/vector_demo"> <target android:animation="@anim/anim" android:name="test"/>//这里的name需要和vectordrawable里的name一样 </animated-vector> ~~~ 动画代码如下: ~~~ <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="3000" android:propertyName="rotation"//path中的属性 android:valueFrom="0" android:valueTo="360"> </objectAnimator> ~~~ 具体使用如下: `((Animatable)imageView.getDrawable()).start();`