android動畫及用法
android中三種動畫:view animation, drawable animation, property animation(3.0以后引入).
官方鏈接:http://developer.android.com/guide/topics/graphics/overview.html
1.view animation/tweened animation 補間動畫
淡入淡出、縮放、平移、旋轉四種
給出兩個關鍵幀,通過一些算法將給定屬性值在給定的時間內在兩個關鍵幀間漸變。
缺點:
- 只能作用于view對象
- 只支持alpha,scale,translate,rotate,不支持背景顏色改變
- 只是改變了View的顯示效果而已,而不會真正去改變View的屬性
現在屏幕的左上角有一個按鈕,然后我們通過補間動畫將它移動到了屏幕的右下角,現在你可以去嘗試點擊一下這個按鈕,點擊事件是絕對不會觸發的,因為實際上這個按鈕還是停留在屏幕的左上角,只不過補間動畫將這個按鈕繪制到了屏幕的右下角而已。
可以通過設置interpolator屬性改變動畫漸變的方式,如AccelerateInterpolator,開始時慢,然后逐漸加快。默認為AccelerateDecelerateInterpolator。
xml中放在res/anim目錄下
2.drawable Animation/frame Animation 幀動畫
逐幀動畫的工作原理很簡單,其實就是將一個完整的動畫拆分成一張張單獨的圖片,然后再將它們連貫起來進行播放,類似于動畫片的工作原理。
3.property animation
真正改變view的left/top值以及width/height等。
ValueAnimator
ValueAnimator表示一個動畫,包含動畫的開始值,結束值,持續時間等屬性。
ValueAnimator封裝了一個TimeInterpolator,TimeInterpolator定義了屬性值在開始值與結束值之間的插值方法。
ValueAnimator還封裝了一個TypeEvaluator,根據開始、結束值與TimeInterpolator計算得到的值計算出屬性值。
ValueAnimator根據動畫已進行的時間跟動畫總時間(duration)的比計算出一個時間因子(0~1),然后根據TimeInterpolator計算出另一個因子,最后TypeAnimator通過這個因子計算出屬性值
//幾個float值平滑過渡
ValueAnimator anim = ValueAnimator.ofFloat(0f, 5f, 3f, 10f);
anim.setDuration(300);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (Float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.setStartDelay(1000);
anim.setTarget(null);
anim.setRepeatCount(5);
//anim.setInterpolator(value);
//anim.setEvaluator(value);
//anim.setFrameDelay(frameDelay);
anim.start();
ObjectAnimator extends ValueAnimator
ValueAnimator只不過是對值進行了一個平滑的動畫過渡,但我們實際使用到這種功能的場景好像并不多
float curTranslationX = textview.getTranslationX();
ObjectAnimator oa = ObjectAnimator.ofFloat(image1, "translationX", curTranslationX, -40, curTranslationX);
//ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
oa.setDuration(1000);
oa.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {}
@Override
public void onAnimationCancel(Animator animation) {}
});
oa.start();
動畫回調太多了,不想監聽那么多事件,使用AnimatorListenerAdapter:
oa.addListener(new AnimatorListenerAdapter() {
public void onAnimationRepeat(Animator animation) {
}
});
組合動畫
實現組合動畫功能主要需要借助AnimatorSet這個類,這個類提供了一個play()方法,如果我們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)將會返回一個AnimatorSet.Builder的實例,AnimatorSet.Builder中包括以下四個方法:
- after(Animator anim) 將現有動畫插入到傳入的動畫之后執行
- after(long delay) 將現有動畫延遲指定毫秒后執行
- before(Animator anim) 將現有動畫插入到傳入的動畫之前執行
- with(Animator anim) 將現有動畫和傳入的動畫同時執行
//image3先從屏幕外移動進屏幕,然后開始旋轉360度,旋轉的同時進行淡入淡出操作
ObjectAnimator moveIn = ObjectAnimator.ofFloat(image3, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(image3, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(image3, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
animSet.setDuration(5000);
animSet.start();
使用XML寫動畫
使用xml寫動畫沒有代碼快,但是可以重用性好。
目錄:res/animator文件夾
共三種標簽:
-
<animator>
對應代碼中的ValueAnimator -
<objectAnimator>
對應代碼中的ObjectAnimator -
<set>
對應代碼中的AnimatorSet
從0到100平滑過渡的動畫:
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType"/>
視圖的alpha屬性從1變成0:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"/>
將一個視圖先從屏幕外移動進屏幕,然后開始旋轉360度,旋轉的同時進行淡入淡出操作:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially" >
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="-500"
android:valueTo="0"
android:valueType="floatType" >
</objectAnimator>
<set android:ordering="together" >
<objectAnimator
android:duration="3000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" >
</objectAnimator>
<set android:ordering="sequentially" >
<objectAnimator
android:duration="1500"
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType" >
</objectAnimator>
<objectAnimator
android:duration="1500"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" >
</objectAnimator>
</set>
</set>
</set>
ValueAnimator的高級用法
搞清楚什么是TypeEvaluator:
ValueAnimator.ofFloat()方法就是實現了初始值與結束值之間的平滑過度,它內置了一個FloatEvaluator:
public class FloatEvaluator implements TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
float startFloat = ((Number) startValue).floatValue();
return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
}
}
自定義一個View,里面使用ValueAnimator.ofObject來定義動畫:
public class MyAnimView extends View {
public static final float RADIUS = 50f;
private MyPoint currentPoint;
private Paint mPaint;
public MyAnimView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.BLUE);
}
@Override
protected void onDraw(Canvas canvas) {
if (currentPoint == null) {
currentPoint = new MyPoint(RADIUS, RADIUS);
drawCircle(canvas);
startAnimation();
} else {
drawCircle(canvas);
}
}
private void drawCircle(Canvas canvas) {
float x = currentPoint.getX();
float y = currentPoint.getY();
canvas.drawCircle(x, y, RADIUS, mPaint);
}
private void startAnimation() {
MyPoint startPoint = new MyPoint(RADIUS, RADIUS);
MyPoint endPoint = new MyPoint(getWidth() - RADIUS, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (MyPoint) animation.getAnimatedValue();//PointEvaluator計算好的
invalidate();
}
});
anim.setDuration(5000);
anim.start();
}
}
MyPoint:
public class MyPoint {
private float x;
private float y;
public MyPoint(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
PointEvaluator:
public class PointEvaluator implements TypeEvaluator<MyPoint> {
@Override
public MyPoint evaluate(float fraction, MyPoint startValue, MyPoint endValue) {
float x = startValue.getX() + fraction * (endValue.getX() - startValue.getX());
float y = startValue.getY() + fraction * (endValue.getY() - startValue.getY());
MyPoint point = new MyPoint(x, y);
return point;
}
}
效果:
再增加ObjectAnimator,兩個動畫playwith,先看效果:
MyAnimView增加setColor方法:
...
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
mPaint.setColor(Color.parseColor(color));
invalidate();
}
修改startAnimation,使用AnimatorSet將兩個動畫一起播放:
private void startAnimation() {
Point startPoint = new Point(RADIUS, RADIUS);
Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint = (Point) animation.getAnimatedValue();
invalidate();
}
});
ObjectAnimator anim2 = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(),
"#0000FF", "#FF0000");
AnimatorSet animSet = new AnimatorSet();
animSet.play(anim).with(anim2);
animSet.setDuration(5000);
animSet.start();
}
ColorEvaluator.java:
//計算一個顏色字符串然后返回
public class ColorEvaluator implements TypeEvaluator {
private int mCurrentRed = -1;
private int mCurrentGreen = -1;
private int mCurrentBlue = -1;
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
String startColor = (String) startValue;
String endColor = (String) endValue;
int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
// 初始化顏色的值
if (mCurrentRed == -1) {
mCurrentRed = startRed;
}
if (mCurrentGreen == -1) {
mCurrentGreen = startGreen;
}
if (mCurrentBlue == -1) {
mCurrentBlue = startBlue;
}
// 計算初始顏色和結束顏色之間的差值
int redDiff = Math.abs(startRed - endRed);
int greenDiff = Math.abs(startGreen - endGreen);
int blueDiff = Math.abs(startBlue - endBlue);
int colorDiff = redDiff + greenDiff + blueDiff;
if (mCurrentRed != endRed) {
mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
fraction);
} else if (mCurrentGreen != endGreen) {
mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
redDiff, fraction);
} else if (mCurrentBlue != endBlue) {
mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
redDiff + greenDiff, fraction);
}
// 將計算出的當前顏色的值組裝返回
String currentColor = "#" + getHexString(mCurrentRed)
+ getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
return currentColor;
}
/**
* 根據fraction值來計算當前的顏色。
*/
private int getCurrentColor(int startColor, int endColor, int colorDiff,
int offset, float fraction) {
int currentColor;
if (startColor > endColor) {
currentColor = (int) (startColor - (fraction * colorDiff - offset));
if (currentColor < endColor) {
currentColor = endColor;
}
} else {
currentColor = (int) (startColor + (fraction * colorDiff - offset));
if (currentColor > endColor) {
currentColor = endColor;
}
}
return currentColor;
}
/**
* 將10進制顏色值轉換成16進制。
*/
private String getHexString(int value) {
String hexString = Integer.toHexString(value);
if (hexString.length() == 1) {
hexString = "0" + hexString;
}
return hexString;
}
}
Intorplator:
第一個動畫增加以下:
anim.setInterpolator(new BounceInterpolator());
設置TimeInterpolator,這個接口要實現float getInterpolation(float input);
時間因子input(0~1):已進行的時間跟動畫總時間的比,0代表開始,1代表結束
ViewPropertyAnimator
它是3.1系統中增加的一個功能。是為了方便使用ObjctAnimator來操作View,代碼更加人性化。
參考:
http://developer.android.com/guide/topics/graphics/overview.html
http://www.cnblogs.com/angeldevil/archive/2011/12/02/2271096.html
http://blog.csdn.net/guolin_blog/article/details/43536355