Android学习之属性动画基础
来源:互联网 发布:澳洲排油丸 知乎 编辑:程序博客网 时间:2024/06/05 08:29
Property Animation是一个很强劲的动画框架,几乎可以为所有的事物加上动画效果。
相关API
Duration:动画的持续时间,默认300ms
Time interpolation:时间差值,定义动画的变化率
Repeat count and behavior:重复次数,以及重复模式;可以定义重复多少次,重复时是从头开始还是反向
Animator set:动画集合,可以定义一组动画,一起执行或者顺序执行
Frame refresh delay:帧刷新延迟,多久刷新一次帧,默认为10ms。但最终依赖系统的当前状态,基本不用管
objectAnimator:动画的执行类
ValueAnimator:动画的执行类
AnimatorSet:用于控制一组动画的执行:线性,一起,每个动画的先后执行等
AnimatorInflater:用于加载属性动画的xml文件
TypeEvalutor:类型估值,主要用于设置动画操作属性的值
TimeInterpolator:时间插值
总得来说,属性动画就是,动画的执行类来设置动画操作的对象的属性,持续时间,开始和结束的属性值,时间差值等,然后系统会根据设置的参数动态的变化对象的属性
1,ObjectAnimator实现动画
先看一个最简单的
public void startAnimation(View view){ ObjectAnimator .ofFloat(view,"rotationX",0.0f,360.0f) .setDuration(1000) .start(); }
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.asus1.testpropertyanimation.MainActivity"> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:id="@+id/iv_image" android:layout_centerInParent="true" android:src="@drawable/bg" android:onClick="startAnimation" /></RelativeLayout>
对于ObjectAnimator
1,提供了ofInt,ofFloat,ofObject,这几个方法都是设置动画动作的元素,作用的属性,动画开始,结束,以及中间的任意个属性。
当对于属性值,只设置一个的时候,会认为当前对象该属性值的开始 (getPropName反射获取),如果设置两个,则一个为开始,一个为结束
2,ObjectAnimator让我们动画的过程变简单,当我们不需要去实现ValueAnimator.AnimatorUpdateListener,因为该对象的动画属性会自动更新
3,ObjectAnimator会自动更新动画,因此必须有一个setter方法,去访问这个属性
关于这个setter方法,我们来看看
public void ViewSetter(View view){ ViewWrap wrap = new ViewWrap(mBall); ObjectAnimator.ofFloat(wrap,"width",500) .setDuration(5000) .start(); } public class ViewWrap{ private View mTarget; public ViewWrap(View view){ mTarget = view; } public int getWdith(){ return mTarget.getLayoutParams().width; } public void setWidth(float width){ mTarget.getLayoutParams().width = (int) width; mTarget.requestLayout(); } }
如果我们用渐变动画来:
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f,3.0f,1.0f,1.0f, Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f); scaleAnimation.setDuration(5000); mBall.startAnimation(scaleAnimation);
我们可以看到,它的里面的内容也在发生改变,而且还超出了屏幕范围
这样就不好看
注意一个地方,我们如果直接对Button的width属性做动画是没有效果的,虽然Button内部提供了getWidth和setWidth方法,但是这个setWidth并不是改变视图得出大小,它是TextView新增的方法,View是没有这个setWidth方法的,由于Button是继承了TextView,所以Button也就有了setWidth方法
我们看看源码:
/** * Makes the TextView exactly this many pixels wide. * You could do the same thing by specifying this number in the * LayoutParams. * * @see #setMaxWidth(int) * @see #setMinWidth(int) * @see #getMinWidth() * @see #getMaxWidth() * * @attr ref android.R.styleable#TextView_width */ @android.view.RemotableViewMethod public void setWidth(int pixels) { mMaxWidth = mMinWidth = pixels; mMaxWidthMode = mMinWidthMode = PIXELS; requestLayout(); invalidate(); }
我们看到它是用来设置TextView的最小宽度和最大宽度的,这个和TextView的宽度不是一个东西
具体来说,TextView的宽度对应Xml文件中的layout_width属性,而android:width属性就是对应setWdith方法的
在官方文档中,对于ObjectAnimator有这种解决方式:
- 给你的对象加上get和set方法,如果你有权限的话
- 用一个类来包装原始对象,间接为其提供get和set方法
- 采用ValueAnimator,监听动画过程,自己实现属性的改变
如果我们想要实现多个动画效果用ObejctAnimator
我们可以这样
ObjectAnimator animator = ObjectAnimator .ofFloat(view,"foo",0.0f,1.0f) .setDuration(2000) ; animator.start(); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float value = (float)valueAnimator.getAnimatedValue(); view.setAlpha(value); view.setScaleX(value); } });
关于设置属性的那个字符串,可以随便写一个,也就是不管,我们只需要按照时间插值和持续时间计算的那个值,自己手动调用
4,我们还可以使用propertyValuesHolder
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha",0.0f,1.0f); PropertyValuesHolder scale = PropertyValuesHolder.ofFloat("scaleX",0.0f,1.0f); PropertyValuesHolder rotate = PropertyValuesHolder.ofFloat("rotationX",0.0f,360.0f); ObjectAnimator.ofPropertyValuesHolder(view,alpha,scale,rotate).setDuration(2000) .start();
效果:
ValueAnimator
其实ObjectAnimator是ValueAnimator的子类,所以两个的用法很相似
public void runVertical(final View view){ ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.0f,mLaout.getHeight()-mBall.getHeight()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { float verticalValue = (float)valueAnimator.getAnimatedValue(); view.setTranslationY(verticalValue); } }); valueAnimator.setDuration(3000); valueAnimator.setInterpolator(new AccelerateInterpolator()); valueAnimator.start(); } public void paowuxian(View view){ ValueAnimator valueAnimator = new ValueAnimator(); valueAnimator.setDuration(5000); valueAnimator.setTarget(mBall2); valueAnimator.setInterpolator(new AccelerateInterpolator()); valueAnimator.setObjectValues(new PointF(0,0)); valueAnimator.setEvaluator(new TypeEvaluator() { @Override public Object evaluate(float v, Object o, Object t1) { PointF pointF = new PointF(); pointF.set(200*v*3,(float)(0.5*200*v*v*9)); return pointF; } }); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { PointF pointF = (PointF)valueAnimator.getAnimatedValue(); mBall2.setX(pointF.x); mBall2.setY(pointF.y); } }); valueAnimator.start(); }
我们可以看到,ValueAnimation是没有指定属性的,因此不需要操作的对象的属性一定有getter和setter方法,我们可以根据当前动画的计算值,来做操作
在做抛物线运动的时候,因为需要两个值来确定位置,x,y。所以我们用了TypeEvaluator,实现它的evluate方法,通过这个方法,我们可以返回当前动画点的自己需要的动画值
监听动画的事件
对于动画,一般都是一些辅助效果,比如要删除个元素,希望一个淡出的效果,但是最终还是删掉,并不是透明度没有了,还占着位置,所以我们需要知道动画如何结束
public void fadeOut(View view) { ObjectAnimator anim = ObjectAnimator.ofFloat(mBlueBall, "alpha", 0.5f); anim.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animation) { Log.e(TAG, "onAnimationStart"); } @Override public void onAnimationRepeat(Animator animation) { // TODO Auto-generated method stub Log.e(TAG, "onAnimationRepeat"); } @Override public void onAnimationEnd(Animator animation) { Log.e(TAG, "onAnimationEnd"); ViewGroup parent = (ViewGroup) mBlueBall.getParent(); if (parent != null) parent.removeView(mBlueBall); } @Override public void onAnimationCancel(Animator animation) { // TODO Auto-generated method stub Log.e(TAG, "onAnimationCancel"); } }); anim.start(); }
这样就可以监听动画的开始、结束、被取消、重复等事件~但是有时候会觉得,我只要知道结束就行了,这么长的代码我不能接收,那你可以使用
anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { Log.e(TAG, "onAnimationEnd"); ViewGroup parent = (ViewGroup) mBlueBall.getParent(); if (parent != null) parent.removeView(mBlueBall); } });
AnimatorListenerAdapter继承了AnimatorListener接口,然后空实现了所有的方法~
animator还有cancel()和end()方法:cancel动画立即停止,停在当前的位置,end动画直接到最终状态
AnimatorSet使用
首先是几个动画组合在一起
public void playTogether(View view){ AnimatorSet set = new AnimatorSet(); ObjectAnimator alpha = ObjectAnimator.ofFloat(view,"alpha",1.0f,0.0f); ObjectAnimator scaleX = ObjectAnimator.ofFloat(view,"scaleX",1.0f,0.0f); ObjectAnimator scaleY = ObjectAnimator.ofFloat(view,"scaleY",1.0f,0.0f); ObjectAnimator translation = ObjectAnimator.ofFloat(view,"translationX",0,mLaout.getWidth()/3*2); set.playTogether(alpha,scaleX,scaleY,translation); set.setDuration(5000); set.start(); }
然后我们来试试先后动画
public void playAfter(View view){ AnimatorSet set = new AnimatorSet(); ObjectAnimator alpha = ObjectAnimator.ofFloat(view,"alpha",1.0f,0.3f); ObjectAnimator scaleX = ObjectAnimator.ofFloat(view,"scaleX",1.0f,0.3f); ObjectAnimator scaleY = ObjectAnimator.ofFloat(view,"scaleY",1.0f,0.3f); ObjectAnimator translation = ObjectAnimator.ofFloat(view,"translationX",0,mLaout.getWidth()/3); ObjectAnimator alphaAfter = ObjectAnimator.ofFloat(view,"alpha",0.3f,1.0f); ObjectAnimator scaleXAfter = ObjectAnimator.ofFloat(view,"scaleX",0.3f,1.5f); ObjectAnimator scaleYAfter = ObjectAnimator.ofFloat(view,"scaleY",0.3f,1.5f); set.setDuration(5000); set.play(alpha).with(scaleX).with(scaleY).with(translation); set.play(alphaAfter).with(scaleXAfter).with(scaleYAfter).after(translation); set.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" android:valueTo="0.5" > </objectAnimator> <objectAnimator android:duration="1000" android:propertyName="scaleY" android:valueFrom="1" android:valueTo="0.5" > </objectAnimator> </set>
然后在方法中加载这个动画
public void scaleX(View view){ Animator animator = AnimatorInflater.loadAnimator(this,R.animator.scalex); animator.setTarget(view); view.setPivotX(view.getWidth()/2); view.setPivotY(view.getHeight()/2); animator.start(); }
使用set标签,有一个orderring属性设置为together(还有一个值,sequentially 表示一个接一个)
布局动画
主要使用LayoutTransition为布局的容器设置动画,当容器中的视图层次发生变化时存在过渡的动画效果
过渡的类型:
LayoutTransition.APPEARING :当一个view在ViewGroup中出现时,对此View设置的动画
LayoutTransition.CHANGR_APPEARING:当一个view在ViewGroup中出现时,对此View对其他View位置造成影响,对其他View设置的动画
LayoutTransition.DISAPPEARING:当一个view在ViewGroup中消失的时候。对此view设置的动画
LayoutTransition.CHANGE.DISAPPEARING:当一个View在ViewGroup中消失时,对此View对其他View位置造成影响,对其他View设置的动画
LayoutTransition.CHANGE:不是由于VIew出现或者消失造成对其他View位置造成影响,然后对其他View设置的动画
@Override public void onClick(View view) { final Button button = new Button(this); button.setText(String.valueOf(mValue++)); mGridView.addView(button,Math.min(1,mGridView.getChildCount())); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mGridView.removeView(button); } }); } @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { mLayoutTransition = new LayoutTransition(); mLayoutTransition. setAnimator(LayoutTransition.APPEARING, (mAppear.isChecked()? mLayoutTransition.getAnimator(LayoutTransition.APPEARING): null)); mLayoutTransition. setAnimator(LayoutTransition.CHANGE_APPEARING, (mAppear.isChecked()? mLayoutTransition.getAnimator(LayoutTransition.CHANGE_APPEARING): null)); mLayoutTransition. setAnimator(LayoutTransition.DISAPPEARING, (mAppear.isChecked()? mLayoutTransition.getAnimator(LayoutTransition.DISAPPEARING): null)); mLayoutTransition. setAnimator(LayoutTransition.CHANGE_DISAPPEARING, (mAppear.isChecked()? mLayoutTransition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING): null)); mGridView.setLayoutTransition(mLayoutTransition); }
这个表现的不是很清楚,我们可以自己定义这个动画
mLayoutTransition. setAnimator(LayoutTransition.APPEARING, (mAppear.isChecked()? ObjectAnimator.ofFloat(button,"scaleX",0.0f,1.0f) : null));
原本的淡入淡出,变成了宽度从中间开始放大
View的anim方法
我们继续模拟下落的过程
先看代码:
public void ViewAnim(final View view){ mBall.animate() .y(mLaout.getHeight()) .alpha(0.3f) .rotationBy(0) .rotationBy(360) .setDuration(3000) .withEndAction(new Runnable() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { mBall.setY(0); mBall.setAlpha(1.0f); } }); } }) .start(); }
看效果:
啊啊啊啊GIF图好难录,大概就是这个效果,它是从顶开始的
感觉animate使得动画变得更加简单
补充:
AccelerateDecelerateInterolator 先加速后减速,开始结束时慢,中间加速
AccelerateInterpolator 加速,开始时慢中间加速
DecelerateInterpolator 减速,开始时快然后减速
AnticipateInterpolator 反向 ,先向相反方向改变一段再加速播放
AnticipateOvershootInterpolator 反向加超越,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
CycleIinterpolator 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2 mCycles Math.PI * input)
LinearInterpolator 线性,线性均匀改变
OvershottInterpolator 超越,最后超出目的值然后缓慢改变到目的值
TimeInterpolator 一个接口,允许你自定义interpolator,以上几个都是实现了这个接口
代码练习的链接
https://github.com/vivianluomin/PracticeEveryDay/tree/master/TestPropertyAnimation
- Android学习之属性动画基础
- Android动画之属性动画基础用法
- android动画之属性动画学习
- android动画学习(二)之属性动画
- 【Android - 基础】之Animator属性动画
- Android动画--属性动画--基础
- android属性动画--基础
- Android属性动画基础
- android属性动画基础
- android基础学习之动画基础
- Android学习之 属性动画<Property Animation>
- Android动画学习(四)之属性动画学习
- 【Android学习笔记】属性动画基础学习笔记
- Android 动画之属性动画
- Android动画之属性动画
- Android动画之属性动画
- android 动画 之 属性动画
- Android动画之---属性动画
- CentOS 7 安装 RabbitMQ
- PAT乙级题1019.数字黑洞
- 线程同步与线程调度初探_发牌功能
- Python学习笔记——条件判断与循环
- highgui的拓展
- Android学习之属性动画基础
- Python Imaging Library: ImageQt Module(图像QT模块)
- C++ Primer Plus 课后编程练习——第六章 分支语句和逻辑运算符
- java io stream
- Reverse Integer算法题
- Threes
- Latex所有常用数学符号整理
- Delete Node in a Linked List
- logstash-input-jdbc实现ElasticSearch与mysql同步