首页 > 代码库 > 属性动画实现卫星菜单效果

属性动画实现卫星菜单效果

原来学过用自定义控件以及视图动画来达到这个效果。后来根据慕课网的视频,接触到了属性动画,发现其精髓之处不是一点两点。


相信大家都知道,当我们在使用视图(View)动画的时候,改变轨迹时,所触发的点击事件却没有相对应的随之轨迹而改变。确切的说,Animation改变显示的位置,不可以实现交互的效果,只是实现了显示效果。

先看下Animator的介绍
This is the superclass for classes which provide basic support for animations which can be started, ended, and have AnimatorListeners added to them.

这是一个父类,什么的父类呢?一些对于动画可以提供启动,结束的类的父类,并且有可以对之实施监听事件。

第一重境界:ObjectAnimator (动画监听事件)

This subclass of ValueAnimator provides support for animating properties on target objects. The constructors of this class take parameters to define the target object that will be animated as well as the name of the property that will be animated. Appropriate set/get functions are then determined internally and the animation will call these functions as necessary to animate the property.

这个类是ValueAnimator的子类,为一些目标物体提供一些动画属性,这个类的构造函数将参数定义为将要动画的目标对象,可以使一些属性名称一样具体动画效果,set get获取,然后确定内部的东西,这些动画将调用这些功能来作为必要的动画属性。大体应该就是这个意思,本人英语水平low的很。

接下来我们先了解下属性动画ObjectAnimator,原理我也不会用语言去描述,通过简单的例子,看代码来了解通俗易懂。点击按钮,让图片做位移,旋转效果的实现。

先看下布局activity_main:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>

 <ImageView 
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:id="@+id/imageview"
  android:onClick="click"
  android:src=http://www.mamicode.com/"@drawable/ic_launcher">

起初我们接触视图动画的时候,一般都用TranslateAnimation,RotationAnition等等了,复杂的动画效果用AnimationSet也能实现。

这里就不具体的讲解了,下面来看下属性动画ObjectAnimator是怎么实现。

ObjectAnimator提供了ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。 当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束 动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法。

    ObjectAnimator.ofFloat(image, "rotation", 0F,360F).setDuration(1000).start();
    ObjectAnimator.ofFloat(image, "translationX", 0F,200F).setDuration(1000).start();
    ObjectAnimator.ofFloat(image, "translationY", 0F,200F).setDuration(1000).start();

虽然三个动画效果依次写出来,但并没有执行先后顺序,偏移XY的同时也在旋转着,并发执行。

既然存在ofxxx,我们可以使用PropertyValuesHolder这个类,它可以先将动画属性和值暂时的存储起来,后一起执行,在有些时候可以使用替换掉AnimatorSet,减少代码量。

    PropertyValuesHolder p1=PropertyValuesHolder.ofFloat("rotation", 0F,360F);
    PropertyValuesHolder p2=PropertyValuesHolder.ofFloat("translationX", 0F,200F);
    PropertyValuesHolder p3=PropertyValuesHolder.ofFloat("translationY",0F,200F);
    ObjectAnimator.ofPropertyValuesHolder(image, p1,p2,p3).setDuration(1000).start();

视图动画有AnimationSet,那我们的属性动画是不是也有一个set管理呢,答案是有的,AnimatorSet.

    ObjectAnimator animator1=ObjectAnimator.ofFloat(image, "rotation", 0F,360F);
    ObjectAnimator animator2=ObjectAnimator.ofFloat(image, "translationX", 0F,200F);
    ObjectAnimator animator3=ObjectAnimator.ofFloat(image, "translationY", 0F,200F);
    AnimatorSet set=new AnimatorSet();
    //可以试着调用不同方法,实现不同的动画效果
    //set.playTogether(animator1,animator2,animator3);
    //set.playSequentially(animator1,animator2,animator3);
    set.play(animator2).with(animator3);
    set.play(animator1).after(animator2);
    set.setDuration(1000);
    set.start();

在使用的过程中,可以试着set不同的实现顺序来看下效果,毕竟多联系下嘛。

属性动画的监听:

对于动画,一般都是一些辅助效果,比如我要删除个元素,我可能希望是个淡出的效果,但是最终还是要删掉,并不是你透明度没有了,还占着位置,所以我们需要知道动画如何结束。

  public void click(View v) {

    ObjectAnimator animator = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f);
    animator.setDuration(1000);

      animator.addListener(new AnimatorListener() {

     public void onAnimationStart(Animator animation) { // TODO
      Auto-generated method stub
     * 
     * }
     * 
     * @Override public void onAnimationRepeat(Animator animation) { // TODO
     * Auto-generated method stub
     * 
     * }
     * 
     * @Override public void onAnimationEnd(Animator animation) { // TODO
     * Auto-generated method stub Toast.makeText(MainActivity.this, "点击了",
     * Toast.LENGTH_SHORT) .show(); }
     * 
     * @Override public void onAnimationCancel(Animator animation) { // TODO
     * Auto-generated method stub
     * 
     * } });
     */
    animator.start();
}

