android動(dòng)畫(huà)及用法
android中三種動(dòng)畫(huà):view animation, drawable animation, property animation(3.0以后引入).
官方鏈接:http://developer.android.com/guide/topics/graphics/overview.html
1.view animation/tweened animation 補(bǔ)間動(dòng)畫(huà)
淡入淡出表窘、縮放翘骂、平移宝当、旋轉(zhuǎn)四種
給出兩個(gè)關(guān)鍵幀芒帕,通過(guò)一些算法將給定屬性值在給定的時(shí)間內(nèi)在兩個(gè)關(guān)鍵幀間漸變占婉。
缺點(diǎn):
- 只能作用于view對(duì)象
- 只支持alpha菇绵,scale澎媒,translate琅绅,rotate,不支持背景顏色改變
- 只是改變了View的顯示效果而已樊诺,而不會(huì)真正去改變View的屬性
現(xiàn)在屏幕的左上角有一個(gè)按鈕矮慕,然后我們通過(guò)補(bǔ)間動(dòng)畫(huà)將它移動(dòng)到了屏幕的右下角,現(xiàn)在你可以去嘗試點(diǎn)擊一下這個(gè)按鈕啄骇,點(diǎn)擊事件是絕對(duì)不會(huì)觸發(fā)的痴鳄,因?yàn)閷?shí)際上這個(gè)按鈕還是停留在屏幕的左上角,只不過(guò)補(bǔ)間動(dòng)畫(huà)將這個(gè)按鈕繪制到了屏幕的右下角而已缸夹。
可以通過(guò)設(shè)置interpolator屬性改變動(dòng)畫(huà)漸變的方式痪寻,如AccelerateInterpolator,開(kāi)始時(shí)慢虽惭,然后逐漸加快橡类。默認(rèn)為AccelerateDecelerateInterpolator。
xml中放在res/anim目錄下
2.drawable Animation/frame Animation 幀動(dòng)畫(huà)
逐幀動(dòng)畫(huà)的工作原理很簡(jiǎn)單芽唇,其實(shí)就是將一個(gè)完整的動(dòng)畫(huà)拆分成一張張單獨(dú)的圖片顾画,然后再將它們連貫起來(lái)進(jìn)行播放取劫,類(lèi)似于動(dòng)畫(huà)片的工作原理。
3.property animation
真正改變view的left/top值以及width/height等研侣。
ValueAnimator
ValueAnimator表示一個(gè)動(dòng)畫(huà)谱邪,包含動(dòng)畫(huà)的開(kāi)始值,結(jié)束值庶诡,持續(xù)時(shí)間等屬性惦银。
ValueAnimator封裝了一個(gè)TimeInterpolator,TimeInterpolator定義了屬性值在開(kāi)始值與結(jié)束值之間的插值方法末誓。
ValueAnimator還封裝了一個(gè)TypeEvaluator扯俱,根據(jù)開(kāi)始、結(jié)束值與TimeInterpolator計(jì)算得到的值計(jì)算出屬性值喇澡。
ValueAnimator根據(jù)動(dòng)畫(huà)已進(jìn)行的時(shí)間跟動(dòng)畫(huà)總時(shí)間(duration)的比計(jì)算出一個(gè)時(shí)間因子(0~1)迅栅,然后根據(jù)TimeInterpolator計(jì)算出另一個(gè)因子,最后TypeAnimator通過(guò)這個(gè)因子計(jì)算出屬性值
//幾個(gè)float值平滑過(guò)渡
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只不過(guò)是對(duì)值進(jìn)行了一個(gè)平滑的動(dòng)畫(huà)過(guò)渡晴玖,但我們實(shí)際使用到這種功能的場(chǎng)景好像并不多
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();
動(dòng)畫(huà)回調(diào)太多了读存,不想監(jiān)聽(tīng)那么多事件,使用AnimatorListenerAdapter:
oa.addListener(new AnimatorListenerAdapter() {
public void onAnimationRepeat(Animator animation) {
}
});
組合動(dòng)畫(huà)
實(shí)現(xiàn)組合動(dòng)畫(huà)功能主要需要借助AnimatorSet這個(gè)類(lèi)窜醉,這個(gè)類(lèi)提供了一個(gè)play()方法,如果我們向這個(gè)方法中傳入一個(gè)Animator對(duì)象(ValueAnimator或ObjectAnimator)將會(huì)返回一個(gè)AnimatorSet.Builder的實(shí)例艺谆,AnimatorSet.Builder中包括以下四個(gè)方法:
- after(Animator anim) 將現(xiàn)有動(dòng)畫(huà)插入到傳入的動(dòng)畫(huà)之后執(zhí)行
- after(long delay) 將現(xiàn)有動(dòng)畫(huà)延遲指定毫秒后執(zhí)行
- before(Animator anim) 將現(xiàn)有動(dòng)畫(huà)插入到傳入的動(dòng)畫(huà)之前執(zhí)行
- with(Animator anim) 將現(xiàn)有動(dòng)畫(huà)和傳入的動(dòng)畫(huà)同時(shí)執(zhí)行
//image3先從屏幕外移動(dòng)進(jìn)屏幕榨惰,然后開(kāi)始旋轉(zhuǎn)360度,旋轉(zhuǎn)的同時(shí)進(jìn)行淡入淡出操作
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寫(xiě)動(dòng)畫(huà)
使用xml寫(xiě)動(dòng)畫(huà)沒(méi)有代碼快静汤,但是可以重用性好琅催。
目錄:res/animator文件夾
共三種標(biāo)簽:
-
<animator>
對(duì)應(yīng)代碼中的ValueAnimator -
<objectAnimator>
對(duì)應(yīng)代碼中的ObjectAnimator -
<set>
對(duì)應(yīng)代碼中的AnimatorSet
從0到100平滑過(guò)渡的動(dòng)畫(huà):
<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"/>
將一個(gè)視圖先從屏幕外移動(dòng)進(jìn)屏幕,然后開(kāi)始旋轉(zhuǎn)360度虫给,旋轉(zhuǎn)的同時(shí)進(jìn)行淡入淡出操作:
<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的高級(jí)用法
搞清楚什么是TypeEvaluator:
ValueAnimator.ofFloat()方法就是實(shí)現(xiàn)了初始值與結(jié)束值之間的平滑過(guò)度藤抡,它內(nèi)置了一個(gè)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);
}
}
自定義一個(gè)View,里面使用ValueAnimator.ofObject來(lái)定義動(dòng)畫(huà):
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計(jì)算好的
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,兩個(gè)動(dòng)畫(huà)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將兩個(gè)動(dòng)畫(huà)一起播放:
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:
//計(jì)算一個(gè)顏色字符串然后返回
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;
}
// 計(jì)算初始顏色和結(jié)束顏色之間的差值
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);
}
// 將計(jì)算出的當(dāng)前顏色的值組裝返回
String currentColor = "#" + getHexString(mCurrentRed)
+ getHexString(mCurrentGreen) + getHexString(mCurrentBlue);
return currentColor;
}
/**
* 根據(jù)fraction值來(lái)計(jì)算當(dāng)前的顏色。
*/
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進(jìn)制顏色值轉(zhuǎn)換成16進(jìn)制药蜻。
*/
private String getHexString(int value) {
String hexString = Integer.toHexString(value);
if (hexString.length() == 1) {
hexString = "0" + hexString;
}
return hexString;
}
}
Intorplator:
第一個(gè)動(dòng)畫(huà)增加以下:
anim.setInterpolator(new BounceInterpolator());
設(shè)置TimeInterpolator瓷式,這個(gè)接口要實(shí)現(xiàn)float getInterpolation(float input);
時(shí)間因子input(0~1):已進(jìn)行的時(shí)間跟動(dòng)畫(huà)總時(shí)間的比,0代表開(kāi)始,1代表結(jié)束
ViewPropertyAnimator
它是3.1系統(tǒng)中增加的一個(gè)功能语泽。是為了方便使用ObjctAnimator來(lái)操作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