- 概述
Android框架提供了两种动画系统:属性动画以及视图动画。这两种动画系统都有变化的选择,但是总的来说,属性动画系统是更好的选择,因为它更加灵活,并提供了更多的特性。
属性动画系统是一个强大的框架,它允许你动画几乎所有的东西,例如一个对象在屏幕中的位置,要动画多久,和动画之间的距值。
ObjectAnimator
缩放效果
public void click(View v){
// ObjectAnimator.ofFloat(v,"rotationX",0f,360f).setDuration(500).start();
//组合多个动画
PropertyValuesHolder p1=PropertyValuesHolder.ofFloat("alpha",1f,0f,1f);
PropertyValuesHolder p2=PropertyValuesHolder.ofFloat("scaleX",1f,0f,1f);
PropertyValuesHolder p3=PropertyValuesHolder.ofFloat("scaleY",1f,0f,1f);
ObjectAnimator.ofPropertyValuesHolder(v,p1,p2,p3).setDuration(3000).start();
}
效果展示
ValueAnimator
掉落效果
public void downClick(View v){
final View vi=v;
DisplayMetrics dm=new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
//定义一个动画
ValueAnimator va= ValueAnimator.ofFloat(v.getY(),dm.heightPixels,v.getY()).setDuration(500);
//监听动画的每个动作
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
vi.setTranslationY((Float) animation.getAnimatedValue());
}
});
va.start();
}
动画事件的监听
public void click(View v){
final View vi=v;
//监听动画事件
ObjectAnimator oa= ObjectAnimator.ofFloat(v,"alpha",1f,0f).setDuration(1000);
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
//取消动画
}
@Override
public void onAnimationEnd(Animator animation) {
//动画结束
ViewGroup viewGroup= (ViewGroup) vi.getParent();
if (viewGroup!=null){
viewGroup.removeView(vi);
System.out.println("removed.");
}
}
@Override
public void onAnimationRepeat(Animator animation) {
//重复
}
@Override
public void onAnimationStart(Animator animation) {
//动画开始
}
});
oa.start();
}
AnimatorSet
ObjectAnimator a1=ObjectAnimator.ofFloat(v,"translationX",0f,200f);
ObjectAnimator a2=ObjectAnimator.ofFloat(v,"translationY",0f,200f);
ObjectAnimator a3=ObjectAnimator.ofFloat(v,"rotation",0f,360f);
AnimatorSet set=new AnimatorSet();
set.setDuration(1000);
// set.playTogether(a1,a2,a3);//三个动画同时执行
// set.playSequentially(a1,a2,a3);//按序列执行
// set.setStartDelay(300);//延迟执行
//a1,a2同时执行,之后执行a3
set.play(a1).with(a2);
set.play(a3).after(a2);
set.start();
注意:animSet.play().with();也是支持链式编程的,但是:
animSet.play(anim1).with(anim2).before(anim3).before(anim4);
这样是不行的,系统不会根据你写的这一长串来决定先后的顺序。
插值器
// set.setInterpolator(new BounceInterpolator());//反弹效果
// set.setInterpolator(new AccelerateDecelerateInterpolator());//插补,其变化率慢慢开始和结束,但通过中间加速。
// set.setInterpolator(new AccelerateInterpolator());//插补,其变化率开始缓慢,然后加快
// set.setInterpolator(new AnticipateInterpolator());//内插的变化开始落后,然后向前甩
// set.setInterpolator(new AnticipateOvershootInterpolator());//内插的变化,开始落后,甩向前过冲目标值,然后终于可以追溯到最终值。
// set.setInterpolator(new CycleInterpolator(2));//内插动画重复指定的周期数
// set.setInterpolator(new DecelerateInterpolator());//插补,其变化的速率开始很快,然后减速
// set.setInterpolator(new LinearInterpolator());//插补,其变化率是恒定的
// set.setInterpolator(new OvershootInterpolator());//内插的变化甩向前和过冲的最后一个值,然后回来
// 一个接口,实现自定义的插补
// set.setInterpolator(new TimeInterpolator() {
// @Override
// public float getInterpolation(float input) {
// return 0;
// }
// });
动画菜单的实现
activity_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".MenuActivity">
<ImageView
android:id="@+id/imageView8"
android:layout_width="40dp"
android:layout_height="40dp"
android:tag="imageView8"
android:layout_gravity="left|top"
app:srcCompat="@mipmap/a8" />
<ImageView
android:id="@+id/imageView7"
android:layout_width="40dp"
android:tag="imageView7"
android:layout_gravity="left|top"
android:layout_height="40dp"
app:srcCompat="@mipmap/a7" />
<ImageView
android:id="@+id/imageView6"
android:layout_width="40dp"
android:layout_gravity="left|top"
android:tag="imageView6"
android:layout_height="40dp"
app:srcCompat="@mipmap/a6" />
<ImageView
android:id="@+id/imageView5"
android:layout_width="40dp"
android:tag="imageView5"
android:layout_height="40dp"
android:layout_gravity="left|top"
app:srcCompat="@mipmap/a5" />
<ImageView
android:id="@+id/imageView4"
android:layout_width="40dp"
android:tag="imageView4"
android:layout_gravity="left|top"
android:layout_height="40dp"
app:srcCompat="@mipmap/a4" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="40dp"
android:layout_gravity="left|top"
android:layout_height="40dp"
android:tag="imageView3"
app:srcCompat="@mipmap/a3" />
<ImageView
android:id="@+id/imageView2"
android:layout_gravity="left|top"
android:layout_width="40dp"
android:layout_height="40dp"
android:tag="imageView2"
app:srcCompat="@mipmap/a2" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="40dp"
android:layout_gravity="left|top"
android:layout_height="40dp"
app:srcCompat="@mipmap/a1" />
</FrameLayout>
MenuActivity
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.BounceInterpolator;
import android.widget.ImageView;
import android.widget.Toast;
import java.util.ArrayList;
public class MenuActivity extends AppCompatActivity implements View.OnClickListener {
private int[] res={
R.id.imageView1,
R.id.imageView2,
R.id.imageView3,
R.id.imageView4,
R.id.imageView5,
R.id.imageView6,
R.id.imageView7,
R.id.imageView8
};
private ArrayList<ImageView> list=new ArrayList<>();
private boolean isOpen=false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
for (int i=0;i<res.length;i++){
ImageView imageView=findViewById(res[i]);
imageView.setOnClickListener(this);
list.add(imageView);
}
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.imageView1:
if (isOpen){
closeMenu();
isOpen=false;
}else {
openMenu();
isOpen=true;
}
break;
default:
Toast.makeText(this,v.getTag().toString(),Toast.LENGTH_SHORT).show();
break;
}
}
//关闭菜单
private void closeMenu() {
for(int i=1;i<res.length;i++){
ObjectAnimator a1= ObjectAnimator.ofFloat(list.get(i),"translationX",100*i,0);
a1.setInterpolator(new BounceInterpolator());
ObjectAnimator a2= ObjectAnimator.ofFloat(list.get(i),"translationY",100*i,0);
a2.setInterpolator(new BounceInterpolator());
AnimatorSet set=new AnimatorSet();
set.setDuration(300);
set.playTogether(a1,a2);
set.start();
}
}
//打开菜单
private void openMenu() {
for(int i=1;i<res.length;i++){
ObjectAnimator a1= ObjectAnimator.ofFloat(list.get(i),"translationX",0,100*i);
a1.setInterpolator(new BounceInterpolator());
ObjectAnimator a2= ObjectAnimator.ofFloat(list.get(i),"translationY",0,100*i);
a2.setInterpolator(new BounceInterpolator());
AnimatorSet set=new AnimatorSet();
set.setDuration(300);
set.playTogether(a1,a2);
set.start();
}
}
}
效果展示
再次点击第一个按钮可以把菜单收回去,由于录屏软件的限制,所以没录到。