Pro Android学习笔记(一一四):2D动画(9):Property Animation(下)

来源:互联网 发布:php视频教程 百度网盘 编辑:程序博客网 时间:2024/05/18 00:31

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处http://blog.csdn.net/flowingflying以及作者@恺风Wei。

ViewPropertyAnimator

如果我们动画的对象是view,Android SDK通过ViewProperyAnimator提供一种更为优化的实现方式,同样一次性设置多个值的变化。小例子片段如下:

public void viewPropertiesTest(View v){ 
    //获取当前状态 
    float h = tv.getHeight(); 
    float w = tv.getWidth(); 
    float x = tv.getX(); 
    float y = tv.getY();
 
    
    //将view初始值。  
    tv.setX(w); 
    tv.setY(h); 
    tv.setAlpha(0.0f);
  
     
    //从view中获取ViewPropertyAnimator的对象
    ViewPropertyAnimator vpa = tv.animate(); 
    //设置目标值 
    vpa.setDuration(5000); 
    vpa.x(x); 
    vpa.y(y); 
    vpa.alpha(1.0f); 
    vpa.setInterpolator(new AccelerateDecelerateInterpolator());                

注意,和之前的方式不同,不需要通过start()来启动动画。当viewPropertiesTest()函数执行完,返回UI线程时,自动启动动画。

TypeEvaluator

回眸ObjectAnimator最初的小例子,通过onFloat()或者onInt()对某个属性进行设置。如果这个属性不是简单的某个value,而是对象,就需要使用onObject()来进行设置。这个对象属性可以是我们自定义的。我们回头看看当初小例子中的部分代码及解释:

ObjectAnimator fadeout = ObjectAnimator.ofFloat(tv, "alpha", 0.0f); //将alpha从当前值变为0(看不见),ofFloat()表明这是对tv对象(参数1)的alpha属性(参数2)进行的浮点类型数值的变化,从当前值变化至目标值(参数3)。哪些可以作为属性,需要具备setXXX()方法,则具备XXX属性,本例Textview对象具有public方法 view.setAlpha(float f)方法,故具有属性alpha。初始值从view.getAlpha()中获取。如果带有4个参数,例如 ofFloat(tv,"alpha",1.0f,0.0f),则第三个参数标识from的数值,第4个参数标识to的数值,即从alpha属性从1.0 变为0.0。  
fadeout.setDuration(5000);  
fadeout.start();

下面,我们将使用PointF(坐标类)来作为属性的值。由于目标的TextView并没有setPoint()和getPoint()的函数,我们需要通过自定义的MyAnimatorView类来实现。我们给出了“point”这个属性的初始值和最终值,但是在动画过程中的中间值如何,需要通过TypeEvaluator的evaluate()来给出,然后通过setPoint()设置给动画对象。

public void typeEvaluatorTest(View v){ 
    //获取当前状态 
    float h = tv.getHeight(); 
    float w = tv.getWidth(); 
    float x = tv.getX(); 
    float y = tv.getY();
  
    // 获取动画变化属性的初始值和最终值 
    PointF startPoint = new PointF(w, h); 
    PointF endPoint = new PointF(x,y);  
    // 步骤1 :提供自定义属性“point”的动画对象
    MyAnimatorView mav = new MyAnimatorView(tv);  
    // 步骤2 :通过ObjectAnimator的ofObject()来设置动画类型。参数1为动画对象;参数2对属性名称,动画对象应提供setXXX()的方法;参数3为动画过程中的中间值设定;参数4/5是初始值和最终值,如果只有参数4,则参数4位最种植,初始值由动画对象的getXXX()提供。下面分别给出了不带初始值和带有初始值的两种情况。
    //ObjectAnimator tea = ObjectAnimator.ofObject(mav, "point", new MyPointEvaluator(), endPoint);
    ObjectAnimator tea = ObjectAnimator.ofObject(mav, "point", new MyPointEvaluator(), startPoint,endPoint);
    tea.setDuration(5000); 
    Log.d("WEI","------------------------------");
    tea.start();        

下面是使用TypeEvaluator来提供中间的组合属性,本例为PointF,代码片段如下:

public class MyPointEvaluator implements TypeEvaluator<PointF>
    @Override // 实现TypeEvaluator接口的evaluate()方式,fraction是动画的进度,从0.0到1.0,根据进度,初始值和最终值,给出动画过程的中间值。
    public PointF evaluate(float fraction, PointF startValue, PointF endValue)
        Log.d("WEI","MyPointEvaluator : evaluate " + fraction);
        return new PointF(startValue.x + fraction * (endValue.x - startValue.x), 
                startValue.y + fraction * (endValue.y - startValue.y)); 
    } 
    

由于TextView不提供setPoint()的方法,不能直接作为动画对象,需要进行封装,代码片段如下:

public class MyAnimatorView{ 
        private View myView = null;  
         
        private MyAnimatorView(View v){ 
            myView = v;  
        }  
        // 如果没有初始值,同getPoint()获取,本例设置为右下角
        public PointF getPoint(){ 
            Log.d("WEI","MyAnimatorView : getPoint()");
            return new PointF(myView.getWidth(),myView.getHeight()); 
        }  
       // 通过setPoint()来设置中间值 
        public void setPoint(PointF p){ 
            Log.d("WEI","MyAnimatorView : setPoint (" + p.x + "," + p.y + ")"); 
            myView.setX(p.x); 
            myView.setY(p.y); 
        } 
    }

下图是没有设置初始值和设置初始值的debug跟踪情况:

在小例子中,属性的组合使用了PointF的坐标类型,我们也可以加入其它的属性,自定义属性的组合。

Keyframe

通过Keyframe,我们可以设定某个时刻,某属性的值是多少。下面的小例子,通过对alpha的设定,实现变淡在变浓的效果。

public void keyFrameTest(View v){ 
    //获取当前状态  
    float w = tv.getWidth(); 
    float x = tv.getX();
  
             
   // 为某个属性(本例子为alpha)设定特定时间的值。参数1为fraction,表示进度;参数2为具体数值,下面表示在进度20%时,该值为0.8,在50%时为0.2,在80%是为0.8,在100%(即最后)为1.0。
    Keyframe kf0 = Keyframe.ofFloat(0.2f, 0.8f);  
    Keyframe kf1 = Keyframe.ofFloat(0.5f, 0.2f); 
    Keyframe kf2 = Keyframe.ofFloat(0.8f, 0.8f); 
    Keyframe kf3 = Keyframe.ofFloat(1.0f, 1.0f);  

    //通过PropertyValuesHolder来关联keyframe和属性。此外,我们还设置一个从x轴从右到左的移动
    PropertyValuesHolder pvAlpha = PropertyValuesHolder.ofKeyframe("alpha", kf0,kf1,kf2,kf3);
    PropertyValuesHolder pvX = PropertyValuesHolder.ofFloat("x", w, x);
    
    ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(tv, pvAlpha,pvX);
    anim.setDuration(5000); 
    anim.start();
 

LayoutTransition

通过LayoutTransition可以对view group中的各个view进行动画处理,这些动画处理包括四种情况:

1、增加一个View(view出现,LayoutTransition.APPEARING)

2、因增加view,而改变现实的其他view(LayoutTransition.CHANGE_APPEARING)

3、删除一个View(LayoutTransition.DISAPPEARING)

4、因删除某个View,而改变现实的其他view(LayoutTransition.CHANGE_DISAPPEARING)

小例子以第一种情况,即在Layout中增加一个view,view出现时具备某种动画效果。首选,需要对Layout中通过LayoutTransition对上述view变化进行设定,可以放在activity初始化阶段:

protected void onCreate(Bundle savedInstanceState) { 
     … …   
    myLayout = (LinearLayout)findViewById(R.id.my_layout);  
    //【步骤1】:将LayoutTransittion关联与某个view group中,本例为LinearLayout
    LayoutTransition lt = new LayoutTransition(); 
    myLayout.setLayoutTransition(lt);  

   //【步骤2】:针对4种情况情况进行动画设置,本例子讲针对添加view的情况,即LayoutTransition.APPREAING

    //(2.1)设置动画ObjectAnimator,其中对象目标为何不重要,因为会通过LayoutTransition对象关联到layout,可设置为null。LayoutTransition的动画是有缺省值的,通过LogCat显示为300ms,如果我们设定的动画时间超过该值,是不其作用的,这种情况,可以通过设置LayoutTransition的动画时间来解决,例如本例可设置lt.setDuration(5000),提供一个慢速的动画效果。 
    ObjectAnimator animIn = ObjectAnimator.ofFloat(null, "rotationY", 90f,0f); 
    animIn.setDuration(lt.getDuration(LayoutTransition.APPEARING));
  
    Log.d("WEI","" + lt.getDuration(LayoutTransition.APPEARING)); 
    //(2.2)将动画加在LayoutTransition上,并指出属于那种情况下的动画
    lt.setAnimator(LayoutTransition.APPEARING, animIn); 
    //(2.3)可以监听动画的执行情况 
    animIn.addListener(new AnimatorListener() {              
       @Override 
        public void onAnimationStart(Animator animation) {  
            // TODO Auto-generated method stub  
        }              
        @Override  
        public void onAnimationRepeat(Animator animation) {  
           // TODO Auto-generated method stub 
        }              
        @Override //获取进行动画的view的例子
        public void onAnimationEnd(Animator animation) {  
            View view = (View) ((ObjectAnimator) animation).getTarget(); 
            Log.d("WEI",view.toString());  
        }              
        @Override  
        public void onAnimationCancel(Animator animation) {  
            // TODO Auto-generated method stub 
        }  
    }); 

我们也可以设置其他的情况的动画,例如删除

Keyframe kf0 = Keyframe.ofFloat(0.2f, 0.8f);  
Keyframe kf1 = Keyframe.ofFloat(0.5f, 0.5f); 
Keyframe kf2 = Keyframe.ofFloat(1.0f, 0.0f); 
PropertyValuesHolder pvh = PropertyValuesHolder.ofKeyframe(  "alpha", kf0, kf1, kf2);
ObjectAnimator animOut = ObjectAnimator.ofPropertyValuesHolder(myLayout,  pvh);  
animOut.setDuration(lt.getDuration(LayoutTransition.DISAPPEARING));  
lt.setAnimator(LayoutTransition.DISAPPEARING, animOut);

如果我们要记住某个时刻动画设置,以便后来恢复,可以使用:

Animator defaultAppearAnimator = lt.getAnimator(APPEARING);

在设置了LayoutTransition后,我们进行添加view的测试,如下:

TextView testView1 = new TextView(this); 
testView1.setText("测试Layout Transition。Hello, World! "); 
myLayout.addView(testView1); 

通过LayoutTranistion将动画加诸在每个view中,animator会复制给每个view。如果这些view有onClick的处理,在进行动画的过程中,如果进行点击,其结果不可预测,因此一般动画设定时间不要过长。


0 0