編輯:關於Android編程
public interface TypeEvaluator< T> { public T evaluate( float fraction, T startValue, T endValue); }2.3 屬性動畫的插值器 屬性動畫的插值器與補間動畫的插值器完全相同 如果覺得現有的插值器不滿足你的需求,就實現TimeInterpolator接口自己寫個插值器。基本上目前定義的插值器已經足夠了。 2.4 使用ValueAnimator實現屬性插值效果 前面我們說過,ValueAnimator不負責屬性動畫第二步,也就是對對象的屬性進行操作,所以下面兩個動作,都是集中在第一步,計算按時間變化的插值序列。 2.4.1 ValueAnimator支持int/float/color的插值動作,相應的接口是ofInt/ofFloat/ofArgb。一個簡單的例子如下:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f); animation.setDuration(1000 ); animation.start();這段代碼表示ValueAnimator在start()執行後的1s內,不斷的生成介於0~1之間的插值。這個插值可以作為某個對象的某個屬性設置到對象裡去。 2.4.2 ValueAnimator支持Object的插值動作,相應接口是ofObject。例子如下: 假設我們有個屬性,類型為:
private static class TestData { float size = 0 ; TestData(float size) { this.size = size; } }我們需要為此對象定義一個TypeEvaluator,如下:
private static class MyTypeEvaluator implements TypeEvaluator然後我們就可以將其放入ValueAnimator使用了:{ @Override public TestData evaluate(float fraction, TestData startValue, TestData endValue) { return new TestData(startValue.size + fraction * (endValue.size - startValue.size)); } }
ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyTypeEvaluator(), new TestData(0), new TestData( 1)); valueAnimator.setDuration(1000 ); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { TestData testData = (TestData) animation.getAnimatedValue(); Log.d( "TEST_PROPERTY_ANIM", "data is " + testData.size); } }); valueAnimator.start();ValueAnimator通過MyTypeEvaluator類(自定義類型估值器)完成TestData(自定義屬性值)的插值工作,並通知到AnimatorUpdateListener接口,這個接口裡可以取到計算出的插值TestData,我們可以根據此值來刷新界面(當然,此Demo內我們只是把值打印出來了,沒有刷新界面)。 2.5 使用ObjectAnimator實現屬性動畫效果 ObjectAnimator繼承自ValueAnimator,ValueAnimator已經封裝了計算隨時間變化的插值屬性值序列的任務,剩下來任務就是將計算好的插值屬性值設置到對象的指定屬性上。ObjectAnimator的目標就是完成剩下來的任務了。 使用ObjectAnimator與使用ValueAnimator非常相似,差別在於,使用ObjectAnimator時需要指定對象和對象屬性的名字(也就是字符串): 比如說我們有一個View對象,對其float型的屬性alpha做透明度0到1的漸顯動畫:
View view = new View(context); ObjectAnimator.ofFloat(view, "alpha", 0f, 1f) .setDuration(1000 ) .start();ObjectAnimator需要依賴以下條件才能正常工作: 第一點,必須要確保指定的屬性在對象內存在setter方法(且是駝峰法命名),上例中View類需要存在setAlpha方法設置透明度值,很幸運,View本來就有此方法。如果我們很不幸的發現並不存在這樣的setter咋辦呢? 下面有幾種辦法: 1)如果我們有權限,就直接去改了對象類,加個setter方法,上例中,如果View沒有setAlpha,我們去View內加個setAlpha。好吧,我們是做不到的,但是google可以,setAlpha方法就是和屬性動畫在API 11一起添加的。 2)如果我們沒辦法改View,那就試著寫個類把它包裹起來。類似下面這樣的:
public class WrappedView extends View { public WrappedView(Context context) { super(context); } public void setAlpha(float alpha) { AlphaAnimation animation = new AlphaAnimation(alpha, alpha); animation.setDuration(0 ); animation.setFillAfter(true); startAnimation(animation); } }API 10以下我們取不到View的TransformationInfo,只能通過動畫來間接實現。 3)如果連包裝都難以做到,就只能用ValueAnimator了。在ValueAnimator回調的onAnimationUpdate做處理。 第二點,如果我們交給接口的values...參數只有一個值,也這樣寫屬性動畫:ObjectAnimator. ofFloat(view, "alpha", 1f),這唯一的一個值1f被認為是動畫的結束值,那動畫的初始值是多少呢?這時候就得靠getter方法返回了。所以View得定義getAlpha方法,沒有getter方法,動畫就不執行。 第三點,getter方法和setter方法必須保持和ObjectAnimator指定的屬性值類型相同。假如有這樣的調用:ObjectAnimator .ofFloat(targetObject, "propName", 1f ),那麼targetObject對象必須存在兩個方法: void setPropName(float value); float getPropName(); 重點就是setter的參數是float,同時getter返回值也必須是float。 第四點,某些情況下我們需要強制調用invalidate/postInvalidate來強制刷新,保證動畫效果顯示在界面上。比如說我們對ImageView.getDrawable的drawable做屬性動畫,這些修改只能在View繪制時才會應用,所以我們要不斷的調用invalidate來重新繪制View;但如果我們調用是View.setAlpha(基本上是所有View的setter方法),此方法會自己刷新界面,就不需要我們調用invalidate了。如果需要調用invalidate,那就應該放在onAnimationUpdate的回調中進行。 2.6 使用動畫集合 在實際應用場景中,可能需要指定動畫與其他動畫一起執行、在其他動畫執行完成後執行或者在指定時間執行,屬性動畫提供了AnimatorSet來管理多個動畫以及他們之間的執行順序關系。可以指定多個動畫一起執行、一個接一個執行或者在指定時間執行,因為AnimatorSet內持有的是抽象Animator類,所以這裡也可以在動畫集合內再套動畫集合,形成復雜的動畫效果。 下面這個例子是google官網的,我就沒有加工了,假設有以下執行順序: 1.執行bounceAnim。 2.隨後同時執行squashAnim1/squashAnim2/stretchAnim1/stretchAnim2。 3.stretchAnim2執行完成後開始執行bounceBackAnim。 4.最後執行fadeAnim。
bouncer.play(bounceAnim).before(squashAnim1); bouncer.play(squashAnim1).with(squashAnim2); bouncer.play(squashAnim1).with(stretchAnim1); bouncer.play(squashAnim1).with(stretchAnim2); bouncer.play(bounceBackAnim).after(stretchAnim2); ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha" , 1f , 0f); fadeAnim.setDuration(250); AnimatorSet animatorSet = new AnimatorSet(); //這裡是為了展示AnimationSet嵌套 animatorSet.play(bouncer).before(fadeAnim); animatorSet.start();2.7 動畫事件監聽 2.7.1 監聽正常的動畫事件如開始/重復/結束/取消應使用Animator.AnimatorListener:
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); objectAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { //動畫開始 } @Override public void onAnimationEnd(Animator animation) { //動畫結束 } @Override public void onAnimationCancel(Animator animation) { //動畫取消,不論動畫如何結束,取消還是正常結束,都會回調onAnimationEnd } @Override public void onAnimationRepeat(Animator animation) { //動畫重復執行 } });如果不想監聽所有的事件,可以使用AnimatorListenerAdapter,這個類實現了AnimatorListener,並提供空實現。 2.7.2 監聽屬性動畫每一次插值刷新界面的動作,使用ValueAnimator.AnimatorUpdateListener
objectAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //屬性動畫開始了新的插值操作 ,動畫每刷新一幀則調用一次 } });在此回調內,可以通過animation.getAnimatedValue()來取得插值的值。如果是使用ValueAnimator,一定需要實現此對象來實現界面刷新,形成動畫效果。 2.8 自定義TypeEvaluator 前面已經寫過一個簡單的TypeEvaluator了,TypeEvaluator的作用是對未知屬性類型的值的插值進行抽象,只有一個接口evaluate需要實現。其輸入是當前的動畫執行時間(經過歸一化和速率變化處理的時間)fraction、屬性開始值startValue和屬性結束值endValue,絕大部分情況下,這裡面的邏輯都是startValue+(endValue - startValue) * fraction; 需要注意一點,fraction已經由外面的插值器(TimeInterpolator)做過加速度變化處理,所以在TypeEvaluator內不需要再考慮速率變化。 舉個系統實現的Float插值的插值器做例子:
public class FloatEvaluator implements TypeEvaluator2.9 使用關鍵幀來做屬性動畫 一個關鍵幀包含一個“時間/屬性值”對,可以此來指定一個動畫在特定時間(關鍵幀中的時間)處於的特殊狀態(關鍵幀 中的屬性值),每個關鍵幀可以包含自己的插值器,此插值器的影響范圍是上一個關鍵幀的時間到當前關鍵幀的時間內動畫的行為。 我們可以用KeyFrame的ofInt()、ofFloat()或者ofObject()來實例化一個關鍵幀對象(注意,沒有ofRgba)。然後通過PropertyValuesHolder.ofKeyframe來根據多個(至少兩個,一個就沒動畫效果了)關鍵幀生成一個PropertyValuesHolder,有了這個對象後,我們就可以通過ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)生成一個屬性動畫了。大概代碼如下:{ public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); } }
Keyframe kf0 = Keyframe. ofFloat(0f , 0f ); Keyframe kf1 = Keyframe.ofFloat(.5f, 360f); Keyframe kf2 = Keyframe.ofFloat(1f, 0f); PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2); ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(view, pvhRotation); rotationAnim.setDuration(5000 );2.10 PropertyValuesHolder使用 前面已經有過一個PropertyValuesHolder的使用舉例了,其實PropertyValuesHolder的作用就是持有一個屬性和它的開始值結束值,一個ObjectAnimator可以同時對多個屬性進行操作,如果一個個寫代碼就太難看了,這時候我們可以通過PropertyValuesHolder來先枚舉所有的屬性和它們的開始結束值,然後將所有的PropertyValuesHolder一次性傳入ObjectAnimator構造中,生成一個屬性動畫。 PropertyValuesHolder支持的屬性類型包括:float/int/keyframe/Object,對應的接口是ofFloat/ofInt/ofKeyframe/ofObject等。 舉個例子看看其使用:
PropertyValuesHolder valuesHolderX = PropertyValuesHolder.ofFloat("translationX", view.getWidth() * 0.5f, view.getWidth() * 1.5f ); PropertyValuesHolder valuesHolderY = PropertyValuesHolder.ofFloat("translationY", view.getHeight() * 0.5f, view.getHeight() * 1.5f); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, valuesHolderX, valuesHolderY);2.11ViewPropertyAnimator使用 ViewPropertyAnimator提供一種簡單的並行操作View的幾種屬性形成屬性動畫的方案。它在操作View屬性時比ObjectAnimator效率更高一點,代碼可讀性更高些。ViewPropertyAnimator雖然以Animator命名,但其不繼承Animator,而是調用ValueAnimator完成相關操作。其支持的操作有:translationX、translationY、translationZ、scaleX、scaleY、rotation、rotationX、rotationY、x、y、z、alpha。 下面是官網的一個例子對比,看一下就明白差別了: 同時操作屬性,改變View的位置: 1)多個ObjectAnimator
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f ); ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f ); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();2)一個ObjectAnimator
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder( myView, pvhX, pvhY).start();3)使用ViewPropertyAnimator
myView.animate().x(50f ).y(100f );三、屬性動畫實現補間動畫效果 之前我們已經討論過屬性動畫與補間動畫的區別,以及屬性動畫相對於補間動畫的優勢。我們再強調一遍,補間動畫改變的是View的繪制方式與位置,而不改變View的真實位置(影響draw過程而非layout過程),原因是相關的操作(平移旋轉縮放)都是由View的父元素完成的,View自身並無操作方法來完成這些動作。結果就是可能View已經發生了動畫變化,但自身並無改變,比如說,View在屏幕左邊繪制,經過動畫到屏幕右邊繪制,這是View布局位置並未變化,相應點擊的區域還在左邊區域。Android3.0(API11)增加了相應的新屬性和setter/getter方法來解決這個問題。 屬性動畫可以通過改變View的屬性來真正的改變View的位置,同時當View的屬性發生變化時,View會自動調用invalidate來刷新界面。View在3.0增加了以下屬性來支持屬性動畫: translationX/translationY:這兩個變量輔助控制View的位置,它們是真實位置與父元素設置布局位置的差值。假設View的左坐標是left,上坐標是top,這兩個值是View的容器設置的,在運行過程中不會發生改變,真實的View位置在left + translationX,top+translationY的位置。 rotation/rotationX/rotationY:這些屬性控制View繞中心點的2D和3D旋轉效果,其中2D效果由rotation表示。 scaleX/scaleY:控制View繞中心點的2D縮放效果。 pivotX/pivotY:控制View的中心點位置,縮放和旋轉動畫基於此位置來執行動畫,默認在View中心位置。 x/y:描述View最終在其容器內的位置,如上所述,x = left + translationX, y = top + translationY。 alpha:表示View的透明度,1不透明,0全透明(不可見)。 要想用屬性動畫實現補間動畫的效果,其實只需要創建屬性動畫,並指定上面這些屬性即可: 以下這些效果,如果希望啟動Activity就執行,應寫在Activity的onWindowFocusChanged內。同時,以下效果都是在3.0以上才能運行,因為這些屬性和屬性動畫本身都是3.0以上才有的。 3.1 平移效果 舉例:x軸從自身位置50%向右平移相對於自己寬度100%的距離,y軸從自身位置50%向下平移相對於自己100%高度的距離,時間1s,先加速再減速:
PropertyValuesHolder valuesHolderX = PropertyValuesHolder.ofFloat("translationX", view.getWidth() * 0.5f, view.getWidth() * 1.5f ); PropertyValuesHolder valuesHolderY = PropertyValuesHolder.ofFloat("translationY", view.getHeight() * 0.5f, view.getHeight() * 1.5f); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, valuesHolderX, valuesHolderY); animator.setDuration(1000); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start();3.2 縮放效果 舉例:x軸從自身50%縮放到自身200%,y軸從自身50%縮放到自身200%,中心點(10%,10%)時間1s,先加速再減速:
PropertyValuesHolder valuesHolderX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1f); PropertyValuesHolder valuesHolderY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 2f ); //兩個中心點其實可以用set方法直接設置,mView.setPivotX(pivotX) float pivotX = mView.getWidth() * 0.1f; float pivotY = mView .getHeight() * 0.1f; PropertyValuesHolder valuesHolderPvX = PropertyValuesHolder.ofFloat("pivotX", pivotX, pivotX); PropertyValuesHolder valuesHolderPvY = PropertyValuesHolder.ofFloat("pivotY", pivotY, pivotY); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder( mView, valuesHolderX, valuesHolderY, valuesHolderPvX, valuesHolderPvY); animator.setDuration(1000); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start();3.3 旋轉效果 舉例:View繞自身中心點(10%,10%)位置,從-270旋轉到180,時間1s,先加速再減速 :
PropertyValuesHolder valuesHolderX = PropertyValuesHolder.ofFloat("rotation", - 270f, 180f); //兩個中心點其實可以用set方法直接設置,mView.setPivotX(pivotX) float pivotX = mView .getWidth() * 0.1f; float pivotY = mView .getHeight() * 0.1f; PropertyValuesHolder valuesHolderPvX = PropertyValuesHolder.ofFloat("pivotX", pivotX, pivotX); PropertyValuesHolder valuesHolderPvY = PropertyValuesHolder.ofFloat("pivotY", pivotY, pivotY); ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder( mView, valuesHolderX, valuesHolderPvX, valuesHolderPvY); animator.setDuration(1000); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start();3.4 Alpha效果 舉例:View從透明到完全不透明,時間1s,先加速再減速:
ObjectAnimator animator = ObjectAnimator.ofFloat(mView, "alpha", 0f , 1f ); animator.setDuration(1000); animator.setInterpolator(new AccelerateDecelerateInterpolator()); animator.start();四、使用XML定義屬性動畫 在XML中定義屬性動畫更容易在多個Activity中復用,且更容易編輯動畫順序。考慮到屬性動畫使用了新的屬性動畫API,為了與補間動畫的動畫文件區分開,在Android 3.1後,屬性動畫的XML動畫文件定義在res/animator/文件夾中。 屬性動畫各接口與XML tag對應關系為: ValueAnimator對應; ObjectAnimator對應 AnimatorSet對應
這裡有兩個動畫集合,外層的動畫集合嵌套內層的動畫集合,外層動畫集合有兩個子元素,它們按照android:ording的要求順序播放,子集合(xy變化)執行完成後再執行下面的alpha動畫。子集合內,x的屬性動畫和y的屬性動畫一起執行。 XML的動畫文件定義好後,我們需要將其加載成Java對象使用,並設置各動畫的target,具體方法如下:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.anim.property_animator); set.setTarget(myObject); set.start();五、屬性動畫實現ViewGroup內Layout變化動畫 屬性動畫對處理ViewGroup內子元素變化導致的動畫行為提供了非常好的支持。使用LayoutTransition類來處理ViewGroup元素的變化動畫。通過給ViewGroup設置LayoutTransition,View從ViewGroup內添加/移除/變的可見(setVisibility(VISIBLE))/變的不可見(setVisibility(GONE))等情況都可以執行一個顯示或隱藏的動畫。當添加/刪除某個View時,ViewGroup內其他的View也可以展示挪到新位置的動畫。 具體設置方法如下:
LayoutTransition transition = new LayoutTransition(); transition.setAnimator(LayoutTransition.APPEARING, objectAnimatorApearing); transition.setAnimator(LayoutTransition.CHANGE_APPEARING, objectAnimatorChangeAppearing); transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, objectAnimatorChangeDisappearing); transition.setAnimator(LayoutTransition.CHANGING, objectAnimatorChanging); transition.setAnimator(LayoutTransition.DISAPPEARING, objectAnimatorDisappearing); ViewGroup group = new LinearLayout(context); group.setLayoutTransition(transition);對應的常量意義為: APPEARING:當View在容器內展示出來時顯示的動畫; CHANGE_APPEARING:當View在容器內展示出來時,其他被影響了的View的動畫; DISAPPEARING:當View從容器內消失時展示的動畫; CHANGE_DISAPPEARING:當View從容器內消失時,其他被影響了的View的動畫; CHANGING:當不是View添加/移除導致的容器重新布局(Layout Change)時,所有被影響了的View的動畫;這個屬性並不是自動默認開啟的,需要通過transition.enableTransitionType(LayoutTransition. CHANGING)來開啟; 如果不通過setAnimator設置而通過enableTransitionType開啟動畫效果的話,LayoutTransition對這些情況的動畫都支持使用默認值。 在XML內將android:animateLayoutchanges置為true就可以開啟ViewGroup的layout transitions。如下:
六、其他注意要點 1.Android屬性動畫只能支持Android3.0以上版本,想要支持3.0以前的版本,需要使用NineOldAndroids包。 七、總結 本文總結了屬性動畫的使用方法,Android屬性動畫相對於補間動畫而言,的確是發生了質的變化,整個框架的抽象性設計非常合理,擴展性也非常強。在實際使用過程中,如果動畫很簡單,而且沒有文中提到的補間動畫的坑(View顯示位置與布局位置不同),可以考慮使用補間動畫,如果動畫比較復雜,建議使用屬性動畫。
華為榮耀於8月1號下午正式發布了6.6吋大屏手機華為榮耀NOTE8,那麼想要購買新機的朋友是不是很想知道華為榮耀note8怎麼預約購買呢?下面小編就馬上帶來
Socket Android手機客戶端與PC服務端局域網內聯測試,筆者采用的是 PC服務器,Android平板客戶端 ,PC模擬器客戶端, 前段時間為了加深對Socket
Android UI組件進階(1)——帶進度條的按鈕 本節引言: 這個系列是繼Android UI組件實例大全後的進階
如圖所示為程序效果動畫圖地圖滾動的原理在本人之前博客的文章中介紹過人物在屏幕中的移動方式,因為之前拼的游戲地圖是完全填充整個手機屏幕的,所以無需處理地圖的平滑滾動。這篇文