这里可以看出,再我们这样使用new AnimatorListener的时候,可以对动画的开始,重复,结束,取消都可以进行监听操作,可是我们有时候只希望监听动画结束或者单一状态的时,该怎么办呢?

那你可以使用AnimatorListenerAdapter

   public void click(View v) {

    ObjectAnimator animator = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f);
    animator.setDuration(1000);
    animator.addListener(new AnimatorListenerAdapter() {

        @Override
        public void onAnimationEnd(Animator animation) {
            // TODO Auto-generated method stub
            super.onAnimationEnd(animation);
            Toast.makeText(MainActivity.this, "点击了", Toast.LENGTH_SHORT)
                    .show();
        }
    });

    animator.start();
}

AnimatorListenerAdapter继承了AnimatorListener接口,然后空实现了所有的方法,当你想使用其中一个状态监听时,可以单一的重写该方法。这样减少了一定的代码量。

属性动画的初步了解应该差不多了,相信他们在阅读完以上部分应该对属性动画有了一定的了解,或许已经感觉到了和视图动画的区别。记得大家都应该知道一种菜单效果--->

ObjectAnimator实现卫星菜单效果

先看张效果图吧

技术分享

当初接触过这个菜单的效果,是自定义控件的实现的,代码量挺复杂的,接触完属性动画,我就想是不是可以用它来完成这个效果呢?

接下来我们实现下,先看下布局,使用的FrameLayout

activity_main.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
    android:id="@+id/imageView_b"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingLeft="5dp"
    android:paddingTop="5dp"
    android:src=http://www.mamicode.com/"@drawable/b" />>

布局效果图:

技术分享

我们存放了5张图片,a图片为按钮图片,bcde则为卫星发射的菜单图。先把这些图片的id保存到数组中,方便我们统一的实例化。

private int[] res = { R.id.imageView_a, R.id.imageView_b, R.id.imageView_c,
        R.id.imageView_d, R.id.imageView_e };

新建一个List数组来存放这些图片。

  private List<ImageView> imageViewList = new ArrayList<ImageView>();

接下来,用循环遍历来实例化这些ImageView控件,把图片添加到imageViewList中,并实现这些菜单图片的监听事件。

 for (int i = 0; i < res.length; i++) {

        ImageView imageView = (ImageView) findViewById(res[i]);

        imageView.setOnClickListener(this);

        imageViewList.add(imageView);

    }

重写onClick方法,因为考虑到菜单要展开和收回两种状态效果。定义一个标志位,初始化为true.

private Boolean flag = true;// 用于判断展开 ,回收

点击事件方法:

@Override
public void onClick(View v) {
    // TODO Auto-generated method stub
    switch (v.getId()) {
    case R.id.imageView_a:
        if (flag) {

            startAnim();

        } else {

            closeAnim();
        }

        break;

    default:
        Toast.makeText(MainActivity.this, "click" + v.getId(),
                Toast.LENGTH_SHORT).show();
        break;
    }
}

一个展开动画效果的方法startAnim()和一个回收效果的方法closeAnim()。其实两者实现的动画效果是相反方向的。

startAnim()

 private void startAnim() {

    // TODO Auto-generated method stub
    for (int i = 1; i < res.length; i++) {

        ObjectAnimator animator = ObjectAnimator.ofFloat(
                imageViewList.get(i),
                "translationY",
                0F,
                (float) (300 * (Math.sin(Math.PI / 2 / (res.length - 2)
                        * (i - 1)))));

        ObjectAnimator animator1 = ObjectAnimator.ofFloat(
                imageViewList.get(i),
                "translationX",
                0F,
                (float) (300 * (Math.cos(Math.PI / 2 / (res.length - 2)
                        * (i - 1)))));
        AnimatorSet set = new AnimatorSet();
        set.playTogether(animator, animator1);
        set.setDuration(300);
        set.start();
        set.setInterpolator(new BounceInterpolator());
        flag = false;

    }
}

实现了位移的偏移X,Y。其实没有多少复杂的,for循环遍历展开效果,只是在偏移的路径需要一些数学知识。这里我固定了偏移的半径,也就是水平或者纵向的最大偏移量为300.

技术分享
掌握了这张图片的原理,应该就在以后的使用过程中得心应手了,不管展开的菜单个数为多少,可以根据数学算出XY偏移量即可。 相对于收回的动画方法,和展开的方法方向相反而已。

下载

属性动画实现卫星菜单效果