android 动画入门(二)

上篇是动画入门一重点讲的是用xml生成动画以及一些属性怎么使用,发现访问量还可以,感谢大家的观看,这篇博客主要是讲上篇使用xml生成的动画改成它对应的类来使用,请看下图:


而这四个类都是继承Animtion,


我们看下父类Animtion都有什么方法:



这里很多方法都比较容易看懂,到时候会把上面的几个重要的方法讲解下,现在把上面四个类说明下:

AlphaAnimation

它有二个构造函数如下:

public AlphaAnimation(Context context, AttributeSet attrs) {} 

参数说明:

第一个是上下文

第二个参数属性集合,等下我会通过查看源代码知道它是怎么回事

 public AlphaAnimation(float fromAlpha, float toAlpha) {} 

参数说明:

fromAlpha:起始透明度,它的值范围为[0,1]

toAlpha:动画结束时透明度,它的值范围为[0,1]

一般使用代码都是使用第二个构造函数,如果你看过AlphaAnimtion类的源代码发现没几行代码,因为所有的逻辑都在它父类Animtion类中,现在写个例子完下,

final AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
alphaAnimation.setDuration(5000);
alphaAnimation.setFillAfter(true);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv_anim.startAnimation(alphaAnimation);
}
});

效果:


终于等到周末了,可以双休了,这二天要把这篇博客写完,进入真题,上面讲了最简单的动画就是透明度,

TranslateAnimation

这是个平移动画,它有三个构造函数,

public TranslateAnimation(Context context, AttributeSet attrs) {} 这个不讲
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {}

参数说明:

fromXDelta:x轴起点坐标

toXDelta:x终点坐标

fromYDelta:y轴起点坐标

toYDelta:y轴终点坐标

例子:

final TranslateAnimation translateAnimation = new TranslateAnimation(0, 200, 0, 0);
translateAnimation.setDuration(5000);
translateAnimation.setFillAfter(false);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv_anim.startAnimation(translateAnimation);
}
});

上面的动画是从imageview的0坐标位置(也就是从view的左上角开始)到坐标为200的位置,效果如下:


public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
            int fromYType, float fromYValue, int toYType, float toYValue) {}

参数说明:

fromXType:x轴起点坐标相对谁

fromXValue:x轴起点坐标

toXType:动画结束时x轴相对谁

toXValue:x轴结束动画坐标点

fromYType,fromYValue,toYType,toYValue同上面的一样,只是换成了y轴而已,意义一样

fromXType,toXType,fromYType,toYType这些int值我咋知道怎么传呢?其实看下它的源码,源码中的注释就写的很清楚了,

@param fromXType Specifies how fromXValue should be interpreted. One of
     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
     *        Animation.RELATIVE_TO_PARENT

这个只是构造函数中第一个形参的说明,再把这三个变量在Antimion类中也找出来

 /**
     * The specified dimension is an absolute number of pixels.
     */
    public static final int ABSOLUTE = 0;


    /**
     * The specified dimension holds a float and should be multiplied by the
     * height or width of the object being animated.
     */
    public static final int RELATIVE_TO_SELF = 1;


    /**
     * The specified dimension holds a float and should be multiplied by the
     * height or width of the parent of the object being animated.
     */
    public static final int RELATIVE_TO_PARENT = 2;


ABSOLUTE  是一个整数值,以自身view左上角为坐标原点可以是正数也可以是负数,如果是x轴 正数就是向右,负数就是向左,如果是y轴 正数就是向下,负数就是向上

例子:

final TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.ABSOLUTE, -60, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0);
translateAnimation.setDuration(5000);
translateAnimation.setFillAfter(false);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv_anim.startAnimation(translateAnimation);
}
});

效果:


RELATIVE_TO_SELF:这个还是以自身view左上角为坐标原点(也就是参考点),而值可以是负数,正数,小数然后乘以它的高或者宽,

final TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.RELATIVE_TO_SELF, 1.5f, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(false);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv_anim.startAnimation(translateAnimation);
}
});

这个x轴的起点坐标是0,结束点坐标为1.5f*view的宽度(100)=150,

效果:


RELATIVE_TO_PARENT:以作用于动画上的view的父view为参考点,

final TranslateAnimation translateAnimation = new TranslateAnimation(Animation.ABSOLUTE, 0, Animation.RELATIVE_TO_PARENT, 0.8f, Animation.ABSOLUTE, 0, Animation.ABSOLUTE, 0);
translateAnimation.setDuration(2000);
translateAnimation.setFillAfter(false);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv_anim.startAnimation(translateAnimation);
}
});

