【轉載】Android 屬性動畫詳解
引言:
現在市面類似功能的App太多,如果你想要在類似的App中脫穎而出的話,那麼你就要在增強使用者體驗方面下功夫,動畫是你最好的選擇。它可以給使用者增加指引、吸引眼球、提醒使用者跟著你一步步操作等等,動畫是一個成熟App必不可少的。
Android動畫分類:
先簡單介紹一下Android中的幾種動畫,大致可分為三種:
1.補間動畫(也叫檢視動畫、Tween動畫):
首先來了解下補間動畫的分類,在android中補間動畫可以分為四類:alpha(漸變)、scale(縮放)、translate(位移)、rotate(旋轉)。在這四種動畫裡每種動畫擁有它的獨有的屬性的同時又擁有相同的屬性,其中
alpha:漸變透明度動畫效果
scale:漸變縮放動畫效果
translate:漸變位置移動動畫效果
rotate:漸變旋轉動畫效果,這四種動畫能夠分別帶來不同的效果體驗,又能混合在一起完成酷炫的動畫效果。
接下來我們就逐一詳細的來學習這四種Tween動畫。
結語:補間動畫是android最早出現的動畫,只支援以上四種簡單的動畫,並且補間動畫只是視覺上的改變,不會影響控制元件的本身位置及大小,(就好像你在放大鏡裡看一個東西一樣,只是視覺上的改變,原物體並不改變),所以現在很少用這種動畫,它可以做的屬性動畫都可以做到。它的主要操作物件是Animation這個類,大家有興趣的可以下去了解一下,這裡主要介紹最常用的屬性動畫,所以就不多說了。
2.幀動畫
幀動畫,顧名思義就是像播放視訊一樣一幀一幀進行展示一樣,其實視訊也是一幀幀圖片組成的,那麼幀動畫也一樣,將 一組運動的圖分離開一個個小動作,然後組成,就是一個動畫,這種動畫一般是少量圖片(如一個icon之類的)才會用到,因為大家都知道要縮小apk的體積的話,首先就的注意減少使用不必要的資原始檔,所以只有少數情況會用這種動畫,使用方法很簡單,這裡就不多說了。
3.屬性動畫
接下來就是我們的重頭戲了,屬性動畫。首先得明白屬性動畫是真是改變控制元件本身的屬性,跟上面所說的補間動畫完全相反。
學習屬性動畫先從ValueAnimator這個類學起,這個類非常關鍵,是屬性動畫機制中最核心的一個類,所有的操作直接或者間接都是用它來改變值進行動畫效果。這句話是不是念起來有點拗口,大家有沒有注意ValueAnimator中Value這個詞,為什麼叫Value呢,大家都知道Java中見名知意,所以這個Value肯定是這個動畫的重點,Value:值的意思,那麼我們可以叫值動畫,沒錯就叫值動畫。屬性動畫是通過不斷改變值,再不斷改變你要作用的物件,來達到動畫的效果。是不是很好理解啊,下面我們來講一下這個值動畫,哈哈!
屬性動畫分類:屬性動畫大致又可以分為ValueAnimator,ObjectAnimator。
ValueAnimator:
valueAnimator是通過不斷改變值,手動進行賦值給物件,進行動畫效果。大家注意這個“手動”二字。
下面我們上程式碼具體來看,光說這些無聊的文字沒意思,大家跟著我一步步來:
Java程式碼寫法:
- //這個就是我們說的ValueAnimator這個值動畫類,傳入引數開始值跟結束值,表示你要從哪個值到哪個值進行變化,我們這個意思是我要讓int值從0到100進行變化
- ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
- //這裡是設定動畫執行時間,毫秒
- valueAnimator.setDuration(2000);
- //這裡是設定動畫的執行次數,為你填的n+1,-1表示一直執行
- valueAnimator.setRepeatCount(0);
- //動畫的下次執行開始位置,RESTART表示動畫每次從原始的狀態執行,REVERSE表示動畫第二次執行要從第一次改變後的狀態逆向執行
- valueAnimator.setRepeatMode(ValueAnimator.RESTART);
- //設定數值變化監聽器,重點在這裡,上面我們動畫的變化數值範圍已經設定好了,那麼動畫變化時的值會在這個監聽器中返回給我們,我們來手動賦值,來進行動畫顯示
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- //獲取當前變化時的值
- int value = (int) animation.getAnimatedValue();
- Log.e("lwd","value:" + value);
- //這裡我們給textview設定一個文字,就是我們的當前的數值,那麼就會顯示為從0到100一直變化的動畫
- mTextViewPro.setText(value + "");
- }
- });
- //開始執行動畫
- valueAnimator.start();
大家通過上面的程式碼跟註釋是不是很好理解了,ValueAnimator這種動畫說到底就是設定好開始值、結束值,然後設定好了之後大家在新增監聽器之後,來捕獲數值,然後手動賦值,進行自己的動畫。
xml寫法:
大家在res下新建android資原始檔夾animator,新建一個xml檔案
- <animator xmlns:android="http://schemas.android.com/apk/res/android"
- android:valueFrom="0"
- android:valueTo="100"
- android:valueType="intType"
- android:duration="3000"
- android:fillBefore = "true"
- android:repeatCount="0"
- android:repeatMode="restart">
- </animator>
大家看到了我們xml檔案已經寫好了,一些屬性都很簡單,見名知意,那麼我們來看一下怎麼載入這個xml檔案。
- //就倆行程式碼,載入xml檔案,然後開始執行動畫
- Animator animator = AnimatorInflater.loadAnimator(this, R.animator.value_animator);
- animator.start();
大家也看到了xml檔案寫好之後數值就不會改變了,所以大多數複雜點的都用java來寫。
下面我們來引入一個新的東西,
重點:TypeEvaluator(估值器)
那麼估值器是什麼東西呢,我們先來看一段源嗎,很簡單
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);這行程式碼我到ofint看看它到底怎麼實現的,
- public static ValueAnimator ofInt(int... values) {
- ValueAnimator anim = new ValueAnimator();
- anim.setIntValues(values);
- return anim;
- }
上面是ofInt的方法我們看到只是new了一個ValueAnimator物件,然後設定一下值,給我們返回了這個物件,那麼操作這個這個值的過程應該跟這行程式碼有關係anim.setIntValues(values);我們進去再繼續看。
- public void setIntValues(int... values) {
- if (values == null || values.length == 0) {
- return;
- }
- if (mValues == null || mValues.length == 0) {
- setValues(PropertyValuesHolder.ofInt("", values));
- } else {
- PropertyValuesHolder valuesHolder = mValues[0];
- valuesHolder.setIntValues(values);
- }
- // New property/values/target should cause re-initialization prior to starting
- mInitialized = false;
- }
從上面程式碼我們可以看到,首先判斷傳入的引數開始值跟結束值是否為空,為空則返回;否則的話設定值,那麼具體設定值,我們還得到PropertyValuesHolder這個類中檢視,下面我們來看。
- void init() {
- if (mEvaluator == null) {
- // We already handle int and float automatically, but not their Object
- // equivalents
- mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
- (mValueType == Float.class) ? sFloatEvaluator :
- null;
- }
- if (mEvaluator != null) {
- // KeyframeSet knows how to evaluate the common types - only give it a custom
- // evaluator if one has been set on this class
- mKeyframes.setEvaluator(mEvaluator);
- }
- }
大家看到了嗎,這裡初始化了一個IntEvaluator估值器,那麼我們看一下這個估值器裡面做了什麼操作
- public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
- int startInt = startValue;
- return (int)(startInt + fraction * (endValue - startInt));
- }
我們看到了一個fraction這個引數,這個引數大家可以理解為百分比的意思,估值器給我們不斷返回了動畫過程中不斷變化的數值,那麼到這裡我們該清楚估值器的作用了吧。
估值器顧名思義就是通過開始值跟結束值,給我們計算中間過渡值的一個東西。系統封裝了一些常用的估值器(如FloatEvaluate、IntEvaluate等),那麼接下來我們講一下怎麼自定義自己的估值器。
自定義估值器
需求:畫一個圓,讓圓的進行線性移動。
分析:首先要畫個圓,那麼我們可以自定義一個view,然後畫個圓。那麼素材有了,我們該怎麼讓它移動呢?用屬性動畫,那麼屬性動畫要確定開始值跟結束值,那麼,我們既然要移動的話,肯定是涉及到x、y座標,那麼我們開始值就可以定為圓當前的x、y座標,結束值我們也可以隨便定義一個,下面移動就是我們的重點了,我們帶著問題來一起往下看。
- /**
- * 屬性動畫值變化實體
- * Created by liweidong on 2019/1/8.
- */
- public class Point {
- private float x;
- private float y;
- public Point(float x, float y) {
- this.x = x;
- this.y = y;
- }
- public float getX() {
- return x;
- }
- public void setX(float x) {
- this.x = x;
- }
- public float getY() {
- return y;
- }
- public void setY(float y) {
- this.y = y;
- }
- }
首先我們建立一個x、y座標的物件。
接下來我們開始畫圓,進行自定義view。
- /**
- * 我的圓
- * Created by liweidong on 2019/1/8.
- */
- public class MyCircleView extends View{
- private static final float RADIUS = 70f;//半徑
- private Paint mPaint;
- private Point mPoint;
- public MyCircleView(Context context,
- super(context, attrs);
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setColor(Color.YELLOW);
- }
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- //如果當前點座標為空,即第一次
- if (mPoint == null){
- mPoint = new Point(RADIUS, RADIUS);
- float x = mPoint.getX();
- float y = mPoint.getY();
- canvas.drawCircle(x, y, RADIUS, mPaint);
- //將屬性動畫作用到view上
- //步驟一,建立初始動畫時的物件點,結束動畫時的物件點
- Point startPoint = new Point(RADIUS,RADIUS);
- Point endPoint = new Point(700,700);
- //步驟二,建立動畫物件,設定初始值跟結束值
- ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyPointEvaluator(), startPoint, endPoint);
- valueAnimator.setDuration(5000);
- valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- //當前的點
- mPoint = (Point) animation.getAnimatedValue();
- //很重要,每次賦值要呼叫重新繪製
- invalidate();
- }
- });
- valueAnimator.start();
- }else{
- //接收到動畫改變的點後,一直進行ondraw
- float x = mPoint.getX();
- float y = mPoint.getY();
- canvas.drawCircle(x, y, RADIUS, mPaint);
- }
- }
- }
上面我們自定義了view,通過動畫監聽來觸發一直進行繪製圓,上面用到了一個MyPointEvaluator這個估值器,那麼大家看一下我們這個估值器做了些什麼。
- /**
- * 我的估值器
- * Created by liweidong on 2019/1/8.
- */
- public class MyPointEvaluator implements TypeEvaluator {
- //複寫估值器,在估值器中寫動畫過渡邏輯
- @Override
- public Object evaluate(float fraction, Object startValue, Object endValue) {
- //將動畫開始值跟結束值轉換為point物件
- Point startPoint = (Point) startValue;
- Point endPoint = (Point) endValue;
- //根據fraction計算當前動畫的x跟y
- float x = (float) (startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX()));
- float y = (float) (startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY()));
- //將當前的動畫的座標賦值到一個新的point物件
- Point currentPoint = new Point(x, y);
- return currentPoint;
- }
- }
這下我們在xml中引用自己的自定義view,就ok了,那麼我們自定義的估值器就完成了,大家可以根據程式碼註釋慢慢體會。
ObjectAnimator
下面我們來介紹objectAnimator:
objectAnimator:通過不斷改變值,自動進行賦值給物件。注意這裡是自動哦,可以理解為ValueAnimator的升級版,自動化,不用進行手動進行賦值了。
先講一下單個動畫:
1.translate(平移)
- float currentX = mButtonTranslate.getTranslationX();
- ObjectAnimator translateAnimator = ObjectAnimator.ofFloat(mButtonTranslate, "translationX", currentX, 50, currentX);
- translateAnimator.setDuration(2000);
- translateAnimator.setRepeatCount(0);
- translateAnimator.setRepeatMode(ValueAnimator.RESTART);
- translateAnimator.start();
2.rotate(旋轉)
- ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(mButtonRotate, "rotation", 0f, 360f);
- rotateAnimator.setDuration(2000);
- rotateAnimator.setRepeatCount(0);
- rotateAnimator.setRepeatMode(ValueAnimator.RESTART);
- rotateAnimator.start();
3.scale(縮放)
- ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(mButtonScale, "scaleX", 1f, 3f, 1f);
- scaleAnimator.setDuration(2000);
- scaleAnimator.setRepeatCount(0);
- scaleAnimator.setRepeatMode(ValueAnimator.RESTART);
- scaleAnimator.start();
4.alpha(透明度)
- ObjectAnimator aphaAnimator = ObjectAnimator.ofFloat(mButtonApha, "alpha" ,0 , 1);
- aphaAnimator.setDuration(2000);
- aphaAnimator.setRepeatCount(0);
- aphaAnimator.setRepeatMode(ValueAnimator.RESTART);
- aphaAnimator.start();
上面只是單個的簡單動畫,一般我們用動2020-08-03畫都是組合動畫。
下面提供幾個api供大家進行動畫組合:
AnimatorSet.play(Animator anim) :播放當前動畫 AnimatorSet.after(long delay) :將現有動畫延遲x毫秒後執行 AnimatorSet.with(Animator anim) :將現有動畫和傳入的動畫同時執行 AnimatorSet.after(Animator anim) :將現有動畫插入到傳入的動畫之後執行 AnimatorSet.before(Animator anim) : 將現有動畫插入到傳入的動畫之前執行
- ObjectAnimator translateAnimator = ObjectAnimator.ofFloat(mButtonTranRotate, "translationX", mButtonTranRotate.getTranslationX()
- , 500, mButtonTranRotate.getTranslationX());
- ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(mButtonTranRotate, "rotation", 0 , 360);
- ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mButtonTranRotate, "alpha", 1f,0f, 1f);
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.play(translateAnimator).with(rotateAnimator).before(alphaAnimator);
- animatorSet.setDuration(3000);
- animatorSet.start();
給大家貼一段組合動畫的程式碼,是不是很簡單。
animatorSet.reverse(); 這個用來恢復動畫到先前狀態。到此屬性動畫大致講完了,歡迎大家來補充。下節重點說一下自定義估值器,TypeEvaluate!
轉載自 https://blog.csdn.net/no_loafer/article/details/86156590