`# 前言
- Android群英傳讀書筆記
// SVG部分待補(bǔ)充
目錄
Android動(dòng)畫機(jī)制與使用技巧.png
Android View動(dòng)畫框架
- Animation框架定義了幾種常見動(dòng)畫
- 透明度 AlphaAnimation
- 旋轉(zhuǎn) RotateAnimation
- 縮放 ScaleAnimation
- 位移 TranslateAnimation
- 動(dòng)畫合集 AnimatorSet
- 實(shí)現(xiàn)原理
- 每次繪制視圖View所在ViewGroup中drawChild函數(shù)獲取該View的Animation的Transformation值
- 調(diào)用canvas.concat(transformToApply.getMatrix())
- 通過(guò)矩陣運(yùn)算完成動(dòng)畫幀
- 如果動(dòng)畫沒(méi)有完成忌怎,就繼續(xù)調(diào)用invalidate()函數(shù)潦俺,啟動(dòng)下次繪制來(lái)驅(qū)動(dòng)動(dòng)畫
- 從而完成整個(gè)動(dòng)畫的繪制
- 缺點(diǎn):不具備交互性诊霹,視圖動(dòng)畫結(jié)束巷挥,但響應(yīng)事件位置不變
- 優(yōu)點(diǎn):效率高,使用方便
Android屬性動(dòng)畫
- 可以響應(yīng)事件
- 通常是AnimatorSet和ObjectAnimator配合
- ObjectAnimator能夠自動(dòng)驅(qū)動(dòng)
ObjectAnimator
- 創(chuàng)建一個(gè)ObjectAnimator只需要通過(guò)它的靜態(tài)工廠類直接返回一個(gè)ObjectAnimator對(duì)象
- 參數(shù)包括一個(gè)對(duì)象和對(duì)戲那個(gè)的屬性名,但這個(gè)屬性必須有Set和Get方法,內(nèi)部會(huì)通過(guò)反射機(jī)制調(diào)用Set方法修改對(duì)象屬性值
// 簡(jiǎn)單的位移動(dòng)畫
ObjectAnimator animator = ObjectAnimator.ofFloat(
view,
"translationX",
300
);
animator.setDuration(300);
animator.start();
屬性 | 作用 |
---|---|
translationX、translationY | 作為增量控制View對(duì)象從它布局容器的左上角坐標(biāo)偏移的位置 |
rotation菱农、rotationX、rotationY | 控制View對(duì)象圍繞支點(diǎn)進(jìn)行2D和3D旋轉(zhuǎn) |
scaleX晤愧、scaleY | 控制View對(duì)象圍繞支點(diǎn)進(jìn)行2D縮放 |
pivotX大莫、pivotY | 控制View對(duì)象的支點(diǎn)位置,圍繞這個(gè)支點(diǎn)進(jìn)行旋轉(zhuǎn)和縮放變換處理官份,默認(rèn)情況下只厘,該支點(diǎn)的位置就是View對(duì)象的中心點(diǎn) |
x烙丛、y | 描述View對(duì)象在他容器的最終位置,是最初左上角坐標(biāo)和translationX羔味、translationY值得累計(jì)和 |
alpha | View對(duì)象的透明度河咽,默認(rèn)為1,0為完全透明 |
- 屬性動(dòng)畫的機(jī)制是通過(guò)反射調(diào)用Set和Get方法赋元,如果一個(gè)屬性沒(méi)有Get和Set方法應(yīng)該怎么辦忘蟹?google在應(yīng)用層提供了兩種解決方法
- 通過(guò)自定義一個(gè)屬性類或者包裝類來(lái)間接的給這個(gè)屬性增加get、set方法
- 通過(guò)ValueAnimator來(lái)實(shí)現(xiàn)
private static class WrapperView {
private View mTraget;
public WrapperView(View target) {
mTarget = target;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
- 通過(guò)上面的方法給屬性動(dòng)畫包裝一層搁凸,并提供了Get媚值、Set方法
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();
PropertyValuesHolder
- 針對(duì)同一個(gè)對(duì)象的多個(gè)屬性,可以使用PropertyValuesHolder
PropertyValuesHolder pv1 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder pv2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pv3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pv1,pv2,pv3).setDuration(2000).start();
ValueAnimator
- ObjectAnimator繼承自ValueAnimator
- 可以看作一個(gè)數(shù)值發(fā)生器护糖,用來(lái)產(chǎn)生有規(guī)律的數(shù)字
- AnimatorUpdateListener監(jiān)聽數(shù)值變換
ValueAnimator animator = ValueAnimator.ofFloat(0,100);
animator.setTarget(view);
animator.setDuration(1000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
//do the animation!
}
});
動(dòng)畫事件監(jiān)聽
- 一個(gè)完整的動(dòng)畫具有Start褥芒、Repeat、End嫡良、Cancel四個(gè)過(guò)程
- 當(dāng)然一般用到最多的是End監(jiān)聽
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0.5f);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
@Override
public void onAnimationRepeat(Animator animation) {
super.onAnimationRepeat(animation);
}
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
}
});
AnimatorSet
- 同樣是對(duì)一個(gè)控件添加多個(gè)動(dòng)畫锰扶,AnimatorSet比PropertyValuesHolder多了精確控制動(dòng)畫順序的功能
- 還是上面的例子
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
set.setDuration(2000);
set.playTogether(animator1,animator2,animator3);
set.start();
API | 作用 |
---|---|
playTogether() | 一起播放 |
playSequentially() | 順序播放 |
animSet.play(anim1).with(anim2) | anim1和anim2一起播放 |
animSet.play(anim1).before(anim2) | anim1在anim2之前播放 |
animSet.play(anim1).after(anim2) | anim1在anim2之后播放 |
XML中使用屬性動(dòng)畫
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1.0"
android:valueTo="2.0"
android:valueType="floatType"/>
- 使用
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex);
anim.setTarget(view);
anim.start();
View的animate方法
- Android3.0之后,View自帶animate方法直接驅(qū)動(dòng)屬性動(dòng)畫(相當(dāng)于簡(jiǎn)寫方法)
view.animate()
.alpha(0)
.y(300)
.setDuration(3000)
.withStartAction(new Runnable() {
@Override
public void run() {
}
})
.withEndAction(new Runnable() {
@Override
public void run() {
}
}).start();
Android布局動(dòng)畫
- 作用在ViewGroup上寝受,給ViewGroup增加View時(shí)添加一個(gè)動(dòng)畫過(guò)渡效果
- 最簡(jiǎn)單的例子
android:animateLayoutChanges="true"
- 或者使用LayoutAnimationController類自定義一個(gè)View過(guò)渡效果
- LayoutAnimationController.ORDER_NORMAL-- 順序
- LayoutAnimationController.ORDER_RANDOM-- 隨機(jī)
- LayoutAnimationController.ORDER_REVERSE-- 逆序
LinearLayout ll = (LinearLayout)findViewById(R.id.ll);
// 設(shè)置過(guò)渡動(dòng)畫
ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
sa.setDuration(2000);
// 設(shè)置布局動(dòng)畫的顯示屬性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
// 為ViewGroup設(shè)置布局動(dòng)畫
ll.setLayoutAnimation(lac);
自定義動(dòng)畫
- 實(shí)現(xiàn)applyTransformation即可
- 根據(jù)情況覆蓋父類initialize方法實(shí)現(xiàn)一些初始化工作
/**
* 電視機(jī)關(guān)閉效果
*/
public class CustomTV extends Animation {
// 中心點(diǎn)坐標(biāo)
private int mCenterWidth;
private int mCenterHeight;
/**
* 實(shí)現(xiàn)初始化操作
*/
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(1000);
// 動(dòng)畫結(jié)束后保留狀態(tài)
setFillAfter(true);
// 設(shè)置默認(rèn)插值器
setInterpolator(new AccelerateInterpolator());
mCenterWidth = width / 2;
mCenterHeight = height / 2;
}
/**
* 參數(shù)1:interpolatedTime:插值器的時(shí)間因子坷牛,由動(dòng)畫當(dāng)前完成的百分比和當(dāng)前時(shí)間所對(duì)應(yīng)的插值計(jì)算得來(lái),取值范圍0到1.0
* 參數(shù)2:Transformation:矩陣的封裝類很澄,一般使用這個(gè)類獲得當(dāng)前的矩陣對(duì)象
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
// 獲得當(dāng)前矩陣對(duì)象
final Matrix matrix = t.getMatrix();
// 通過(guò)改變matrix對(duì)象京闰,實(shí)現(xiàn)動(dòng)畫效果
matrix.preScale(1, 1 - interpolatedTime, mCenterWidth, mCenterHeight);
}
}