这个x轴结束动画的坐标就是以父view为参考点,

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff"
  >
    <Button
        android:id="@+id/btn_start_anim"
        android:layout_width="100px"
        android:layout_height="40px"
        android:padding="10px"
        android:text="开始动画" />
    <ImageView 
        android:id="@+id/iv_anim"
        android:layout_width="100px"
        android:layout_height="100px"
        android:background="@drawable/aa"
        android:layout_below="@id/btn_start_anim"
        android:layout_marginTop="60px"
        android:layout_marginLeft="10px"
        />
    <com.example.anim.MyPointView
        android:id="@+id/mypointview"
          android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        ></com.example.anim.MyPointView>
</RelativeLayout>

从这个布局文件中可以看出imageview的父view是RelativeLayout ,也就是以整个屏幕,

结束点动画x轴坐标=0.8*(父view的宽也就是屏幕的宽,屏幕是320)=0.8*320=256

效果:


下面做一个就是线移动的例子,

xml文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff"
  >
  <LinearLayout 
      android:id="@+id/ll_root"
      android:layout_width="match_parent"
      android:layout_height="50dp"
      android:orientation="horizontal"
      android:background="#7FFFD4"
      >
      <Button 
          android:id="@+id/btn_news"
          android:layout_width="0dp"
          android:layout_height="50dp"
          android:layout_weight="1"
          android:text="新闻"
          android:gravity="center"
          android:background="#00000000"
          />
      <Button 
          android:id="@+id/btn_military"
          android:layout_width="0dp"
          android:layout_height="50dp"
          android:layout_weight="1"
          android:gravity="center"
          android:text="军事"
          android:background="#00000000"
          />
      <Button 
          android:id="@+id/btn_amusement"
          android:layout_width="0dp"
          android:layout_height="50dp"
          android:layout_weight="1"
           android:text="娱乐"
           android:gravity="center"
           android:background="#00000000"
          />
  </LinearLayout>
    <View
        android:id="@+id/v_under_line"
        android:layout_width="60px"
        android:layout_height="4px"
        android:layout_marginLeft="20dp"
        android:layout_below="@id/ll_root"
        android:background="#CD6090"
        />
</RelativeLayout>

代码:

package com.example.anim;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.Toast;
@SuppressLint("NewApi")
public class MainActivity extends Activity implements OnClickListener {
private static final String TAG = "MainActivity";
private Button btn_news;
private Button btn_military;
private Button btn_amusement;
private int screenWidth;
private int startX = 0;
private View v_under_line;
private int frameTagId = R.id.btn_news;//记录上一次滑动框的位置
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSreenWidth();
v_under_line = findViewById(R.id.v_under_line);
btn_news = (Button) findViewById(R.id.btn_news);
btn_military = (Button) findViewById(R.id.btn_military);
btn_amusement = (Button) findViewById(R.id.btn_amusement);

btn_news.setOnClickListener(this);
btn_military.setOnClickListener(this);
btn_amusement.setOnClickListener(this);

}
/**
* 获取屏幕的宽 高
*/
private void getSreenWidth() {
WindowManager wm = getWindowManager();
screenWidth = wm.getDefaultDisplay().getWidth();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_news:
if(frameTagId!=R.id.btn_news){
frameTagId=R.id.btn_news;
int endX = 0;
translate(startX,endX);
startX = endX;
}
break;
case R.id.btn_military://军事
if(frameTagId!= R.id.btn_military){
frameTagId= R.id.btn_military;
int endX = screenWidth/3;
translate(startX,endX);
startX = endX;
}
break;
case R.id.btn_amusement://娱乐
if(frameTagId!= R.id.btn_amusement){
frameTagId= R.id.btn_amusement;
int endX = screenWidth/3*2;
translate(startX,endX);
startX=endX;
}
break;
}
}
public void translate(int startX,int endX){
TranslateAnimation translateAnimation = new TranslateAnimation(startX,endX,0,0);
translateAnimation.setDuration(800);
translateAnimation.setFillAfter(true);
v_under_line.startAnimation(translateAnimation);
}
}

效果:


ScaleAnimation:渐变缩放尺寸效果

它有四个构造函数:

public ScaleAnimation(Context context, AttributeSet attrs) {} 第一个不讲

public ScaleAnimation(float fromX, float toX, float fromY, float toY) {}

参数说明:

fromX:x轴开始缩放的比例,是float值

toX:x轴结束缩放的比例 是float值

fromY:y轴开始缩放的比例,是float值

toY:y轴结束是缩放的比例 是float值

例子代码:

