學(xué)習(xí)資料:Android開發(fā)藝術(shù)探索
和Animation的api
1.屬性動畫
屬性動畫可以對任意對象的屬性進行動畫不僅僅是View
岭埠,動畫默認時間間隔是300ms
盏混,默認幀率是100ms/幀
蔚鸥。
作用:在一個時間間隔內(nèi)完成對一個對象從屬性值到另一個屬性值的改變。
三個常用類:ValueAnimator,ObjectAnimator,AnimatorSet
Java代碼
private void initView() {
Button bt = (Button) findViewById(R.id.bt_object_animation_activity);
ImageView iv = (ImageView) findViewById(R.id.iv_object_animation_activity);
//改變背景屬性
ValueAnimator colorAnim = ObjectAnimator.ofInt(iv, "backgroundColor", Color.parseColor("#FF4081"), Color.CYAN);
colorAnim.setRepeatCount(2);
colorAnim.setRepeatMode(ObjectAnimator.REVERSE);
colorAnim.setDuration(1000);
colorAnim.setEvaluator(new ArgbEvaluator());//估值器
//動畫集合
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(iv, "rotationX", 0, 360),//繞x軸旋轉(zhuǎn)360度
ObjectAnimator.ofFloat(iv, "rotation", 0, -90),//逆時針旋轉(zhuǎn)90度
ObjectAnimator.ofFloat(iv, "translationX", 0, 90),//右移
ObjectAnimator.ofFloat(iv, "scaleY", 1, 0.5f),//y軸縮放到一半
ObjectAnimator.ofFloat(iv, "alpha", 1, 0.25f, 1)//透明度變換
);
//延遲一秒開始
set.setStartDelay(1000);
bt.setOnClickListener((v) -> {
//改變屬性 位置 向下移動iv高的二分之一
ObjectAnimator.ofFloat(iv, "translationY", iv.getHeight() / 2).start();
//背景屬性改變開始
colorAnim.start();
//集合動畫
set.setDuration(3000).start();
});
}
軸點默認為View
的中心點括饶。
常用的propertyName
:
-
rotationX
圍繞x軸旋轉(zhuǎn) -
rotationY
圍繞y軸旋轉(zhuǎn) -
rotation
圍繞軸點旋轉(zhuǎn) -
translationX
在x軸方向上平移 -
translationY
在y軸方向上平移 -
scaleX
在x軸方向縮放 -
scaleY
在y軸方向縮放 -
alpha
透明度 -
width
寬度 -
height
高度
1.2 常用方法介紹
1.2.1 ObjectAnimator
類
ObjectAnimator.ofFloat(Object target, String propertyName, float... values)
Constructs and returns an ObjectAnimator that animates between float values.
返回一個根據(jù)方法內(nèi)的具體的values
創(chuàng)建的ObjectAnimator
對象
-
Object target
株茶,目標控件View
的對象 -
String propertyName
,屬性的名字 -
float... values
,根據(jù)具體需求需要的屬性值
ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
Constructs and returns an ObjectAnimator that animates between the sets of values specified in PropertyValueHolder objects
返回一個由PropertyValueHolder
對象創(chuàng)建的ObjectAnimator
對象
public void byPropertyValuesHolder(ImageView iv) {
PropertyValuesHolder pvh_1 = PropertyValuesHolder.ofFloat("rotationX", 0, 360);
PropertyValuesHolder pvh_2 = PropertyValuesHolder.ofFloat("rotation", 0, -90);
PropertyValuesHolder pvh_3 = PropertyValuesHolder.ofFloat("alpha", 1, 0.25f, 1);
ObjectAnimator.ofPropertyValuesHolder(iv, pvh_1, pvh_2, pvh_3).setDuration(1000).start();
}
ofInt(Object target, String propertyName, int... values)
Constructs and returns an ObjectAnimator that animates between int values.
返回一個由int
值屬性創(chuàng)建的ObjectAnimator
對象
-
setTarget(Object target)
設(shè)置動畫目標View
1.2.2 ValueAnimator
類
ValueAnimator
的setEvaluator(new ArgbEvaluator())
設(shè)置估值器addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)
Adds a listener to the set of listeners that are sent update events through the life of an animation.
添加一個監(jiān)聽图焰,可以用來在動畫過程中改變屬性
setRepeatCount(int num)
設(shè)置動畫的循環(huán)次數(shù)启盛,默認為0,-1為無限循環(huán)setStartDelay(long startDelay)
設(shè)置動畫開始的延遲時間
cancel()
取消一個正在進行的動畫技羔。取消前僵闯,動畫進行到哪個狀態(tài),取消后藤滥,就保持在那個狀態(tài)鳖粟。end()
結(jié)束動畫。調(diào)用結(jié)束方法后拙绊,View
會跳轉(zhuǎn)到結(jié)束狀態(tài)向图。如果動畫設(shè)置了循環(huán)次數(shù)setRepeatCount()
和重復(fù)模式setRepeatMode(ObjectAnimator.REVERSE)
,結(jié)束狀態(tài)就要根據(jù)具體設(shè)置分析标沪。
1.2.3 AnimatorSet
類
playTogether(Animator... items)
Sets up this AnimatorSet to play all of the supplied animations at the same time.
同時播放所有的Animator
動畫對象榄攀。
playSequentially(Animator... items)
Sets up this AnimatorSet to play each of the supplied animations when the previous animation ends.
順序播放Animator
動畫對象
setInterpolator(TimeInterpolator interpolator)
Sets the TimeInterpolator for all current child animations of this AnimatorSet.
設(shè)置插值器
1.2.4 Animator
類
直接子類:AnimatorSet
和 ValueAnimator
間接子類:ObjectAnimator
和 TimeAnimator
Animator
類的方法子類都可以直接使用。
addListener(Animator.AnimatorListener listener)
Adds a listener to the set of listeners that are sent events through the life of an animation, such as start, repeat, and end.
為動畫添加一個監(jiān)聽過程的接口金句。如果不想實現(xiàn)AnimatorListener
接口中的所有方法也可以繼承AnimatorListenerAdapter
檩赢。
set.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
toast("動畫開始");
}
@Override
public void onAnimationEnd(Animator animator) {
toast("動畫結(jié)束");
}
@Override
public void onAnimationCancel(Animator animator) {
toast("動畫取消");
}
@Override
public void onAnimationRepeat(Animator animator) {
toast("動畫重建");
}
});
實現(xiàn)了接口中全部的方法。
繼承AnimatorListenerAdapter
:
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
toast("動畫結(jié)束");
}
});
根據(jù)實際需求违寞,實現(xiàn)不同的方法贞瞒。
addPauseListener(Animator.AnimatorPauseListener listener)
Adds a pause listener to this animator
為動畫增加暫停監(jiān)聽
2.插值器和估值器
TimeInterpolator
,時間插值器趁曼。用來根據(jù)時間流逝的百分比來計算出當(dāng)前屬性的值變化的百分比军浆。
常用插值器:
名稱 | 作用 |
---|---|
LinearInterpolator |
線性插值器。勻速動畫 |
AccelerateDecelerateInterpolator |
加速減速插值器 |
DecelerateInterpolator |
勻減速插值器挡闰。動作越來越慢 |
BounceInterpolator |
回彈插值器瘾敢。到達平移后,回彈 |
CycleInterpolator |
循環(huán)插值器尿这。在兩點間往還運動 |
PathInterpolator |
路徑插值器簇抵。根據(jù)單一方向定義的路徑坐標運動 |
OvershootInterpolator |
超越插值器。超出后射众,再返回來 |
AnticipateInterpolator |
預(yù)期插值器碟摆。先反向運動再根據(jù)指定的方向運動 |
都是Interpolator
的子類
TypeEvaluator
,類型估值算法(估值器)叨橱。用來根據(jù)當(dāng)前屬性變化改變的百分比來計算改變后的屬性值典蜕。
-
IntEvaluator
断盛,針對整型屬性 -
FloatEvaluator
,針對浮點型屬性 -
ArgbEvaluator
愉舔,針對Color
屬性
2.1簡單Demo
private void initView() {
Button bt = (Button) findViewById(R.id.bt_interpolator_activity);
ImageView iv = (ImageView) findViewById(R.id.iv_interpolator_activity);
LinearLayout root = (LinearLayout) findViewById(R.id.root_interpolator_activity);//根布局
AnimatorSet set = new AnimatorSet();
set.setInterpolator(new BounceInterpolator());
set.setDuration(3000);
//利用View的post方法拿到根布局的高度
root.post(() -> {
//計算下降高度
int height = root.getHeight() - iv.getHeight() - bt.getHeight();
//設(shè)置動畫
set.play(ObjectAnimator.ofFloat(iv, "translationY", height));
});
bt.setOnClickListener(v ->set.start());
}
利用BounceInterpolator
可以很方便的做出模擬小球下落的動畫钢猛。也可以根據(jù)需求進行自定義插值器。
2.2 對任意屬性做動畫
對Object
的屬性abc
做動畫轩缤,必須滿足2個條件:
-
object
必須提供setAbc()
的方法命迈。如果動畫的時候沒有傳遞初始值,還要提供getAbc()
方法火的。因為系統(tǒng)要去abc
的初始值壶愤。如果不滿足,程序直接Crash
-
object
的setAbc
對屬性abc
所做的改變必須能夠通過某種方法反映出來馏鹤,比如UI
改變之類的征椒。這條不滿足,動畫無效但程序不會Crash
2.2.1 改變Button
的寬度
例如湃累,想要利用屬性對話來改變一個Button
的寬度勃救。
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt));
}
private void performAnimate(Button bt) {
ObjectAnimator.ofInt(bt, "width", 500).setDuration(1000).start();
}
實際測試,這段代碼完全不起作用治力。
2.2.2不起作用的原因
Button
繼承的TextView
/**
* Makes the TextView exactly this many pixels wide.
* You could do the same thing by specifying this number in the
* LayoutParams.
*
* @see #setMaxWidth(int)
* @see #setMinWidth(int)
* @see #getMinWidth()
* @see #getMaxWidth()
*
* @attr ref android.R.styleable#TextView_width
*/
@android.view.RemotableViewMethod
public void setWidth(int pixels) {
mMaxWidth = mMinWidth = pixels;
mMaxWidthMode = mMinWidthMode = PIXELS;
requestLayout();
invalidate();
}
/**
* Return the width of the your view.
*
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
getWidth()
方法View
的方法剪芥,是可以獲取Button
高度的。setWidth()
方法TextView
和子類的專屬方法琴许。是用來設(shè)置TextView
的最大寬度和最小寬度的,并不是用來設(shè)置TextView
的寬度的方法溉躲。TextView
的寬度對應(yīng)于XML
的android:layout_width
榜田,setWidth
方法對應(yīng)的是android:width
。
也就是說:Button
的setWidth()
和getWidth()
對應(yīng)的就不是一個屬性锻梳。只滿足的條件1箭券,不滿足條件2
2.2.3 解決辦法
有三種解決辦法:
- 如果有權(quán)限,給對象加上
get
和set
方法 - 用一個類包裝原始對象疑枯,間接提供
get
和set
方法 - 采用
ValueAnimation
辩块,監(jiān)聽動畫過程,實現(xiàn)屬性的改變
get
和set
方法往往拿不到權(quán)限荆永。
利用包裝類方法:
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth()));
}
private void performAnimate(Button bt,int width) {
ButtonWrapper wrapper = new ButtonWrapper(bt);
wrapper.setWidth(width);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(1000).start();
}
private static class ButtonWrapper {
private View target;
public ButtonWrapper(View target) {
this.target = target;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int width) {
target.getLayoutParams().width = width;
target.requestLayout();
}
}
拿到Button
的寬度后废亭,設(shè)置給ButtonWrapper
。這樣動畫開始后具钥,Button
會從原始大小開始變化豆村。
利用ValueAnimation
方法:
private void initView() {
Button bt = (Button) findViewById(R.id.bt_button_activity);
bt.setOnClickListener((v) -> performAnimate(bt,bt.getWidth(),500));
}
private void performAnimate(Button bt,int start, int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
//IntEvaluator對象,估值的時候使用
IntEvaluator intEvaluator = new IntEvaluator();
valueAnimator.addUpdateListener((animator -> {
//獲取當(dāng)前動畫的進度值 骂删, 整型掌动, 1到100
int currentValue = (int) animator.getAnimatedValue();
//獲取當(dāng)前進度的占整個動畫過程的比例四啰,浮點型, 0到1
float fraction = animator.getAnimatedFraction();
//直接利用整型估值器粗恢,通過比例計算寬度柑晒,然后Button設(shè)置
bt.getLayoutParams().width = intEvaluator.evaluate(fraction,start,end);
bt.requestLayout();
}));
//開啟動畫
valueAnimator.setDuration(1000).start();
}
監(jiān)控動畫過程,利用IntEvaluator
估值器
3.最后
動畫基礎(chǔ)知識大概介紹完眷射,自定義TypeEvaluator
和Interpolator
以及配合自定義View
更多高級的用法匙赞,以后再做補充。
接下來相當(dāng)長的時間會用來學(xué)習(xí)自定義View
凭迹。想通過一系列博客來記錄學(xué)習(xí)罚屋,一開始先學(xué)習(xí)一些View
的基礎(chǔ)屬性知識為學(xué)習(xí)自定義View
做準備,再學(xué)習(xí)具體的測量嗅绸,繪制過程脾猛,View
的事件體系,工作原理鱼鸠。學(xué)習(xí)過程中間會加入繼續(xù)對動畫的深入學(xué)習(xí)猛拴,也可能會加入RxJava
的后續(xù)學(xué)習(xí)或者其他的框架的學(xué)習(xí)。