Android中级——动画
创始人
2024-06-03 19:26:30
0

动画

  • 视图动画
    • AlphaAnimation
    • RotateAnimation
    • TranslateAnimation
    • ScaleAnimation
    • AnimationSet
    • 动画监听
  • 属性动画
    • PropertyValuesHolder
    • AnimatorSet
    • ValueAnimator
    • 动画监听
    • XML中使用
    • animate()
  • 布局动画
  • Interpolator
  • 自定义动画
  • SVG矢量动画
    • path
    • vector
    • animate-vector
    • 实例——线图动画
    • 实例——模拟三球仪
    • 实例——轨迹动画
  • 动画特效
    • 灵动菜单
    • 计时器动画
    • 下拉展开动画

视图动画

视图动画不具备交互性,发生动画后,其响应事件还在之前的位置

AlphaAnimation

ImageView iv = findViewById(R.id.iv);
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(3000);
iv.startAnimation(aa);

如上设置3秒从透明到不透明的动画

在这里插入图片描述

RotateAnimation

 ImageView iv = findViewById(R.id.iv);RotateAnimation ra = new RotateAnimation(0, 360, 100, 100);ra.setDuration(3000);iv.startAnimation(ra);

如上设置绕(100, 100)从0°旋转到360°

在这里插入图片描述

若绕自身中心旋转则为

ImageView iv = findViewById(R.id.iv);
RotateAnimation ra = new RotateAnimation(0, 360,Animation.RELATIVE_TO_SELF, 0.5F,Animation.RELATIVE_TO_SELF, 0.5F);
ra.setDuration(3000);
iv.startAnimation(ra);

TranslateAnimation

ImageView iv = findViewById(R.id.iv);
TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
ta.setDuration(3000);
iv.startAnimation(ta);

如上设置从(0, 0)平移到(200, 300)

在这里插入图片描述

ScaleAnimation

ImageView iv = findViewById(R.id.iv);
ScaleAnimation sa = new ScaleAnimation(0, 2, 0, 2);
sa.setDuration(3000);
iv.startAnimation(sa);

如上设置从0开始放大2倍

在这里插入图片描述

AnimationSet

用于混合动画

ImageView iv = findViewById(R.id.iv);
AnimationSet as = new AnimationSet(true);
as.setDuration(3000);
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(3000);
as.addAnimation(aa);TranslateAnimation ta = new TranslateAnimation(0, 200, 0, 300);
ta.setDuration(3000);
as.addAnimation(ta);
iv.startAnimation(as);

如上边平移边改变透明度

在这里插入图片描述

动画监听

对于上面每一个动画,都可获取其开始、结束、重复事件

AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {}@Overridepublic void onAnimationRepeat(Animation animation) {}
});

属性动画

属性动画解决了视图动画的弊端,在发生动画后,其响应事件会跟随移动

ImageView iv = findViewById(R.id.iv);
ObjectAnimator animator = ObjectAnimator.ofFloat(iv, "translationX", 300);
animator.setDuration(3000);
animator.start();

如上实现平移,第二个参数为操作的属性,其必须有get()/set()方法(利用反射调用),可选的属性有

  • translationX / translationY:相对于父布局左上角偏移的位置
  • rotation / rotationX / rotationY:围绕某点2D或3D旋转
  • scaleX / scaleY:围绕某点缩放
  • pivotX / pivotY:围绕某点旋转和缩放,默认为View中点
  • x / y:最终位置
  • alpha:透明度,默认为1(不透明)

若一个属性无get()/set()方法,则可自定义属性类或包装类,间接增加get()/set()方法

private static class 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();}
}

如上包装了View的width,然后可利用ObjectAnimator调用

ImageView iv = findViewById(R.id.iv);
WrapperView wrapperView = new WrapperView(iv);
ObjectAnimator.ofInt(wrapperView, "width", 500).setDuration(3000).start();

PropertyValuesHolder

用于实现混合动画效果,如下实现平移过程中先缩小后放大

ImageView iv = findViewById(R.id.iv);
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(iv, pvh1, pvh2, pvh3).setDuration(3000).start();

AnimatorSet

也是用于实现混合动画效果,能实现比PropertyValuesHolder更为精细的顺序控制,有before()、after()等方法

ImageView iv = findViewById(R.id.iv);
ObjectAnimator animator1 = ObjectAnimator.ofFloat(iv, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0f, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(iv, "scaleY", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
set.setDuration(3000);
set.playTogether(animator1, animator2, animator3);
set.start();

ValueAnimator

用于产生具有一定规律的数字,用于表示动画的过程,以实现动画的变换

ImageView iv = findViewById(R.id.iv);
ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
animator.setTarget(iv);
animator.setDuration(3000).start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {Float animatedValue = (Float) animation.getAnimatedValue();}
});