final ScaleAnimation scaleAnimation = new ScaleAnimation(0f, 2.0f, 0, 2.0f);
scaleAnimation.setDuration(8000);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv.startAnimation(scaleAnimation);
}
});

效果:


public ScaleAnimation(float fromX, float toX, float fromY, float toY,float pivotX, float pivotY) {}

fromX:x轴开始缩放的比例,是float值

toX:x轴结束缩放的比例 是float值

fromY:y轴开始缩放的比例,是float值

toY:y轴结束是缩放的比例 是float值

pivotX:x轴伸缩值 是整数值,可以是正数也可以是负数

pivotY:y轴伸缩值 是整数值,可以是正数也可以是负数

例子:

final ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1.0f, 0, 1.0f,30, 30);
scaleAnimation.setDuration(8000);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
iv.startAnimation(scaleAnimation);
}
});

效果:


备注:计算x轴缩放的坐标点=0*(imageview的宽度)+30,y轴缩放的坐标点=0*(imageview的高度)+30

所以它的起点缩放坐标为(30,30)

public ScaleAnimation(float fromX, float toX, float fromY, float toY,
            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {}

fromX:x轴开始缩放的比例,是float值

toX:x轴结束缩放的比例 是float值

fromY:y轴开始缩放的比例,是float值

toY:y轴结束是缩放的比例 是float值

pivotXType 缩放比例模式,这个和前面的平移动画是一样的,

pivotXValue 相对模式的值

pivotYType 同上

pivotYValue 同上

这个就不讲了,下面讲一个例子玩下,

package com.example.anim;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class MyPointView extends View {
public MyPointView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyPointView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(160, 300, 80, paint);
canvas.drawPoint(160, 300, paint);
}
}


public class MainActivity extends Activity{
private static final String TAG = "MainActivity";
private Button btn_start_anim;
private ImageView iv;
private MyPointView  mypointview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
mypointview = (MyPointView) findViewById(R.id.mypointview);
btn_start_anim = (Button) findViewById(R.id.btn_start_anim);
final ScaleAnimation scaleAnimation = new ScaleAnimation(0, 1.0f, 0, 1.0f,160, 300);
scaleAnimation.setRepeatCount(100);
scaleAnimation.setRepeatMode(Animation.REVERSE);
scaleAnimation.setDuration(100);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mypointview.startAnimation(scaleAnimation);
}
});
}
}

效果:


RotateAnimation view旋转效果

它的构造函数如下:

public RotateAnimation(Context context, AttributeSet attrs) {} 第一个不讲

public RotateAnimation(float fromDegrees, float toDegrees) {}

参数说明:

fromDegrees 开始旋转的角度

toDegrees:动画所执行的角度

public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {}

参数说明:

fromDegrees: 开始旋转的角度

toDegrees:动画所执行的角度

pivotX:x轴缩放模式,具体的数值,参考的是view本身

pivotY:y轴缩放模式,具体的数值,参考的是view本身

public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
            int pivotYType, float pivotYValue) {}

这个同上讲的一样,不讲

现在讲一个例子,

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff"
  >
  <Button 
      android:id="@+id/btn_start_anim"
      android:layout_width="match_parent"
      android:layout_height="45px"
      android:text="开始动画"
      android:gravity="center"
      android:background="#F4A460"
      />
   <ImageView 
       android:id="@+id/iv"
       android:layout_width="100px"
       android:layout_height="100px"
       android:background="@drawable/aa"
       android:layout_below="@id/btn_start_anim"
       android:layout_marginTop="15px"
       android:layout_marginLeft="10px"
       android:visibility="visible"
       />
   <com.example.anim.MyCircleView 
       android:id="@+id/mycircleview"
       android:layout_width="200px"
       android:layout_height="200px"
       android:layout_marginLeft="60px"
       android:layout_below="@id/iv"
       />
   <com.example.anim.MyPointView
        android:id="@+id/mypointview"
       android:layout_width="200px"
       android:layout_height="200px"
       android:layout_marginLeft="60px"
       android:layout_below="@id/iv"
       ></com.example.anim.MyPointView>
</RelativeLayout>


自定义View圆:

package com.example.anim;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class MyCircleView extends View {
public MyCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyCircleView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyCircleView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStrokeWidth(2);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.BLUE);
canvas.drawCircle(100, 100, 78, paint);
paint.setColor(Color.GREEN);
canvas.drawCircle(100, 100, 98, paint);
}
}

自定义2个小圆和线的view:

package com.example.anim;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class MyPointView extends View{
public MyPointView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyPointView(Context context) {
super(context);
}
@Override
public void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(2);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(100, 11, 9, paint);
paint.setColor(Color.DKGRAY);
canvas.drawCircle(100, 188, 9, paint);
paint.setColor(Color.RED);
canvas.drawLine(100, 11, 100, 188, paint);
}
}

逻辑代码:

mypointview = (MyPointView) findViewById(R.id.mypointview);
mycircleview = (MyCircleView) findViewById(R.id.mycircleview);
btn_start_anim = (Button) findViewById(R.id.btn_start_anim);

final RotateAnimation rotateAnimation = new RotateAnimation(0, 360,100,100);
rotateAnimation.setRepeatCount(100);
rotateAnimation.setRepeatMode(Animation.RESTART);
rotateAnimation.setDuration(500);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mypointview.startAnimation(rotateAnimation);
}
});

效果:


分析图:


记得很早之前优酷客户端有个效果,就是另外旋转动画实现的,今天就实现玩下,首先看下布局效果:


这些图片都是从网上找的,其实这个动画只要明白2点就可以做出来,

1:顺时针和逆时针问题,这个和我们的钟表走的是一样的

2:旋转点问题,

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#ffffff" >
    <RelativeLayout
        android:id="@+id/relate_level3"
        android:layout_width="280dp"
        android:layout_height="140dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level3" >
        <ImageButton
            android:id="@+id/c1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="6dip"
            android:layout_marginLeft="12dip"
            android:background="@drawable/channel1" />
        <ImageButton
            android:id="@+id/c2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/c1"
            android:layout_marginBottom="12dip"
            android:layout_marginLeft="28dip"
            android:background="@drawable/channel2" />
        <ImageButton
            android:id="@+id/c3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/c2"
            android:layout_marginBottom="8dip"
            android:layout_marginLeft="6dip"
            android:layout_toRightOf="@+id/c2"
            android:background="@drawable/channel3" />
        <ImageButton
            android:id="@+id/c4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="6dip"
            android:background="@drawable/channel4" />
        <ImageButton
            android:id="@+id/c5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/c6"
            android:layout_marginBottom="8dip"
            android:layout_marginRight="6dip"
            android:layout_toLeftOf="@+id/c6"
            android:background="@drawable/channel5" />
        <ImageButton
            android:id="@+id/c6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_above="@+id/c7"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="12dip"
            android:layout_marginRight="28dip"
            android:background="@drawable/channel6" />
        <ImageButton
            android:id="@+id/c7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="6dip"
            android:layout_marginRight="12dip"
            android:background="@drawable/channel7" />
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/relate_level2"
        android:layout_width="180dp"
        android:layout_height="90dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level2" >
        <ImageButton
            android:id="@+id/ib_level_menu"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_margin="6dip"
            android:background="@drawable/icon_menu" />
        <ImageButton
            android:id="@+id/search"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_margin="10dip"
            android:background="@drawable/icon_search" />
        <ImageButton
            android:id="@+id/myyouku"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_margin="10dip"
            android:background="@drawable/icon_myyouku" />
    </RelativeLayout>
    <RelativeLayout
        android:id="@+id/relate_level1"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:background="@drawable/level1" >
        <ImageButton
            android:id="@+id/ib_level_home"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@drawable/icon_home" />
    </RelativeLayout>
</RelativeLayout>

封装的动画工具类MyAnimationUtils.java

package com.example.anim;


import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;


public class MyAnimationUtils {
/**
* @param viewGroup 动画作用的view对象
* @param durationMillis 动画执行的时间
*/
public static void startAnimationsIn(ViewGroup viewGroup,long durationMillis){
viewGroup.setVisibility(0);
RotateAnimation animation = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 1.0f);
animation.setFillAfter(true);
animation.setDuration(durationMillis);
viewGroup.startAnimation(animation);
}
/**
* 逆向旋转
* @param viewgroup 动画作用的view对象
* @param durationMillis 动画执行的时间
* @param startOffset 延迟执行动画
*/
public static void startAnimationsOut(final ViewGroup viewgroup,
long durationMillis, long startOffset) {
Animation animation;
animation = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF,
0.5f, Animation.RELATIVE_TO_SELF, 1.0f);
animation.setFillAfter(true);
animation.setDuration(durationMillis);
animation.setStartOffset(startOffset);
viewgroup.startAnimation(animation);
}
}

逻辑控制代码:

public class MyYoukuTestActivity extends Activity implements OnClickListener {
private RelativeLayout relate_level3;//最外层布局
private RelativeLayout relate_level2;//中间层布局
private RelativeLayout relate_level1;//最底层布局

private ImageButton ib_level_home;
private ImageButton ib_level_menu;

private boolean isLevel2Out = false;
private boolean isLevel1Out = false;
private long durationMillis = 1000;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_youku_test);
initView();
initListener();
}
private void initListener() {
ib_level_home.setOnClickListener(this);
ib_level_menu.setOnClickListener(this);
}
private void initView() {
relate_level3 = (RelativeLayout) findViewById(R.id.relate_level3);
relate_level2 = (RelativeLayout) findViewById(R.id.relate_level2);
relate_level1 = (RelativeLayout) findViewById(R.id.relate_level1);
ib_level_home = (ImageButton) findViewById(R.id.ib_level_home);
ib_level_menu = (ImageButton) findViewById(R.id.ib_level_menu);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ib_level_home:
if(!isLevel2Out&&!isLevel1Out){
MyAnimationUtils.startAnimationsOut(relate_level2, durationMillis, durationMillis);
MyAnimationUtils.startAnimationsOut(relate_level3, durationMillis, 0);
isLevel2Out = true;
isLevel1Out = true;
}else{
if(isLevel1Out){
isLevel1Out = false;
MyAnimationUtils.startAnimationsIn(relate_level2, durationMillis);
}else{
isLevel1Out = true;
MyAnimationUtils.startAnimationsOut(relate_level2, durationMillis,0);
}
}
break;
case R.id.ib_level_menu:
if(!isLevel2Out){
isLevel2Out = true;
MyAnimationUtils.startAnimationsOut(relate_level3, 500,0);
}else{
isLevel2Out = false;
MyAnimationUtils.startAnimationsIn(relate_level3, 500);
}
break;
}
}
}

效果:


现在演示tween动画一个不能交互的缺点,写一个平移动画就ok

TranslateAnimation translateAnimation = new TranslateAnimation(0, 100, 0, 0);
translateAnimation.setDuration(5000);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {

}
});
iv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "imageview被点击了", Toast.LENGTH_LONG).show();
}
});

效果:


从上面的效果上看imageview动画执行完后,再次点击它没反应,而点击imageview在未被平移的位置反而有效,这就是为什么tween要被属性动画替代的一个很大的原因,它不能交互只能实现效果,

我们知道凡是和界面有关的都是通过view的draw绘制上去的,现在分析下动画的实现过程,

iv.startAnimation(translateAnimation);我们一般是这样设置一个动画给view对象的,现在看下startAnimation()源码:

 /**
     * Start the specified animation now.
     *
     * @param animation the animation to start now
     */
    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);//设置动画的其实时间为-1
        setAnimation(animation);//给view类的变量mCurrentAnimation赋值
        invalidateParentCaches();
        invalidate(true);//重新绘制
    }

下面是invalidate(true)的方法源码:

 void invalidate(boolean invalidateCache) {
        if (skipInvalidate()) {
            return;
        }
        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) ||
                (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) ||
                (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || isOpaque() != mLastIsOpaque) {
            mLastIsOpaque = isOpaque();
            mPrivateFlags &= ~PFLAG_DRAWN;
            mPrivateFlags |= PFLAG_DIRTY;
            if (invalidateCache) {
                mPrivateFlags |= PFLAG_INVALIDATED;
                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
            }
            final AttachInfo ai = mAttachInfo;
            final ViewParent p = mParent;
            //noinspection PointlessBooleanExpression,ConstantConditions
            if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
                if (p != null && ai != null && ai.mHardwareAccelerated) {
                    // fast-track for GL-enabled applications; just invalidate the whole hierarchy
                    // with a null dirty rect, which tells the ViewAncestor to redraw everything
                    p.invalidateChild(this, null);
                    return;
                }
            }


            if (p != null && ai != null) {
                final Rect r = ai.mTmpInvalRect;
                r.set(0, 0, mRight - mLeft, mBottom - mTop);
                // Don't call invalidate -- we don't want to internally scroll
                // our own bounds
             
  p.invalidateChild(this, r);//这是调用viewgroup的invalidateChild()方法
            }
        }
    }

ViewGroup中的invalidateChild(View child, final Rect dirty)方法,再调用ViewGroup中的invalidateChildInParent(final int[] location, final Rect dirty)方法,然后调用ViewGroup中的dispatchDraw()方法绘制其子view,下面是ViewGroup中的部分dispatchDraw()源码:

  @Override
    protected void dispatchDraw(Canvas canvas) {
        final int count = mChildrenCount;
        final View[] children = mChildren;
        int flags = mGroupFlags;
..........
        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
            for (int i = 0; i < count; i++) {
                final View child = children[i];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |=
drawChild(canvas, child, drawingTime);
                }
            }
        } else {
            for (int i = 0; i < count; i++) {
                final View child = children[getChildDrawingOrder(count, i)];
                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
                    more |= drawChild(canvas, child, drawingTime);
                }
            }
        }
