Android之动画
Android的動畫可以分為三種,View動畫、幀動畫、屬性動畫。View動畫通過對場景里的對象不斷做圖像變化(平移、縮放、旋轉、透明度)從而產生動畫效果,它是一種漸進式動畫,而且View動畫支持自定義。幀動畫可以理解為圖片切換動畫(如果圖片過多,會導致OOM)。屬性動畫為API11的新特性,屬性動畫通過通過動態地改變對象的屬性從而達到動畫效果。
- View動畫
View動畫的種類:View動畫的四種變換效果對應著Animation的四個子類。TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation。這四種動畫可以通過XML來定義,也可以通過代碼動態創建。對于View動畫來說,建議XML來定義,這是因為XML格式的動畫可讀性好。
| 名稱 | 標簽 | 子類 | 效果 |
| 平移動畫 | <translate> | TranslateAnimation | 移動View |
| 縮放動畫 | <scale> | ScaleAnimation | 放大或縮小View |
| 旋轉動畫 | <rotate> | RotateAnimation | 旋轉View |
| 透明度動畫 | <alpha> | AlphaAnimation | 改變View的透明度 |
?
?
?
?
?
?
?
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:shareInterpolator="true"android:duration="1000"android:fillAfter="false"><alpha android:fromAlpha="float"android:toAlpha="float" /><scale android:fromXScale="float"android:toXScale="float"android:fromYScale="float"android:toYScale="float"android:pivotX="float"android:pivotY="float" /><translate android:fromXDelta="float"android:toXDelta="float"android:fromYDelta="float"android:toYDelta="float" /><rotate android:fromDegrees="float"android:toDegrees="float"android:pivotX="float"android:pivotY="float" /> </set>View動畫可以是單個動畫,也可以是一系列動畫組成。<set>表示動畫集合,對應AnimationSet類。
android:interpolator:表示動畫集合所使用的插值器,插值器影響動畫的速度,比如非勻速動畫就需要通過插值器來控制動畫的播放速度。
android:shareInterpolator:表示集合中的動畫是否共享同一個插值器。如果不指定插值器,那么子動畫就需要單獨指定所需要的插值器或者使用默認值。
AlphaAnimation anim = (AlphaAnimation) AnimationUtils.loadAnimation(this, R.anim.anim_test); TextView tv = findViewById(R.id.tv); tv.startAnimation(anim);除了XML定義動畫之外,還可以使用代碼創建動畫。
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1); alphaAnimation.setDuration(300); TextView tv = findViewById(R.id.tv); tv.startAnimation(alphaAnimation);?
?注意:通過Animation的setAnimationListener的方法可以給View動畫添加過程監聽。如適用于帶動畫的啟動頁,監聽動畫完成后進入MainActivity。
anim.setAnimationListener(new Animation.AnimationListener() {@Overridepublic void onAnimationStart(Animation animation) {}@Overridepublic void onAnimationEnd(Animation animation) {}@Overridepublic void onAnimationRepeat(Animation animation) {} });?
自定義View動畫:
自定義View動畫是一件簡單而復雜的事情。說簡單是:因為派生一種新動畫只需要集成Animation這個抽象類,然后重寫它的initialize和applyTransformation方法。在initialize中進行初始化工作,在applyTransformation中進行相應的矩陣變換即可。很多時候需要Camera來簡化矩陣變換過程。說復雜,是因為需要運用到矩陣變換的過程,使用到數學上的概念。之后再做相關的分析講解。
?
- 幀動畫
幀動畫順序播放一組預先定義好的圖片,類似電影播放。不同于View動畫,系統提供另外一個類AnimationDrawable來使用幀動畫。幀動畫的使用比較簡單,先使用XML來定義一個AnimationDrawable。在drawable目錄下創建:
<?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="false"><item android:drawable="@drawable/img1" android:duration="500"/><item android:drawable="@drawable/img2" android:duration="500"/><item android:drawable="@drawable/img3" android:duration="500"/> </animation-list>將上述的Drawable作為View的背景并通過Drawable來播放動畫。
TextView tv = findViewById(R.id.tv); tv.startAnimation(alphaAnimation); AnimationDrawable animationDrawable = (AnimationDrawable) tv.getBackground(); animationDrawable.start();注意幀動畫比較容易引起OOM,所以避免使用尺寸較大的圖片。
?
- View動畫的特殊場景使用
在一些特殊場景下,如ViewGroup中控制子元素的出場效果,在Activity中實現不同Activity之間的切換效果。
?
LayoutAnimation作用于ViewGroup,作為ViewGroup指定一個動畫,這樣當它的子元素出場時都具有這樣的效果。常見的ListView的Item的出場以一定的動畫效果出現。
定義LayoutAnimation如下:
//res/anim/anim_layout.xml<?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"android:delay="0.5"android:animationOrder="normal"android:animation="@anim/anim_item" />
delay表示子元素開始動畫的時間延遲。比如說元素入場動畫周期為300ms,那么0.5表示每個子元素都需要延遲150ms才能播放入場動畫。第一個子元素延遲150ms開始播放入場動畫,第二個子元素延遲300m開始播放入場,以此類推。
animationOrder:子元素動畫順序,normal, reverse和random。
android:animation: 為元素指定具體的入場動畫。
為ViewGroup指定android:layoutAnimation屬性,對于ListView而言,指定之后就是Item的出場動畫了。除了XML中指定LayoutAnimation之外,還可以通過LayoutAnimationController來實現,具體如下:
ListView listView = findViewById(R.id.list_view); Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item); LayoutAnimationController controller = new LayoutAnimationController(animation); controller.setDelay(0.5f); controller.setOrder(LayoutAnimationController.ORDER_NORMAL); listView.setLayoutAnimation(controller);?
?
Activity的切換效果:
Activity有默認的切換效果,但是這個效果是可以自定義的,主要用到overridePendingTransition(int enterAnim, int exitAnim)這個方法,這個必須在startActivity(intent)和finish()之后被調用才能生效,它的參數含義如下:
enterAnim - Activity被打開時,所需要的動畫資源id
exitAnim - Activity被暫停時,所需要的動畫資源id
當啟動一個Activity時,可以按照如下方式為其添加自定義的切換效果:
Intent intent = new Intent(this, TestActivity.class); startActivity(intent); overridePendingTransition(R.anim.enter_anim, R.anim.exitAnim);?
當Activityt退出時,也可以為其指定自定義的切換效果:
@Override public void finish() {super.finish();overridePendingTransition(R.anim.enter_anim, R.anim.exitAnim); }?
Fragment也可以添加切換效果,可以通過FragmentTransaction中的setCustomAnimations()方法來添加切換動畫,這個切換動畫需要時View動畫。還有其他的方法為Activity和Fragment添加切換動畫,但是需要考略兼容為題。
?
- 屬性動畫
屬性動畫是API11新加入的特性,和View動畫不同,它對作用的對象進行擴展,屬性動畫可以對任何對象做動畫,甚至還可以沒有對象。屬性動畫可以對任意對象的屬性進行動畫而不僅僅是View。屬性動畫中有ValueAnimator, ObjectAnimator和AnimatorSet等概念。為兼容之前的版本,采用nineoldandroids源動畫庫。
ObjectAnimator繼承自ValueAnimator,AnimatorSet是動畫集合。屬性動畫需要定義在res/animator/目錄下。
屬性動畫除了代碼實現之外,還可以通過XML來定義。
eg.
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android"><objectAnimator android:propertyName="x"android:duration="300"android:valueTo="300"android:valueType="intType" /><objectAnimator android:propertyName="y"android:duration="300"android:valueTo="300"android:valueType="intType"/> </set>加載上面的屬性動畫:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.property_animator); set.setTarget(view); set.start();建議:屬性動畫采用代碼來實現,這是因為代碼的方式會比較簡單,更重要的是,在一個屬性起始值無法確定的情況下,將無法將屬性定義在xml文件中。
- 插值器和估值器
?TimeInterpolater:時間插值器,它的作用是根據時間流逝的百分比來計算出當前屬性改變的百分比,系統預置的有LinearInterpolator(線性插值器:勻速動畫)、AccelerateDecelerateInterpolator(加速減速插值器:動畫兩頭慢,中間快),DeceleratrInterpolator(減速插值器:動畫越來越慢)等。
TypeEvaluator:類型估值算法,也叫做估值器。它的作用是根據當前屬性改變的百分比來計算改變后的屬性值,系統預置的有IntEvaluator(針對整型屬性),FloatEvaluator(針對浮點型屬性),ArgbEvaluator(針對Color屬性)。
?
線性差值器,屬于勻速動畫,其源碼為:
/*** An interpolator where the rate of change is constant*/ @HasNativeInterpolator public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {public LinearInterpolator() {}public LinearInterpolator(Context context, AttributeSet attrs) {}public float getInterpolation(float input) {return input; /** 輸入值和返回值一樣 */}/** @hide */@Overridepublic long createNativeInterpolator() {return NativeInterpolatorFactoryHelper.createLinearInterpolator();} }那到底輸入數值是什么?這需要估值算法來確定,如整型估值器的源碼為:
public class IntEvaluator implements TypeEvaluator<Integer> {/*** This function returns the result of linearly interpolating the start and end values, with* <code>fraction</code> representing the proportion between the start and end values. The* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,* and <code>t</code> is <code>fraction</code>.** @param fraction The fraction from the starting to the ending values* @param startValue The start value; should be of type <code>int</code> or* <code>Integer</code>* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>* @return A linear interpolation between the start and end values, given the* <code>fraction</code> parameter.*/public Integer evaluate(float fraction, Integer startValue, Integer endValue) {int startInt = startValue;return (int)(startInt + fraction * (endValue - startInt));} }evaluate方法的三個參數分別是估值小數,起始值,結束值。例子:勻速動畫,對象從0位置到100,當估值小數位0.5的時候,根據算法result = 0 + 0.5(100 - 0) = 50。
屬性動畫要求對象有該屬性的set方法和get方法(可選)。除了系統預置的插值器和估值算法外,我們還可以自定義。自定義差值算法需要實現Interpolator或TimeInterpolator;自定義估值算法需要實現TypeEvaluator。
屬性動畫的監聽:
有兩個常用的屬性動畫監聽:AnimatorListener, AnimatorUpdateListener
ObjectAnimator animator = new ObjectAnimator(); animator.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {} });從AnimatorListener的定義來看,它可以監聽動畫的開始,結束,取消和重復動畫。
public static interface AnimatorUpdateListener {/*** <p>Notifies the occurrence of another frame of the animation.</p>** @param animation The animation which was repeated.*/void onAnimationUpdate(ValueAnimator animation);}AnimatorUpdateListener比較特殊,它會監聽整個動畫過程,動畫是由許多幀組成的,每播放一幀onAnimationUpdate()就會被調用一次,利用這個特征,我們可以做一些特殊事情。
?
- 對任意屬性做動畫
?例如TextView的寬度動畫變為800的動畫,當layout_width的屬性為wrap_content時,是有動畫的,但是如果設置為固定值之后,下面的屬性動畫就沒有效果了。
//高度從原來的默認值變為800 ObjectAnimator objectAnimator = ObjectAnimator.ofInt(textView, "width", 800); objectAnimator.setDuration(500); objectAnimator.start();?
?這是因為:
public void setWidth(int pixels) {mMaxWidth = mMinWidth = pixels;mMaxWidthMode = mMinWidthMode = PIXELS;requestLayout();invalidate(); } public final int getWidth() {return mRight - mLeft;}setWidth不是所謂意義上的寬度設置,所以就不會產生動畫。對任意屬性做動畫,一般需要有set和get方法,設置流程如下:
?如果沒有屬性設置獲取權限,可以定義一個包裹類進行封裝,實現set和get方法;如:
public static class ViewWrapper{private View mTarget;public ViewWrapper(View target){mTarget = target;}public void setWidth(int width){mTarget.getLayoutParams().width = width;mTarget.requestLayout();}public int getWidth(){return mTarget.getLayoutParams().width;} } ViewWrapper viewWrapper = new ViewWrapper(view); ObjectAnimator widthAnimator = ObjectAnimator.ofInt(viewWrapper, "width", btn.getWidth(), 800).setDuration(500); widthAnimator.start();這樣,上面的TextView就可以實現寬度的動畫效果了。
自定義View設置相關屬性,進行動畫案例:
/*** 自定義圓環* 對屬性progress設置set和get方法* 就可以對CircleProgress的progress進行屬性動畫*/ public class CircleProgress extends View{private Paint mPaint;private RectF rectF;private int progress = 270;int width, height;public CircleProgress(Context context) {super(context);init();}public CircleProgress(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(20);mPaint.setColor(Color.RED);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);if (widthMode == MeasureSpec.EXACTLY){width = widthSize;}else{width = 100; //設置為默認值 }if (heightMode == MeasureSpec.EXACTLY){height = heightSize;}else{height = 100; //設置為默認值 }rectF = new RectF(20, 20, width-20, height-20);setMeasuredDimension(Math.min(width, height), Math.min(width, height)); //矩形 }@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawArc(rectF, -90, progress, false, mPaint);invalidate();}public void setProgress(int progress){this.progress = progress;}public int getProgress(){return progress;} }?
轉載于:https://www.cnblogs.com/denluoyia/p/8978045.html
總結
以上是生活随笔為你收集整理的Android之动画的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Elasticsearch DSL
- 下一篇: 压缩tar