动画监听

利用AnimatorListener可监听动画Start、Repeat、End、Cancel过程

ImageView iv = findViewById(R.id.iv);
ObjectAnimator animator = ObjectAnimator.ofFloat(iv, "alpha", 0.5f);
animator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}
});

若只需监听其中一个,可利用AnimatorListenerAdapter

ImageView iv = findViewById(R.id.iv);
ObjectAnimator animator = ObjectAnimator.ofFloat(iv, "alpha", 0.5f);
animator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);}
});

XML中使用

res下新建animator文件夹,新建scalex.xml放置在此处




要引用xml则通过

ImageView iv = findViewById(R.id.iv);
Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scalex);
animator.setTarget(iv);
animator.start();

animate()

可直接使用view中的animate()实现属性动画

ImageView iv = findViewById(R.id.iv);
iv.animate().alpha(0).y(300).setDuration(3000).withStartAction(new Runnable() {@Overridepublic void run() {}}).withEndAction(new Runnable() {@Overridepublic void run() {runOnUiThread(new Runnable() {@Overridepublic void run() {}});}}).start();

布局动画

布局动画作用在ViewGroup上,用于ViewGroup增加View时添加一个动画过渡效果



如上设置animateLayoutChanges="true"打开布局动画

LinearLayout ll = findViewById(R.id.root);
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
sa.setDuration(3000);
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
ll.setLayoutAnimation(lac);

如上通过LayoutAnimationController控制子View的过渡效果,第二个参数为子View显示的时间,不为0时可设置子View显示的顺序

  • LayoutAnimationController.ORDER_NORMAL——顺序
  • LayoutAnimationController.ORDER_RANDOM——随机
  • LayoutAnimationController.ORDER_REVERSE——反序

Interpolator

Interpolators用于定义动画变换速率,类似于物理中的加速度,如对于平移动画,使用不同的Interpolators:

  • LinearInterpolator:单位时间所移动的距离都是一样的
  • AccelerateInterpolator:单位时间所移动的距离将越来越大
  • 上面两者速度不同,但最终平移距离是一样的

自定义动画

自定义动画需继承Animation并重写applyTransformation()

  • 第一个参数为Interpolator的时间因子,由动画当前完成的百分比和当前时间所对应的插值所计算得来,范围为0-1.0
  • 第二个参数为矩阵的封装类,用于获取矩阵

如下缩小图片的纵向,模拟电视机关闭动画

public class MyAnimation extends Animation {private int mCenterWidth;private int mCenterHeight;@Overridepublic void initialize(int width, int height, int parentWidth, int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);mCenterWidth = width / 2;mCenterHeight = height / 2;}@Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {Matrix matrix = t.getMatrix();matrix.preScale(1, 1 - interpolatedTime, mCenterWidth, mCenterHeight);}
}

使用方法和其他动画一样

ImageView iv = findViewById(R.id.iv);
MyAnimation myAnimation = new MyAnimation();
myAnimation.setDuration(3000);
iv.startAnimation(myAnimation);

在这里插入图片描述

SVG矢量动画

Scalable Vector Graphics为可伸缩矢量图形

  • www标准,用于网络的基于矢量的图形
  • 用XML定义
  • 放大或改变尺寸不会导致图形质量损失

path

用于设置指令控制画笔,有以下指令

  • M = moveto(M X,Y):画笔移动到(x, y),未绘制
  • L = lineto(L X,Y):画直线到(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 curvrto(T,ENDX,ENDY):映射前面路径的终点
  • A = elliptical Are(A RX,RY,XROTATION,FLAG1FLAG2,X,Y):弧线
  • Z = closepath():关闭路径

需注意

  • 指令大写为绝对定位(全局坐标系),小写为相对定位(父容器坐标系)
  • 指令和数据间的空格可以省略
  • 同一指令出现多次可以只用一个

vector

用于在XML创建SVG图形,如下在drawable文件夹新增vector.xml

  • width / height:实际大小
  • viewportWidth / viewportHeight:表示划分比例,将200dp分为100份
  • M 25 50 表示移动到(25, 50)坐标,即距离原点x=25 / 100 * 200 = 50dp,y=50 / 100 * 200=100dp的位置
  • A指令绘制椭圆,参数12:XY轴长度。参数3:X轴与水平顺时针方向的夹角。参数4:1大角度弧线 / 0小角度弧线。参数5:起点到终点的方向,1顺时针 / 0逆时针。参数67:XY轴终点坐标



通过 background / src 属性引用,效果如下
在这里插入图片描述
如果不需要填充,则可改为

android:strokeColor="@android:color/holo_blue_light"
android:strokeWidth="2"

在这里插入图片描述

animate-vector

用于给图形添加动画,先在animator文件夹新增anim_path1.xml

  • android:propertyName 引用vector.xml中group的属性
  • 若为属性为pathData,则需要添加android:valueType=“pathType”


然后在drawable文件夹新增vector_animate.xml

  • android:drawable 引用上面的vector.xml
  • android:name 需要与vector.xml的group名称一样
  • android:animation 引用上面anim_path1.xml



使用src属性引用vector_animate.xml



转为Animatable并开启动画

public class MainActivity extends AppCompatActivity {protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView iv = findViewById(R.id.iv);((Animatable) iv.getDrawable()).start();}
}