..............省略部分源码
        // Draw any disappearing views that have animations
        if (mDisappearingChildren != null) {
            final ArrayList<View> disappearingChildren = mDisappearingChildren;
            final int disappearingCount = disappearingChildren.size() - 1;
            // Go backwards -- we may delete as animations finish
            for (int i = disappearingCount; i >= 0; i--) {
                final View child = disappearingChildren.get(i);
                more |= drawChild(canvas, child, drawingTime);
            }
        }
........................   省略部分源码
    }

红色的代码是主要的,drawChild()源码如下:

 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return child.draw(canvas, this, drawingTime);
    }

这是调用view的draw(Canvas canvas, ViewGroup parent, long drawingTime)方法:

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        boolean useDisplayListProperties = mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
        boolean more = false;
        final boolean childHasIdentityMatrix = hasIdentityMatrix();
        final int flags = parent.mGroupFlags;
.......
     
  final Animation a = getAnimation(); //这个就是setAnimation()方法中的动画赋值给mCurrentAnimation,
        if (a != null) {
            more = drawAnimation(parent, drawingTime, a, scalingRequired);
            concatMatrix = a.willChangeTransformationMatrix();
            if (concatMatrix) {
                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            transformToApply = parent.getChildTransformation();
        } else {
            if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) ==
                    PFLAG3_VIEW_IS_ANIMATING_TRANSFORM && mDisplayList != null) {
                // No longer animating: clear out old animation matrix
                mDisplayList.setAnimationMatrix(null);
                mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            if (!useDisplayListProperties &&
                    (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                final Transformation t = parent.getChildTransformation();
                final boolean hasTransform = parent.getChildStaticTransformation(this, t);
                if (hasTransform) {
                    final int transformType = t.getTransformationType();
                    transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
                    concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
                }
            }
        }
...........
        concatMatrix |= !childHasIdentityMatrix;
.................
        // Sets the flag as early as possible to allow draw() implementations
        // to call invalidate() successfully when doing animations
        mPrivateFlags |= PFLAG_DRAWN;
  }
        }
...............
        int sx = 0;
        int sy = 0;
        if (!hasDisplayList) {
            computeScroll();
            sx = mScrollX;
            sy = mScrollY;
        }
        final boolean hasNoCache = cache == null || hasDisplayList;
        final boolean offsetForScroll = cache == null && !hasDisplayList &&
                layerType != LAYER_TYPE_HARDWARE;
..................
        int restoreTo = -1;
        if (!useDisplayListProperties || transformToApply != null) {
            restoreTo = canvas.save();
        }  
        float alpha = useDisplayListProperties ? 1 : (getAlpha() * getTransitionAlpha());
      ................. 
        if (!useDisplayListProperties && hasDisplayList) {
            displayList = getDisplayList();
            if (!displayList.isValid()) {
                // Uncommon, but possible. If a view is removed from the hierarchy during the call
                // to getDisplayList(), the display list will be marked invalid and we should not
                // try to use it again.
                displayList = null;
                hasDisplayList = false;
            }
        }
..................
        mRecreateDisplayList = false;
.............
        return more;
    }

