Android之动画精讲一:从setTranslationX谈属性动画和view动画的区别
轉載:http://blog.csdn.net/yanzi1225627/article/details/47850471
最近又用到了動畫,決定把幾次項目里用到的動畫走過的彎路總結一下,順便梳理下android的動畫體系。眾所周知,android動畫分三類:一是View 動畫,又叫Tween動畫,二是frame 動畫(幀動畫),又叫drawable 動畫,三是屬性動畫,即property animation.?
? ? 轉載地址:http://blog.csdn.net/yanzi1225627/article/details/47850471 ??
? ? View動畫,根據作用又分為縮放動畫ScaleAnimation/移位動畫TranslateAnimation / 透明度動畫AlphaAnimation / 旋轉動畫RotateAnimation,這四個動畫都繼承android.view.animation下的Animation類。繼承Animation的除了這四個類外,還有AnimationSet,關系圖如下所示:?
 
?幀動畫 對應AnimationDrawable類,繼承自DrawableContainer,通過加載多個Drawable來一幀一幀播放達到動畫效果。盡管很多人覺得這個不值一提,但是某些動畫效果,如顯示個小羊吃草還必須得用這個動畫。?
 ? ? ? ?接下來進入正題談屬性動畫,該動畫從android3.0引入,API11引入,是為了彌補view動畫的不足。正式項目里用的話為了兼容android2.3可以用NineOldAndroids,直接將生成的jar包放進去就ok了。?
 ? ? ? ?屬性動畫都在android.animation包下,基類是Animator類,子類為ValueAnimator和AnimatorSet(作用同view動畫的AnimationSet相同),ValueAnimator的子類有ObjectAnimator和TimeAnimator,一般我們用屬性動畫ObjectAnimator就ok了。不妨簡單對比下和view動畫架構上的異同:?
 ? ? ? ?View動畫,包名android.view.animation,基類為Animation,核心子類為TranslateAnimation,ScaleAnimation,AlphaAnimation,RotateAnimation及AnimationSet。?
 ? ? ? ?Property動畫,包名android.animation,基類為Animator,核心子類為AnimatorSet,ValueAnimator,ObjectAnimator,TimeAnimator。?
 ? ? ? ?在詳細對比屬性動畫和view動畫前,先介紹個函數setTranslationX和setTranslationY,api版本為11,是設置view相對原始位置的偏移量,正式項目用的話考慮到兼容api11之前的用nineoldandroids里的ViewHelper即可。
 
public void setTranslationX (float translationX)Added in API level 11 Sets the horizontal location of this view relative to its left position. This effectively positions the object post-layout, in addition to wherever the object's layout placed it.Related XML Attributes android:translationX Parameters translationX The horizontal position of this view relative to its left position, in pixels. 上面是api介紹,即相對left position的偏移,所謂left position也即getLeft(),同時可以在xml里直接用android:translationX進行設置。關于view的位置,我們最常用的莫過于android:layoutMargin這一套,用來設置相對父布局的偏移,在java代碼里可以通過新建或更新view的LayoutParams進行修改,如下所示:
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)text.getLayoutParams();params.leftMargin = 0;params.rightMargin = 0;params.setMargins(0, 0, 0, 0);text.setLayoutParams(params); ?之所以說有時需要新建Params而有時候需要更新,是因為有時候從view取來的params是空的,這個日后開篇文章專門談這個問題。總之,通過view的LayoutParams設置margin最終影響了view的位置,這個同時會改變view的getLeft/getRight等變量。但通過setTranslationX改變view的位置,是不改變view的LayoutParams的,也即不改變getLeft等view的信息。 但他確實改變了view的位置,這一點可以通過獲取其在window或screen的坐標,或通過getLocationInWindow及如下所示的api等到view的精確位置:
text.getLocationInWindow(pos);text.getLocationOnScreen(pos);text.getLocalVisibleRect()text.getGlobalVisibleRect()
 總結:?
 1,setTranslationX改變了view的位置,但沒有改變view的LayoutParams里的margin屬性;?
 2,它改變的是android:translationX 屬性,也即這個參數級別是和margin平行的。
 ? ? ? ?下面來看這個例子,通過點擊按鍵讓一個view從最左邊移動到屏幕的最右邊,分別用view的TranslateAnimation和屬性動畫來實現。?
 布局代碼:
MainActivity.java
package com.example.yanzi.myapplication;import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.View; import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast;import com.nineoldandroids.animation.Animator; import com.nineoldandroids.animation.ObjectAnimator; import com.nineoldandroids.view.ViewHelper; import com.yanzi.util.UiUtil;public class MainActivity extends ActionBarActivity implements View.OnClickListener{private static final String TAG = "YanZi";Button btn_start_anim;Button btn_reset_pos;Button btn_start_anim2;TextView text;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();initUI();}private void initData(){UiUtil.initialize(getApplicationContext());}private void initUI(){btn_start_anim = (Button)findViewById(R.id.btn_start_anim);btn_start_anim.setOnClickListener(this);btn_start_anim2 = (Button)findViewById(R.id.btn_start_anim2);btn_start_anim2.setOnClickListener(this);btn_reset_pos = (Button)findViewById(R.id.btn_reset_pos);btn_reset_pos.setOnClickListener(this);text = (TextView)findViewById(R.id.text);text.setOnClickListener(this);LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)text.getLayoutParams();params.leftMargin = 0;params.rightMargin = 0;params.setMargins(0, 0, 0, 0);text.setLayoutParams(params);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.btn_start_anim:playAnim1();break;case R.id.btn_start_anim2:playAnim2();break;case R.id.btn_reset_pos:resetPos();break;case R.id.text:printParams();break;default:break;}}public void printParams(){LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)text.getLayoutParams();if(params != null){String s = "leftMargin = " + params.leftMargin + " rightMargin = " + params.rightMargin+ " getLeft = " + text.getLeft() + " getRight = " + text.getRight() + " getWidth = " + text.getWidth();Log.i(TAG, s);int[] pos = new int[2];text.getLocationInWindow(pos);Log.i(TAG, "location, x = " + pos[0] + " y = " + pos[1]);Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();}}private void playAnim1(){int w = text.getWidth();int screenW = UiUtil.getScreenWidth();int transX = screenW - w;ObjectAnimator transAnim = ObjectAnimator.ofFloat(text, "translationX", 0, transX);transAnim.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animator) {}@Overridepublic void onAnimationEnd(Animator animator) {}@Overridepublic void onAnimationCancel(Animator animator) {}@Overridepublic void onAnimationRepeat(Animator animator) {}});transAnim.setDuration(300);transAnim.start();;}private void playAnim2(){int w = text.getWidth();int screenW = UiUtil.getScreenWidth();int transX = screenW - w;TranslateAnimation transAnim = new TranslateAnimation(0, transX, 0, 0);transAnim.setDuration(300);text.setAnimation(transAnim);transAnim.start();}private void resetPos(){ViewHelper.setTranslationX(text, 0);} } 用到了一個輔助類獲得屏幕的寬高和dip轉px:
package com.yanzi.util;import android.content.Context; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ListAdapter; import android.widget.ListView;public class UiUtil {private static final String TAG = "YanZi_UiUtil";private static int screenWidth = 0;private static int screenHeight = 0;private static float screenDensity = 0;private static int densityDpi = 0;private static int statusBarHeight = 0;public static void initialize(Context context){if (context == null)return;DisplayMetrics metrics = new DisplayMetrics();WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);wm.getDefaultDisplay().getMetrics(metrics);screenWidth = metrics.widthPixels; // 屏幕寬度screenHeight = metrics.heightPixels; // 屏幕高度screenDensity = metrics.density; // 0.75 / 1.0 / 1.5 / 2.0 / 3.0densityDpi = metrics.densityDpi; //120 160 240 320 480Log.i(TAG, "screenDensity = " + screenDensity + " densityDpi = " + densityDpi);}public static int dip2px(float dipValue){return (int)(dipValue * screenDensity + 0.5f);}public static int px2dip(float pxValue){return (int)(pxValue / screenDensity + 0.5f);}public static int getScreenWidth() {return screenWidth;}public static int getScreenHeight() {return screenHeight;}}
大概說下里面核心的幾個函數:?
1,使用view動畫TranslateAnimation:
private void playAnim2(){int w = text.getWidth();int screenW = UiUtil.getScreenWidth();int transX = screenW - w;TranslateAnimation transAnim = new TranslateAnimation(0, transX, 0, 0);transAnim.setDuration(300);text.startAnimation(transAnim);} 2,使用屬性動畫移位:
private void playAnim1(){int w = text.getWidth();int screenW = UiUtil.getScreenWidth();int transX = screenW - w;ObjectAnimator transAnim = ObjectAnimator.ofFloat(text, "translationX", 0, transX);transAnim.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animator) {}@Overridepublic void onAnimationEnd(Animator animator) {}@Overridepublic void onAnimationCancel(Animator animator) {}@Overridepublic void onAnimationRepeat(Animator animator) {}});transAnim.setDuration(300);transAnim.start();;} 3,點擊text打印它的坐標:
public void printParams(){LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)text.getLayoutParams();if(params != null){String s = "leftMargin = " + params.leftMargin + " rightMargin = " + params.rightMargin+ " getLeft = " + text.getLeft() + " getRight = " + text.getRight() + " getWidth = " + text.getWidth();Log.i(TAG, s);int[] pos = new int[2];text.getLocationInWindow(pos);Log.i(TAG, "location, x = " + pos[0] + " y = " + pos[1]);Toast.makeText(getApplicationContext(), s, Toast.LENGTH_LONG).show();}}
4,使用屬性動畫后如果想復位:
private void resetPos(){ViewHelper.setTranslationX(text, 0);}
直接將translationX設為0即可,而不是上次偏移量的相反數。正因為如此,重復點擊屬性動畫,看到view每次都從最左邊到最右邊,并最終停在最右邊。因為屬性動畫的執行過程就是setTranslationX(0), 1, 2, 3, 4,……..N的過程,所以才會有看到的效果。
? ? ? ?另外,可以看到使用view的TranslateAnimation動畫播放完畢后,view瞬間又回到了原點;而使用屬性動畫移位后view位置確實發生了改變。但LayoutParams里的margin和getLeft信息并未改變。有沒有辦法讓view的TranslateAnimation播放完畢后,停在那個地方呢?
? ? ? ?肯定是有,加上這句話:transAnim.setFillAfter(true);之后運行發現view確實停在了屏幕的右側,但是點擊右側的textview并沒有觸發打印參數的函數,而點擊textview的初始位置才觸發。所以它并沒有改變view的位置,僅僅是繪制在了屏幕的右側。因此,如果使用view動畫但又想真正改變view位置需要如下代碼:
private void playAnim2(){int w = text.getWidth();int screenW = UiUtil.getScreenWidth();int transX = screenW - w;TranslateAnimation transAnim = new TranslateAnimation(0, transX, 0, 0);transAnim.setDuration(300); // transAnim.setFillAfter(true);transAnim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {updateParams();}@Overridepublic void onAnimationRepeat(Animation animation) {}});text.startAnimation(transAnim);}private void updateParams(){int w = text.getWidth();int screenW = UiUtil.getScreenWidth();LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) text.getLayoutParams();params.leftMargin = screenW - w;text.setLayoutParams(params);} ?即使用LayoutParams在動畫結束后設置下就ok了,這樣也能達到屬性動畫改變view的位置的效果。view 動畫+updateParams 約等于property動畫效果。?
? ? ? ?但是切忌,使用view動畫+updateParams策略時,務必注意不要使用transAnim.setFillAfter(true);這句話,先看看setFillAfter的api:
If fillAfter is true, the transformation that this animation performed will persist when it is finished. Defaults to false if not set. Note that this applies to individual animations and when using an AnimationSet to chain animations.Related XML Attributes android:fillAfter Parameters fillAfter true if the animation should apply its transformation after it ends
 如果為true,動畫結束后關于view的變換會一直存在。在view動畫+updateParams+transAnim.setFillAfter(true)這種策略下,view最終的繪制位置等于將view先updateParams后在新的位置基礎上,再進行動畫移位,一般情況下這并不是我們想要的!?
 ? ? ? ?基本上可以這么說,如果需要view位置真正改變setFillAfter一定不要設!?
 ? ? ? ?時間原因,很多東西只有下次再寫了,關于屬性動畫和view動畫詳細對比可以參考官方文檔里How Property Animation Differs from View Animation這一段,見后文。
? ? ? ?總之,要知其然并知其所以然,不要一味否定view動畫而肯定屬性動畫。很多多個界面間的復雜效果非view動畫不可,用屬性動畫只能掉坑里,我是兩種坑都掉過。如果想改變動畫后view的屬性,如位置,可以用屬性動畫也可以用view動畫+updateParams,當然前者更省事。在有些情況下,僅僅是想得到動畫的呈現,動畫結束后的位置就是view的初始位置,如view從一個地方飛過來,動畫結束時view的位置就是view的位置時,此時view動畫最合適!
he view animation system provides the capability to only animate View objects, so if you wanted to animate non-View objects, you have to implement your own code to do so. The view animation system is also constrained in the fact that it only exposes a few aspects of a View object to animate, such as the scaling and rotation of a View but not the background color, for instance.Another disadvantage of the view animation system is that it only modified where the View was drawn, and not the actual View itself. For instance, if you animated a button to move across the screen, the button draws correctly, but the actual location where you can click the button does not change, so you have to implement your own logic to handle this.With the property animation system, these constraints are completely removed, and you can animate any property of any object (Views and non-Views) and the object itself is actually modified. The property animation system is also more robust in the way it carries out animation. At a high level, you assign animators to the properties that you want to animate, such as color, position, or size and can define aspects of the animation such as interpolation and synchronization of multiple animators.The view animation system, however, takes less time to setup and requires less code to write. If view animation accomplishes everything that you need to do, or if your existing code already works the way you want, there is no need to use the property animation system. It also might make sense to use both animation systems for different situations if the use case arises.文中測試代碼下載: http://download.csdn.net/detail/yanzi1225627/9034125 ?
--—–--本文系原創,轉載注明作者yanzi1225627
 
 
 
 
 
 
 
總結
以上是生活随笔為你收集整理的Android之动画精讲一:从setTranslationX谈属性动画和view动画的区别的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Android之可以做的两件坏事---破
 - 下一篇: Hibernate之悲观锁与乐观锁