效果为旋转一圈

在这里插入图片描述

实例——线图动画

创建line.xml,绘制两条直线,path1先移动到(20,80),再绘制直线到(50,80)和(80,80),形成3个点(中点用于动画),path2同理




创建line_animator1.xml用于path1,让动画先移动到(20,80),再绘制直线到(50,50)和(80,80),即让中点变换



创建line_animator2.xml用于path2,同理



创建line_animate.xml合并动画




布局通过src属性引用



通过点击事件开启动画

public class MainActivity extends AppCompatActivity {private ImageView mIv;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mIv = findViewById(R.id.iv);mIv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {animate();}});}private void animate() {((Animatable) mIv.getDrawable()).start();}
}

效果为两平行的直线,中点合并为一个X

在这里插入图片描述

实例——模拟三球仪

创建sun_earth.xml,绘制静态的三个球,在(60,60)绘制半径为10的sun,在(75,55)绘制半径为5的earth,在(89,55)绘制半径为4的moon




创建sun_earth_animator.xml,为两个group设置旋转动画



创建sun_earth_animate.xml设置动画




使用方法同上,不再赘述,效果为三个球绕其旋转中心旋转
在这里插入图片描述

实例——轨迹动画

创建search.xml绘制搜索栏




创建search_animator.xml,设置android:propertyName=“trimPathStart”,其利用0-1百分比按照轨迹绘制SVG



创建search_animate.xml,设置动画



使用方法同上,不再赘述,效果为点击后搜索框会沿着绘制的轨迹消失

在这里插入图片描述

动画特效

灵动菜单

如下实现菜单的弹出、聚拢特效,先放置5个图片在同一位置



如下为每个图片设置动画(为什么设置相反的数值不能close回去,而是需要调用getScrollX)

public class MainActivity extends AppCompatActivity {private boolean mFlag = true;private ImageView iv0;private ImageView iv1;private ImageView iv2;private ImageView iv3;private ImageView iv4;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv0 = findViewById(R.id.iv0);iv1 = findViewById(R.id.iv1);iv2 = findViewById(R.id.iv2);iv3 = findViewById(R.id.iv3);iv4 = findViewById(R.id.iv4);iv0.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mFlag) {statAnim();} else {closeAnim();}}});}private void closeAnim() {ObjectAnimator animator0 = ObjectAnimator.ofFloat(iv0, "alpha", 0.5F, 1F);ObjectAnimator animator1 = ObjectAnimator.ofFloat(iv1, "translationY", -iv1.getScrollY());ObjectAnimator animator2 = ObjectAnimator.ofFloat(iv2, "translationX", -iv2.getScrollX());ObjectAnimator animator3 = ObjectAnimator.ofFloat(iv3, "translationY", iv3.getScrollY());ObjectAnimator animator4 = ObjectAnimator.ofFloat(iv4, "translationX", iv3.getScrollX());AnimatorSet set = new AnimatorSet();set.setInterpolator(new AccelerateDecelerateInterpolator());set.playTogether(animator0, animator1, animator2, animator3, animator4);set.start();mFlag = true;}private void statAnim() {ObjectAnimator animator0 = ObjectAnimator.ofFloat(iv0, "alpha", 1F, 0.5F);ObjectAnimator animator1 = ObjectAnimator.ofFloat(iv1, "translationY", 200F);ObjectAnimator animator2 = ObjectAnimator.ofFloat(iv2, "translationX", 200F);ObjectAnimator animator3 = ObjectAnimator.ofFloat(iv3, "translationY", -200F);ObjectAnimator animator4 = ObjectAnimator.ofFloat(iv4, "translationX", -200F);AnimatorSet set = new AnimatorSet();set.setInterpolator(new BounceInterpolator());set.playTogether(animator0, animator1, animator2, animator3, animator4);set.start();mFlag = false;}
}

效果如下,点击中间的图片让其他图片弹出或聚拢
在这里插入图片描述

计时器动画