看下drawAnimation源码:

  private boolean drawAnimation(ViewGroup parent, long drawingTime,
            Animation a, boolean scalingRequired) {
        Transformation invalidationTransform;
        final int flags = parent.mGroupFlags;
        final boolean initialized = a.isInitialized();
//判断是否初始化了
        if (!initialized) {//没有初始化的话
            a.initialize(mRight - mLeft, mBottom - mTop, parent.getWidth(), parent.getHeight());//调用Animation的initialize()进行初始化,把view的绘制正方形绘制出来
            a.initializeInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop);//绘制view的可行区域
            if (mAttachInfo != null) a.setListenerHandler(mAttachInfo.mHandler);
            onAnimationStart();//动画开始回调
        }
        final Transformation t = parent.getChildTransformation();
        boolean more = a.getTransformation(drawingTime, t, 1f);
        if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
            if (parent.mInvalidationTransformation == null) {
                parent.mInvalidationTransformation = new Transformation();
            }
            invalidationTransform = parent.mInvalidationTransformation;
            a.getTransformation(drawingTime, invalidationTransform, 1f);这是重点,前面逻辑是判断是否需要缩放以及是否需要渐变透明,a是Animation变量,查看下Animation的getTransformation()方法
        } else {
            invalidationTransform = t;
        }
        if (more) {
            if (!a.willChangeBounds()) {
                if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
                        ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
                    parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
                } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
                    // The child need to draw an animation, potentially offscreen, so
                    // make sure we do not cancel invalidate requests
                    parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    parent.invalidate(mLeft, mTop, mRight, mBottom);
                }
            } else {
                if (parent.mInvalidateRegion == null) {
                    parent.mInvalidateRegion = new RectF();
                }
                final RectF region = parent.mInvalidateRegion;
                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                        invalidationTransform);
                // The child need to draw an animation, potentially offscreen, so
                // make sure we do not cancel invalidate requests
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                final int left = mLeft + (int) region.left;
                final int top = mTop + (int) region.top;
                parent.invalidate(left, top, left + (int) (region.width() + .5f),
                        top + (int) (region.height() + .5f));
            }
        }
        return more;
    }

 public boolean getTransformation(long currentTime, Transformation outTransformation,
            float scale) {
        mScaleFactor = scale;
        return getTransformation(currentTime, outTransformation);
    }

getTransformation(currentTime, outTransformation);方法源码:

    public boolean getTransformation(long currentTime, Transformation outTransformation) {
        if (mStartTime == -1) {
            mStartTime = currentTime;
        }
        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;
        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
        }
        final boolean expired = normalizedTime >= 1.0f;
        mMore = !expired;
        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            if (!mStarted) {
                fireAnimationStart();
                mStarted = true;
                if (USE_CLOSEGUARD) {
                    guard.open("cancel or detach or getTransformation");
                }         
            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
            if (mCycleFlip) {
                normalizedTime = 1.0f - normalizedTime;
            }
            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
//获取动画执行的时间
            applyTransformation(interpolatedTime, outTransformation);//这是动画执行的完整逻辑,可以看下平移或者其他动画都实现了这方法,
        }
        if (expired) {
            if (mRepeatCount == mRepeated) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }
                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }
                mStartTime = -1;
                mMore = true;
                fireAnimationRepeat();
            }
        }
        if (!mMore && mOneMoreTime) {
            mOneMoreTime = false;
            return true;
        }
        return mMore;
    }

现在动画分析完成了,画图整理下,


这就是整个执行过程,现在主要研究下applyTransformation()方法,发现很多效果实现这个方法,

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
}

参数说明:

float interpolatedTime:动画执行的时间度 范围为0~1

Transformation  t:动画执行的逻辑就是封装在Transformation 类中

看看TranslateAnimation类中的applyTransformation()方法源码:

 @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float dx = mFromXDelta;
        float dy = mFromYDelta;
        if (mFromXDelta != mToXDelta) {
            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
        }
        if (mFromYDelta != mToYDelta) {
            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
        }

//前面都是一些赋值操作,真正操作动画的是下面这句话,是通过矩阵进行平移的
        t.getMatrix().setTranslate(dx, dy);
    }

这个类Transformation的源码比较少,可以贴出来看下,

