編輯:關於android開發
本篇涉及例子下載:Github
本篇講android 3.0引入的屬性動畫框架,上篇寫視圖動畫View Animation時就說過ViewAnimation的缺點,那就是動畫作用的是view本身的視覺部分,view實際屬性並沒有隨著動畫的改變而變化。很多時候就需要額外去出來由於動畫引起的事件不同步,比如ViewAnimation已經講View移出了屏幕,但View的事件觸發還在原地,這就需要額外處理了。 但是,PropertyAnimation的引入就完全解決了這個問題,它可以保證動畫和事件的變化總是一起的發生的。其本質是View是屬性變化帶動View重繪來完成動畫。
個人覺得了解繼承結構對學習框架幫助挺大的,所以開始先說一下屬性動畫的框架,我自己用StartUML畫了下面的類圖: 畫得比較糙(StartUML用的不好, -_-!),上面沒畫出TypeEvaluator和Interpolator,一來是怕復雜了不好看重點信息,二來是後面會重點講。大致講一下各個類的作用:
以上便是屬性動畫框架的大致結構,關於ypeEvaluator和Interpolator後面有講到。
1.屬性動畫時“真正的”動畫機制嗎? 我覺得不是(放下手頭的磚,讓我說完 ㄟ( ▔, ▔ )ㄏ),為什麼我說不是?它並不是直接生成一段動畫,它是通過在眾多動畫幀上改變View的屬性,然後View重繪,進而達到動畫的目的。或者把它理解成一個過渡值產生器更合適,它在每個動畫幀時間點上產生一個過渡值,然後把這個值設為相應的屬性。 2.屬性動畫VS視圖動畫: 屬性動畫相比於視圖動畫的優點是顯而易見的,因為動畫的來源是由於View的屬性變化引起的重繪,所以不存在事件與動畫不一致的情況。 視圖動畫最大的優點就是它簡單了,不需要添加各種監聽器就能實現動畫。 我還沒有兩種動畫性能的對比,不知道在實現相同效果時孰優孰劣,所以不敢妄下結論。留到後面討論吧! 3.ValueAnimator VS ObjectAnimator ObjectAnimator是ValueAnimator的便捷類,為了避免復雜的實現盡量選擇用ObjectAnimator。
前面多次說道動畫幀(Frame),什麼叫Frame呢?相當於視圖動畫中的幀動畫,也就是每一個單獨的頁面,類似於膠卷電影中的一格。人眼的可察覺刷新頻率是24幀每秒,在低於這個頻率的動畫中我們看到影像就不是連續的。而熟悉動畫中用來指定動畫刷新頻率的是setFrameDelay()方法,默認情況下它的值是10ms,也就是100幀每秒。有了刷新頻率,如何講它映射到值呢?這就要用到TypeEvaluator和Interpolators了! 先說Interpolators(插值器):它的總用是怎樣將一個時間分數裝換為一個值分數,兩者的相對關系體現了動畫的形式:勻速、加速、先加速後減速,或者其它的數學變換。怎麼理解講一個時間分數裝換為值分數呢?來看一下接口吧!所以插值器都是TimeInterpolator接口的子類,其中聲明的方法:
abstract float getInterpolation(float input)
input是一個已經過去的時間占總時間的比例,也就是動畫已經完成的百分比。返回的也是個小數,代表了希望這個點完成的總動畫的百分數。假設有這樣一個插值器,它接受0.5,返回0.8。這意味著這個差值器在經過50%的時間時,希望動畫完成了80%。為什麼是希望呢?因為真正映射為屬性值是由TypeEvaluator來完成的。 TypeEvaluator:(翻譯成類型求值器吧!這樣比較貼合它的意思)它完成了差值器返回的值到實際值的映射?或許你會疑問為什麼需要這個值,不應該是下面這個公式嗎?
startVal:起始值 endVal:結束值 percent:完成百分比 progress:當前值 progress = startVal + (endVal-startVal)*progress
對,的確是這樣的。內置的IntEvator、floatEvator也是這樣的,但是我們動畫的類型不一定都是這種數值類型的值吧!我們還可以動畫字符串,在不同的時間點顯示不同的字串,這時上述公式就不成立了。所以TypeEvaluator就是為了達到自定義裝換的需求的。要實現滿足自己需求的TypeEvaluator也很簡單,只需要實現TypeEvaluator接口就可以了,它聲明的方法如:
abstract T evaluate(float fraction, T startValue, T endValue)
T是要動畫的屬性類型,fraction是值分數,startValue、endValue是進行動畫屬性的起始值和終止值。返回映射的中間值。 到這,可以對屬性動畫的工作原理做個簡單的接受了。 動畫需要指定持續時間,屬性起始值,屬性終止值,幀延時。然後每過幀延時的時間間隔,觸發插值器,接著觸發值裝換器,接下來是觸發AnimationUpdateListenr中的onAnimationUpdate()方法重新設置View的屬性,接著View重繪,如此循環知道動畫結束。為了方便,我畫了如下流程圖:
ps:這符圖不是很嚴謹,因為有些內容是不好表達,比如動畫幀的計時是連續的,並不是等到上一幀完成了才開始計數下一幀。其次就是Animator本身的Listener是班法畫上去的,當Animator調用cance(),end()之類的方法是能打斷這個流程的。但是這圖對理解過程還是不錯的。
先來講最常使用的屬性動畫,ObejectAnimator,通過置頂好動畫作用對象個對用屬性等設置值後,ObjectAnimator能夠通過反射區設置對象屬性達到動畫的目的。 ObjectAnimator提供了四種靜態方法來構造自己的對象,分別是:ofInt、ofFloat、ofArgb、ofObject 。它們動畫的值類型正如名字中說的那樣,如ofInt動畫作用於一個int域,offArgb注意與一個帶透明的顏色域。 來看一個用動畫改變欄背景的例子
public class MainActivity extends AppCompatActivity { @Bind(R.id.btn_bg) Button mGradientBg; ObjectAnimator mAnimator; @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final View view = findViewById(android.R.id.content); final Drawable bgd =view.getBackground(); mAnimator = ObjectAnimator.ofArgb(view,"backgroundColor",0x881DDA38,0x88D48AB2); mAnimator.setDuration(5*1000); mAnimator.setRepeatMode(ObjectAnimator.REVERSE); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); view.setBackground(bgd); } }); ButterKnife.bind(this); } @OnClick(R.id.btn_bg) void onGradientActionbarClicked() { mAnimator.start(); } }
效果如下:
上面就是ObjectAnimation使用過程,先通過四個靜態方法構造出一個ObjectAnimation對象,這裡使用的是ofArgb:
static ObjectAnimator ofArgb(Object target, String propertyName, int... values)
ObjectAnimator作用的域在對象是必須有相應的setter,getter(用於省略初始值的情況)方法,否則反射調用不了會報錯。至於沒有setter方法的域如何動畫,官方Guide提供了如下解決方案:
講一下使用包裝類,假設有類A,且A類中有域a,我可以正常操作A.a(訪問和寫值),現在我要動畫A的a域,直接用ObjectAnimator是不行了,因為A類中沒有A.setA()這個方法,那該怎麼辦呢?使用包裝類,寫一個A的包裝類AWrapper,它接受一個A對象來構造自己,然後裡面有一個setter和getter方法來設置和讀取A的a域。現在就可以在AWrapper上使用ObjectAnimator了。
ValueAnimator提供的便利性大大強於ObejectAnimator,因為需要你自己去更新對象的值,ValueAnimator只是為了提供了一個過渡值。為了更新對象的值,你就必須去設置前面講的AnimationUpdateListenr接口。一個簡單的例子來完成ValueAnimator的使用:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); final float X = 0, Y = 500; LogUtil.d("X,Y = "+X+","+Y); mValueAnimator = ValueAnimator.ofFloat(0f, 1f); mValueAnimator.setDuration(3 * 1000); mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mChangelocation.setY(Y * (Float)animation.getAnimatedValue()); mChangelocation.invalidate(); } }); mValueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); mChangelocation.setY(Y); } }); } @OnClick(R.id.btn_cl) void onChangeLocationClicked() { mValueAnimator.start(); } }
效果:
類似於ViewAnimation中的動畫集,參考資料:AnimatorSet
Keyframes實現動畫集:Keyframes
本篇涉及例子下載:Github
dagger2記錄篇,dagger2記錄作為一個碼農,什麼都不用多講,貼代碼 build project build module Application pub
android ndk開發(二)實現一個官方demo,androidndk實現了一個官方的demo:bitmap-plasma(水波紋) 源代碼就在samples文件夾下
谷歌電子市場5--推薦,谷歌電子市場5-- 1.RecommendFragment public class RecommendFragment exten
Android安全開發之ZIP文件目錄遍歷,androidzip1、ZIP文件目錄遍歷簡介 ZIP壓縮包文件中允許存在“../”的字符串,攻擊者可