public class MainActivity extends AppCompatActivity {protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView tv = findViewById(R.id.tv);tv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {tvTimer(v);}});}private void tvTimer(final View view) {ValueAnimator va = ValueAnimator.ofInt(0, 100);va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {((TextView) view).setText("$" + animation.getAnimatedValue());}});va.setDuration(3000);va.start();}
}

点击后,通过onAnimationUpdate()不断设置数值,将在3秒内从0增加到100,如下
在这里插入图片描述

下拉展开动画

设置两个LinearLayout,一个常显,一个隐藏




通过ValueAnimator的onAnimationUpdate()方法逐渐增加或减少View的高度,关闭时通过onAnimationEnd()在动画结束时隐藏View

public class MainActivity extends AppCompatActivity {private LinearLayout mHiddenView;private float mDensity;private int mHiddenViewMeasuredHeight;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);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) {animOpen(mHiddenView);} else {animClose(mHiddenView);}}private void animOpen(final View view) {view.setVisibility(View.VISIBLE);ValueAnimator va = createDropAnim(view, 0, mHiddenViewMeasuredHeight);va.start();}private void animClose(View view) {ValueAnimator va = createDropAnim(view, mHiddenViewMeasuredHeight, 0);va.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {view.setVisibility(View.GONE);}});va.start();}private ValueAnimator createDropAnim(final View view, int start, int end) {ValueAnimator va = ValueAnimator.ofInt(start, end);va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {int value = (int) animation.getAnimatedValue();ViewGroup.LayoutParams layoutParams = view.getLayoutParams();layoutParams.height = value;view.setLayoutParams(layoutParams);}});return va;}
}

效果如下

在这里插入图片描述

相关内容

热门资讯

求经典台词和经典旁白 求经典台词和经典旁白谁有霹雳布袋戏里的经典对白和经典旁白啊?朋友,你尝过失去的滋味吗? 很多人在即将...
小王子第二章主要内容概括 小王子第二章主要内容概括小王子第二章主要内容概括小王子第二章主要内容概括
爱情睡醒了第15集里刘小贝和项... 爱情睡醒了第15集里刘小贝和项天骐跳舞时唱的那首歌是什么谢谢开始找舞伴的时候是林俊杰的《背对背拥抱》...
世界是什么?世界是什么概念?可... 世界是什么?世界是什么概念?可以干什么?物质的和意识的 除了我们生活的地方 比方说山 河 公路 ...
全职猎人中小杰和奇牙拿一集被抓 全职猎人中小杰和奇牙拿一集被抓动画片是第五十九集,五十八集被发现,五十九被带回基地,六十逃走
“不周山”意思是什么 “不周山”意思是什么快快快快......一座山,神话里被共工撞倒了。
《揭秘》一元一分15张跑得快群... 一元一分麻将群加群主微【ab120590】【tj525555】 【mj120590】等风也等你。喜欢...
玩家必看手机正规红中麻将群@2... 好运连连,全网推荐:(ab120590)(mj120590)【tj525555】-Q号:(QQ443...
始作俑者15张跑的快群@24小... 微信一元麻将群群主微【ab120590】 【tj525555】【mj120590】一元一分群内结算,...
《重大通知》24小时一元红中麻... 加V【ab120590】【tj525555】【mj120590】红中癞子、跑得快,等等,加不上微信就...
盘点一下正规一块红中麻将群@2... 一元一分麻将群加群主微:微【ab120590】 【mj120590】【tj525555】喜欢手机上打...
(免押金)上下分一元一分麻将群... 微【ab120590】 【mj120590】【tj525555】专业麻将群三年房费全网最低,APP苹...
[解读]正规红中麻将跑的快@群... 微信一元麻将群群主微【ab120590】 【tj525555】【mj120590】一元一分群内结算,...
《普及一下》全天24小时红中... 微【ab120590】 【mj120590】【tj525555】专业麻将群三年房费全网最低,APP苹...
优酷视频一元一分正规红中麻将... 好运连连,全网推荐:(ab120590)(mj120590)【tj525555】-Q号:(QQ443...
《火爆》加入附近红中麻将群@(... 群主微【ab120590】 【mj120590】【tj525555】免带押进群,群内跑包包赔支持验证...
《字节跳动》哪里有一元一分红中... 1.进群方式-[ab120590]或者《mj120590》【tj525555】--QQ(QQ4434...
全网普及红中癞子麻将群@202... 好运连连,全网推荐:(ab120590)(mj120590)【tj525555】-Q号:(QQ443...
「独家解读」一元一分麻将群哪里... 1.进群方式《ab120590》或者《mj120590》《tj525555》--QQ(4434063...
通知24小时不熄火跑的快群@2... 1.进群方式《ab120590》或者《mj120590》《tj525555》--QQ(4434063...