public class Transformation {
    /**
     * Indicates a transformation that has no effect (alpha = 1 and identity matrix.)
     */
    public static final int TYPE_IDENTITY = 0x0;
    /**
     * Indicates a transformation that applies an alpha only (uses an identity matrix.)
     */
    public static final int TYPE_ALPHA = 0x1;
    /**
     * Indicates a transformation that applies a matrix only (alpha = 1.)
     */
    public static final int TYPE_MATRIX = 0x2;
    /**
     * Indicates a transformation that applies an alpha and a matrix.
     */
    public static final int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX;
    protected Matrix mMatrix;
    protected float mAlpha;
    protected int mTransformationType
    /**
     * Creates a new transformation with alpha = 1 and the identity matrix.
     */
    public Transformation() {
        clear();
    }
    /**
     * Reset the transformation to a state that leaves the object
     * being animated in an unmodified state. The transformation type is
     * {@link #TYPE_BOTH} by default.
     */
    public void clear() {
        if (mMatrix == null) {
            mMatrix = new Matrix();
        } else {
            mMatrix.reset();
        }
        mAlpha = 1.0f;
        mTransformationType = TYPE_BOTH;
    }
    /**
     * Indicates the nature of this transformation.
     *
     * @return {@link #TYPE_ALPHA}, {@link #TYPE_MATRIX},
     *         {@link #TYPE_BOTH} or {@link #TYPE_IDENTITY}.
     */
    public int getTransformationType() {
        return mTransformationType;
    }
    /**
     * Sets the transformation type.
     *
     * @param transformationType One of {@link #TYPE_ALPHA},
     *        {@link #TYPE_MATRIX}, {@link #TYPE_BOTH} or
     *        {@link #TYPE_IDENTITY}.
     */
    public void setTransformationType(int transformationType) {
        mTransformationType = transformationType;
    }
    /**
     * Clones the specified transformation.
     * @param t The transformation to clone.
     */
    public void set(Transformation t) {
        mAlpha = t.getAlpha();
        mMatrix.set(t.getMatrix());
        mTransformationType = t.getTransformationType();
    }
    /**
     * Apply this Transformation to an existing Transformation, e.g. apply
     * a scale effect to something that has already been rotated.
     * @param t
     */
    public void compose(Transformation t) {
        mAlpha *= t.getAlpha();
        mMatrix.preConcat(t.getMatrix());
    }
    /**
     * Like {@link #compose(Transformation)} but does this.postConcat(t) of
     * the transformation matrix.
     * @hide
     */
    public void postCompose(Transformation t) {
        mAlpha *= t.getAlpha();
        mMatrix.postConcat(t.getMatrix());
    }
    /**
     * @return The 3x3 Matrix representing the trnasformation to apply to the
     * coordinates of the object being animated
     */
    public Matrix getMatrix() {
        return mMatrix;
    }
    /**
     * Sets the degree of transparency
     * @param alpha 1.0 means fully opaqe and 0.0 means fully transparent
     */
    public void setAlpha(float alpha) {
        mAlpha = alpha;
    }
    /**
     * @return The degree of transparency
     */
    public float getAlpha() {
        return mAlpha;
    }
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(64);
        sb.append("Transformation");
        toShortString(sb);
        return sb.toString();
    }
    /**
     * Return a string representation of the transformation in a compact form.
     */
    public String toShortString() {
        StringBuilder sb = new StringBuilder(64);
        toShortString(sb);
        return sb.toString();
    }
    /**
     * @hide
     */
    public void toShortString(StringBuilder sb) {
        sb.append("{alpha="); sb.append(mAlpha);
        sb.append(" matrix="); mMatrix.toShortString(sb);
        sb.append('}');
    }
    /**
     * Print short string, to optimize dumping.
     * @hide
     */
    public void printShortString(PrintWriter pw) {
        pw.print("{alpha="); pw.print(mAlpha);
        pw.print(" matrix=");
        mMatrix.printShortString(pw);
        pw.print('}');
    }
}

先从构造函数讲起,

 public Transformation() {
        clear();
    }

clear()方法:

 public void clear() {
        if (mMatrix == null) {//判断矩阵是否为null
            mMatrix = new Matrix();直接new
        } else {
            mMatrix.reset();//重置
        }
        mAlpha = 1.0f;//透明度设置为1
        mTransformationType = TYPE_BOTH;
    }

set(Transformation t)方法:

 public void set(Transformation t) {
        mAlpha = t.getAlpha();//获取透明度值
        mMatrix.set(t.getMatrix());//重新设置矩阵
        mTransformationType = t.getTransformationType();
    }

//获取矩阵的方法

public Matrix getMatrix() {
        return mMatrix;
    }

//设置动画透明度

 public void setAlpha(float alpha) {
        mAlpha = alpha;
    }

发现动画都是封装在Matrix矩阵中,这个好像是大学数学中的知识,有时间要看下,

现在自定义动画实现一个简单的效果,算是对applyTransformation()方法简单的了解下,

public class MyTranslateAnimation extends TranslateAnimation {
private View view;//textview显示文字渐变的动画
public void setView(View view) {
this.view = view;
}
public MyTranslateAnimation(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
super(fromXDelta, toXDelta, fromYDelta, toYDelta);
}
public MyTranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue, int fromYType,
float fromYValue, int toYType, float toYValue) {
super(fromXType, fromXValue, toXType, toXValue, fromYType, fromYValue, toYType, toYValue);
// TODO Auto-generated constructor stub
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
view.setAlpha(interpolatedTime*0.8f);
}
}

------------------------------------------------------------------------

btn_start_anim = (Button) findViewById(R.id.btn_start_anim);
final MyTranslateAnimation translateAnimation = new MyTranslateAnimation(0, 60, 0, 0);
translateAnimation.setView(tv_content);
translateAnimation.setDuration(8000);
translateAnimation.setFillAfter(true);
btn_start_anim.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
button.startAnimation(translateAnimation);
}
});

效果:


好了这篇博客总算写完了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值