属性动画是3.0推出的新特性,和View动画不同,他对对象进行了扩展,属性动画可以对任何对象做动画.在Animator框架中使用最多的就是AnimatorSet和ObjectAnimator配合,使用ObjectAnimator进行更精细化控制,只控制一个对象的属性值,使用多个ObjectAnimator组合到AnimatorSet中形成一个动画.
在这里说明,动画默认的时间间隔是300ms,默认帧率10ms/帧
下面对属性动画做一一的介绍:
ObjectAnimator
ObjectAnimator animator=ObjectAnimator.ofFloat(
//需要操作的View
textView,
//要操作的属性
"translationX",
//从200点坐标移动到300点坐标
200,300);
animator.setDuration(300);
animator.start();
- rotation rotationX rotationY
三个属性控制View对象围绕支点进行2D和3D的旋转. - translationX和translationY
控制着View对象从它布局容器的左上角坐标偏移量的位置 - scaleX和scaleY.
控制着View对象围绕他的支点进行2D缩放 (1.1.5F)由1倍放大至1.5倍 - alphy
表示View对象的alpha透明度.默认是1不透明,0代表完透明
或者通过XML文件来展示属性动画,建议还是通过代码,比如说在不知道屏幕宽度的时候我们没法执行一些效果
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially"> //表示动画组合是按顺序执行还是一起执行
<objectAnimator
android:propertyName="x" //属性名称
android:duration="3000" //执行时间
android:valueFrom="300" //属性的起始值
android:valueTo="0" //属性的结束值
android:startOffset="0" //表示动画的延迟时间
android:repeatCount="3" //表示动画的重复次数
android:repeatMode="reverse" //表示动画的重复模式 reverse表示逆向重复,restart表示连续重复
android:valueType="floatType" //表示属性的类型
/>
</set>
然后通过JAVA代码执行
AnimatorSet set=
(AnimatorSet) AnimatorInflater.loadAnimator(MainActivity.this,R.animator.zc);
set.setTarget(textView);
set.start();
PropertyValuesHolder
类似视图动画中的AnimationSet,在属性动画中,如果针对一个对象多个属性同时作用多种动画,可以使用PropertyValuesHolder来实现.
PropertyValuesHolder pvh1=PropertyValuesHolder.ofFloat("translationX",300f);
PropertyValuesHolder pvh2=PropertyValuesHolder.ofFloat("scaleX",1f,0.5f);
PropertyValuesHolder pvh3=PropertyValuesHolder.ofFloat("scaleY",1f,0.5f);
PropertyValuesHolder pvh4=PropertyValuesHolder.ofFloat("alphy",1,0,1);
ObjectAnimator.ofPropertyValuesHolder(textView,pvh1,pvh2,pvh3,pvh4)
.setDuration(3000).start();
ValueAnimatior
本身不提供效果,更像是一个数值发生器,用来产生具有一定规律的数字 .
ValueAnimator animator=ValueAnimator.ofInt(0,100);
animator.addUpdateListener(
new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
textView.setText((Integer) animation.getAnimatedValue()+"");
}
});
animator.setTarget(textView);
animator.setDuration(3000);
animator.start();
动画事件的监听
一个完整的动画具有Start Repeat End Cancel四个过程.通过Android提供了接口可以方便的监听这四个实践
ObjectAnimator animator=ObjectAnimator.ofFloat(textView,"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) {
}
});
AnimatiorSet
AnimatiorSet不仅能够实现PropertyValuesHolder的效果,同时也能更为精确的控制顺序
ObjectAnimator animator1 =
ObjectAnimator.ofFloat(textView,"translationX", 300f);
ObjectAnimator animator2 =
ObjectAnimator.ofFloat(textView,"scaleX", 1f, 0.5f);
ObjectAnimator animator3 =
ObjectAnimator.ofFloat(textView,"scaleY", 1f, 0.5f);
ObjectAnimator animator4 =
ObjectAnimator.ofFloat(textView,"alphy", 1, 0, 1);
AnimatorSet set=new AnimatorSet();
set.setDuration(1000);
set.playSequentially(animator1,animator2,animator3,animator4);
set.start();
设置如何播放动画由以下几种:
- playTogether 动画一起执行
- playSequentially 按设置的动画一个一个的执行
- -
包装原始对象,提供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();
}
}
WrapperView wrapperView=new WrapperView(v);
ObjectAnimator.ofInt(wrapperView,"width", 500).setDuration(8000).start();
View的animate方法
属性动画的一种简写方式
textView.animate()
.alpha(0)
.y(300)
.setDuration(3000)
.withStartAction(new Runnable() {
@Override
public void run() {
}
})
.withEndAction(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
}).start();
属性动画中的标签对应
<set>----AnimatorSet
<animator>----ValueAnimator
<objectAnimator>----ObjectAnimator
属性动画实例:
效果图:
代码如下所示:
布局代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="com.zc.myapplication.MainActivity"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_blue_bright"
android:gravity="center_vertical"
android:onClick="llClick"
android:orientation="horizontal">
<ImageView
android:id="@+id/app_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:gravity="left"
android:text="Click me"
android:textSize="30sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/hidden_view"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@android:color/holo_orange_light"
android:orientation="horizontal"
android:visibility="gone"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tv_hidden"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="I Am Hidden"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
JAVA代码:
public class MainActivity extends AppCompatActivity {
private LinearLayout mHiddenView;
private float mDensity;
private int mHiddenViewMeasuredHeigth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHiddenView= (LinearLayout) findViewById(R.id.hidden_view);
//获取像素密度
mDensity=getResources().getDisplayMetrics().density;
//获取布局的高度 这里的40就是View设置的40dp
mHiddenViewMeasuredHeigth= (int) (mDensity*40+0.5);
}
public void llClick(View view) {
if (mHiddenView.getVisibility()==View.GONE){
//打开动画
animateOpen(mHiddenView);
}else {
//关闭动画
animateClose(mHiddenView);
}
}
private void animateOpen(LinearLayout mHiddenView) {
mHiddenView.setVisibility(View.VISIBLE);
ValueAnimator animator=createDropAnimator(
mHiddenView
,0
,mHiddenViewMeasuredHeigth);
animator.start();
}
private void animateClose(final LinearLayout mHiddenView) {
int origHeigth=mHiddenView.getHeight();
ValueAnimator animator=createDropAnimator(
mHiddenView
,origHeigth
,0);
animator.start();
//监听动画的结束,结束以后将View设置为GONE
animator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
mHiddenView.setVisibility(View.GONE);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
}
private ValueAnimator createDropAnimator(final LinearLayout mHiddenView, int start, int end) {
ValueAnimator animator=ValueAnimator.ofInt(start,end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value= (int) animation.getAnimatedValue();
ViewGroup.LayoutParams layoutParams=
mHiddenView.getLayoutParams();
layoutParams.height=value;
mHiddenView.setLayoutParams(layoutParams);
}
});
return animator;
}
}
在这里说明一下为什么要设置一个动画的监听,因为如果不设置的话那么将隐藏的View显示以后,再执行点击事件将View隐藏,在点击以后View不会再显示出来,因为
public void llClick(View view) {
if (mHiddenView.getVisibility()==View.GONE){
//打开动画
animateOpen(mHiddenView);
}else {
//关闭动画
animateClose(mHiddenView);
}
}
只有当判断View为GONE的时候才会隐藏,我们只是把View给显示高度为0了 但是并没有隐藏.