文章目錄
要點提煉|開發藝術之Animation
Android 屬性動畫:這是一篇全面 & 詳細的 屬性動畫 總結&攻略
自定義View
1.空氣質量
可以看出該自定義View包括:(1)頂層文字Text(2)中間的文字Text(3)中間的數字Text(動畫形式)(4)兩個有角度的不同顏色的圓,第一個圓默認畫至一定角度,第二個圓根據空氣質量數值轉化成角度。
我們設定空氣質量爲500時是滿格,空氣質量的數字是Text是動畫效果增長至最終值,而頂層圓也是動畫效果到達對應的角度。提供一個方法當獲取到天氣信息後調用,在updateIndex(int value,String middleText)進行動畫效果,
主要遇到的問題是drawText中的baseline選取。
layout中使用:
<com.example.aaa.coolweather.View.CircleIndexView
android:id="@+id/circleindexview"
android:layout_width="170dp"
android:layout_height="170dp"
app:topText="污染指數"
/>
2.日出日落
主要思路:繪製一個封閉的半圓,在半圓的左下角與右下角繪製日出時間與日落時間。根據日出日落時間可以計算出總時間。用當前時間-日出時間,可以計算出現在太陽應走過的時間。用應走過的時間/總時間就可以得到當前時間對應的角度。
計算出當前時間對應的最終角度的代碼如下,之後會通過動畫的形式來將太陽圖片移動到對應角度。
mStartTime = startTime;
mEndTime = endTime;
mCurrentTime = currentTime;
mTotalMinute = calculateTime(mStartTime, mEndTime);//計算總時間,單位:分鐘
mNeedMinute = calculateTime(mStartTime, mCurrentTime);//計算當前所給的時間 單位:分鐘
mPercentage = formatTime(mTotalMinute, mNeedMinute);//當前時間的總分鐘數佔日出日落總分鐘數的百分比
mCurrentAngle = (180 * mPercentage);
setAnimation(0, mCurrentAngle, 5000);
太陽對應的x座標位置與y座標位置如下,這裏的mCurrentAngle是動畫中當前幀的角度,而不是最終角度。可以通過sin函數和cos函數計算出對應的xy座標值,因爲該自定義View具有屬性radius來代表圓的半徑。
注意:(1)要記得減去這個圖標自己的寬度/2,否則圖片就不在線的正中間了。
(2)同時要注意Math.cos和sin傳入的都是弧度制。
(3)marginTop是用於將圓弧距離頂部一定位置,防止圖片到上面就顯示不全了。
//當太陽在最高點時不會有部分消失
private int marginTop = 30;//離頂部的高度
private int mRadius; //圓的半徑
...
positionX=mWidth/2-(float)(mRadius*Math.cos((mCurrentAngle)*Math.PI/180))-mSunIcon.getWidth()/2;
positionY=marginTop+mRadius-(float)(mRadius*Math.sin((mCurrentAngle)*Math.PI/180))-mSunIcon.getHeight()/2;
在layout中的具體使用,除了寬高以外,還定義了字體大小,字體顏色,圓的顏色以及圓的半徑(最重要)
<com.example.aaa.coolweather.View.SunView
android:id="@+id/sunview"
android:layout_width="match_parent"
android:layout_height="150dp"
app:sun_circle_color="#ffff"
app:sun_circle_radius="120"
app:sun_font_size="10px"
app:sun_font_color="#ffff"
/>
自定義View+動畫的整體流程總述
自定義View過程
(1-6是自定義View,7-9是屬性動畫的使用)
- 寫一個MyView類,繼承自View。
- 在values文件夾下創建attrs.xml文件,在
<declare-styleable name="XXXView">
標籤下聲明自定義的屬性(會生存R.styleable.name的數組,幷包含R.styleable.name_attrname的索引)。 - 在構造函數中使用TypedArray獲取在佈局中定義的屬性,並使用。
- 重寫onMeasure以支持wrap_content屬性。定義wrap_content下的寬高值,並當SpecMode(不論寬高)==MeasureSpec.AT_Most,就調用setDimension(定義的寬與高);
- 重寫onDraw來繪製我們想要繪製的東西。
- 在layout佈局中使用自定義屬性,注意開頭要使用app,而非android。
動畫過程
- 創建ValueAnimator對象或ObjectAnimator對象,設置動畫持續時間與開始停止位置;
- 設置動畫從開始到停止的執行邏輯(設置插值器與估值器,比如線性與非線性的動畫效果等等,線性不需要設置)
注:(1)(2)過程都是在ValueAnimator.ofObject/ofInt/ofFloat中執行的
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
- 設置AnimatorUpdateListener監聽器,當動畫每播放一幀時會調用其中的onAnimationUpdate方法,如果是ValueAnimator就需要在其中手動設置該對象的屬性,否則則不需要。最後調用invalidate方法讓View重繪,調用onDraw方法。
- (如果需要的話)使用AnimatorSet讓兩個動畫同時進行,使用
playTogether(Animator A,Animator B)
方法。 - 如果使用AnimatorSet就要調用其start方法,如果沒有使用,就要調用ValueAnimator對象的start方法。
- 之後動畫會按照要求執行,直到結束。
自定義View過程
- 寫一個MyView類,繼承自View。
- 在values文件夾下創建attrs.xml文件,聲明自定義的屬性。
- 在構造函數中使用TypedArray獲取在佈局中定義的屬性,並使用。
- 重寫onMeasure以支持wrap_content屬性
- 重寫onDraw來繪製我們想要繪製的東西。
- 在layout佈局中使用自定義屬性,注意開頭要使用app,而非android。
1.構造函數
只帶有Context的構造函數是在代碼中使用的,而帶有context和AttributeSet(屬性集)的是layout文件中使用的。在最後的構造函數中,我們要初始化參數(將layout中的參數加載進來),以及初始化畫筆(我們要通過畫筆繪製Text與圓)。
public CircleIndexView(Context context)
{
this(context, null);
}
public CircleIndexView(Context context, AttributeSet attrs)
{
this(context, attrs, -1);
}
public CircleIndexView(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
initParams(context, attrs);
initPaint();
}
2.創建attrs.xml文件,並聲明自定義的屬性
需要在values文件夾下創建attrs.xml文件聲明自定義的屬性。在resource標籤下,寫一個declare-styleable的標籤,name一定要寫自定義View的類名。具體屬性使用<attr name="middleText" format="string"/>
,name表示名稱,format代表類型。類型包括:string、dimension(尺寸)、color、integer等等。
declare-styleable 不是必要的,但是styleable可以幫我們節省開發工作量,幫我們生成數組的索引常量。每個declare-styleable產生一個R.styleable.name數組,外加每個屬性的R.styleable.name_attrname 對應屬性的下標,可以通過這個下標取出對應屬性存儲的值。
<resources>
<declare-styleable name="CircleIndexView">
<attr name="middleText" format="string"/>
<attr name="middleTextSize" format="dimension"/>
<attr name="middleTextColor" format="color"/>
<attr name="topText" format="string"/>
<attr name="topTextSize" format="dimension"/>
<attr name="topTextColor" format="color"/>
<attr name="numberTextSize" format="dimension"/>
<attr name="numberColor" format="color"/>
<attr name="outCircleColor" format="color"/>
<attr name="inCircleColor" format="color"/>
<attr name="duration" format="integer"/>
</declare-styleable>
</resources>
3.構造函數中使用TypedArray獲取在佈局中定義的屬性,並使用。
TypedArray幫我們實現了獲取attrs中的id與解析id的功能,因此我們可以根據這個id在TypedArray中獲取想要的屬性值。
(1)取出數組名爲R.styleable.CircleIndexView的TypeArray,之後讀取該TypedArray中的屬性。
(2)最後,recycle()
方法的作用就是關閉了這個TypedArray。
可以看到下標爲R.styleable.CircleIndexView_middleText,因此可知declare-styleable爲我們創建了數組,同時可以讓我們通過TypedArray對應下標去查詢屬性。
另外注意一點TypedValue.applyDimension()
可以將非標準尺寸(dp,sp等等)轉成Android中的標準尺寸(px像素)。
/**
* 初始化View參數
* 獲取佈局(xml)中的屬性以及使用佈局(xml)中的屬性,
*
*
* @param context
* @param attrs
*/
private void initParams(Context context, AttributeSet attrs)
{
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleIndexView);
middleText = ta.getString(R.styleable.CircleIndexView_middleText);
setMiddleText(middleText);
...
//這裏其實是一般在layot裏沒有去寫這個屬性,因此這裏直接將默認值轉爲px以便使用
topTextSize = ta.getDimension(R.styleable.CircleIndexView_topTextSize,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 15, context.getResources().getDisplayMetrics()));
ta.recycle();
}
初始化畫筆就比較簡單了,初始化畫筆的顏色,粗細等等
4.重寫onMeasure以支持wrap_content屬性
常規操作:
1. 首先通過當前的MeasureSpec來獲取寬高測量模式和大小
2. 設定WrapContent模式下的寬高
3. 當測量模式爲AT_MOST且佈局參數爲wrap_content時,直接調用setMeasuredDimension(mWidth, mHeight);
,去設置寬高。
完整代碼:
@Override
protected 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);
// 設置wrap_content的默認寬 / 高值
// 默認寬/高的設定並無固定依據,根據需要靈活設置
// 類似TextView,ImageView等針對wrap_content均在onMeasure()對設置默認寬 / 高值有特殊處理,具體讀者可以自行查看
int mWidth = 400;
int mHeight = 400;
// 當佈局參數設置爲wrap_content時,設置默認值
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, mHeight);
// 寬 / 高任意一個佈局參數爲= wrap_content時,都設置默認值
} else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, heightSize);
} else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(widthSize, mHeight);
}
}
而本自定義View中爲了好看,默認wrap_content和match_parent都爲寬高200dp,除非爲固定大小,我們才更改size爲固定的大小,注意這裏不管是200dp還是固定大小,都要將dp轉成px。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}
5.重寫onDraw來繪製我們想繪製的東西
(1)繪製圓(canvas.drawArc)
RectF的四個參數分別是左上右下,可以通過創建RectF來讓canvas通過canvas.drawArc(mRectF,startAngle,sweepAngle,false,outPaint);
來畫圓(其中傳入參數是圓的左上右下四個點的位置,開始的角度,停止的角度),稍微注意一下內圓圈的角度是會變的,所以這裏使用getInSweepAngle()
方法來獲取。
(2)繪製文字(canvas.drawText)
canvas.drawText(text, x, y, paint)方法,其中x是這個字符串的左邊在屏幕的位置(也就是最左邊那條豎線的座標),如果設置了paint.setTextAlign(Paint.Align.CENTER);那就是字符串的中心;y是指定這個字符串baseline在屏幕上的位置,而不是字符居中的位置。
baseline問題:
FontMetrics可以理解爲字體度量,可以獲取到top,ascent,descent與bottom距離base的距離。而值向下增加,值向上減少,也就是說top、ascent的值都是負的。
fontMetrics=middleTextPaint.getFontMetrics();
在設計baseline時,我們想讓Text內容居中在某個位置(比如說整個View的正中心)。那麼我們就要讓baseline的位置爲正中心高度middle+(base距離正中心高度的距離)。
我們可以推出這個距離設base距離正中心高度爲x,因爲是正中心(top距離middle和bottom距離middle相等),因此top距離base的距離-x = bottom距離base的距離+x,可以求得x=(dtop-dbottom)/2,而已知top的值是負的,因此x就等於(-top-bottom)/2,將該值加上正中心高度middle即可。
baseline=(getCircleHeight()-fontMetrics.bottom-fontMetrics.top)/2;
protected void onDraw(Canvas canvas){
//200dp:middleTextSize:60,middleNumberSize:100,bottomTextSize:45
super.onDraw(canvas);
mCenter = getCircleWidth()/2;
//mRadius = getCircleWidth() / 2 - 50;
mRadius = getCircleWidth()/2-50;
//左,上,右,下
mRectF =new RectF(mCenter-mRadius,mCenter-mRadius+getTopTextSize(),
mCenter+mRadius,mCenter+mRadius+getTopTextSize());
//外圓圈
canvas.drawArc(mRectF,startAngle,sweepAngle,false,outPaint);
//內圓圈
canvas.drawArc(mRectF,startAngle,getInSweepAngle(),false,inPaint);
//中心文字(etc. 良)
middleTextPaint.setColor(getMiddleTextColor());
middleTextPaint.setTextSize(getMiddleTextSize());
Paint.FontMetrics fontMetrics=middleTextPaint.getFontMetrics();
float baseline=(getCircleHeight()-getNumberTextSize()-getMiddleTextSize()-20-fontMetrics.bottom-fontMetrics.top)/2+getTopTextSize();
//想讓他在居中的位置(getCircleHeight()/2-getNumberTextSize()/2-getMiddleTextSize()/2-20)
canvas.drawText(getMiddleText(), getCircleWidth() / 2, baseline , middleTextPaint);
//中心數字(etc)
middleTextPaint.setColor(getNumberColor());
middleTextPaint.setTextSize(getNumberTextSize());
fontMetrics=middleTextPaint.getFontMetrics();
baseline=(getCircleHeight()-fontMetrics.bottom-fontMetrics.top)/2+getTopTextSize();
canvas.drawText(getIndexValue()+"",getCircleWidth()/2,
baseline,middleTextPaint);
//頂部文字(etc. 空氣污染指數)
middleTextPaint.setColor(getTopTextColor());
middleTextPaint.setTextSize(getTopTextSize());
fontMetrics=middleTextPaint.getFontMetrics();
baseline=(getCircleHeight()-2*mRadius-fontMetrics.bottom-fontMetrics.top)/2;
//canvas.drawText(getBottomText(), getCircleWidth() / 2, getCircleHeight() - 50, middleTextPaint);
//讓這個與圓的最頂端間隔20px
canvas.drawText(getTopText(), getCircleWidth() / 2,baseline ,middleTextPaint);
}
6.layout文件中使用
注意我們自己定義的屬性,要使用app:attrname。同時要在前面定義 :xmlns:app=“http://schemas.android.com/apk/res-auto”。
動畫
空氣質量的使用
這是空氣質量動畫暴露給外界使用的函數,當獲取到天氣信息後,會通過updateIndex去更新當前空氣質量的數值與空氣質量的文字,其中使用了動畫。
注意調用invalidate會導致View重繪,最終會調用onDraw。因此在invalidate之前要通過set方法設置當前的角度與當前的空氣質量值。
/**
*
* @param value 空氣質量數值
* @param middleText 空氣質量(良)
*/
public void updateIndex(int value,String middleText){
setMiddleText(middleText);
invalidate();
//當前角度
float inSweepAngle = sweepAngle * value / 500;
//角度由0f到當前角度變化
ValueAnimator angleAnim = ValueAnimator.ofFloat(0f, inSweepAngle);
//動畫持續時間
angleAnim.setDuration(getDuration());
//數值由0到value變化
ValueAnimator valueAnim = ValueAnimator.ofInt(0,value);
valueAnim.setDuration(getDuration());
//註冊監聽器
angleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
//當angleAnim變化回調/
public void onAnimationUpdate(ValueAnimator valueAnimator)
{
float currentValue = (float) valueAnimator.getAnimatedValue();
//將當前的角度值賦給inSweepAngle
setInSweepAngle(currentValue);
//通知view改變,調用這個函數後,會返回到onDraw();
invalidate();
}
});
valueAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator)
{
int currentValue = (int) valueAnimator.getAnimatedValue();
setIndexValue(currentValue);
invalidate();
}
});
//讓兩個動畫同時進行。
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setInterpolator(new DecelerateInterpolator());
animatorSet.setStartDelay(150);
animatorSet.playTogether(angleAnim, valueAnim);
animatorSet.start();
}
日出日落動畫的使用
setTimes是暴露給外部使用的方法,用於更新當前的時間。
public void setTimes(String startTime, String endTime, String currentTime)
{
mStartTime = startTime;
mEndTime = endTime;
mCurrentTime = currentTime;
mTotalMinute = calculateTime(mStartTime, mEndTime);//計算總時間,單位:分鐘
mNeedMinute = calculateTime(mStartTime, mCurrentTime);//計算當前所給的時間 單位:分鐘
mPercentage = formatTime(mTotalMinute, mNeedMinute);//當前時間的總分鐘數佔日出日落總分鐘數的百分比
mCurrentAngle = (180 * mPercentage);
setAnimation(0, mCurrentAngle, 5000);
}
private void setAnimation(float startAngle,float currentAngle,int duration){
ValueAnimator sunAnimator= ValueAnimator.ofFloat(startAngle,currentAngle);
sunAnimator.setDuration(duration);
sunAnimator.setTarget(currentAngle);
sunAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurrentAngle=(float)animation.getAnimatedValue();
invalidateView();
}
});
//千萬別忘了這個
sunAnimator.start();
}
private void invalidateView(){
//繪製太陽的x座標與y座標
//記得要減去太陽圖標的大小。(讓太陽始終在半圓上)
positionX=mWidth/2-(float)(mRadius*Math.cos((mCurrentAngle)*Math.PI/180))-mSunIcon.getWidth()/2;
positionY=marginTop+mRadius-(float)(mRadius*Math.sin((mCurrentAngle)*Math.PI/180))-mSunIcon.getHeight()/2;
invalidate();
}
動畫總結
View動畫
主要包括translate平移動畫、scale縮放動畫、rotate旋轉動畫、alpha透明度動畫等四種。
View動畫的View移動只是視覺效果,並不能真正的改變view的位置。
xml的使用:
首先創建xml文件,地址爲res/anim/xxx.xml,可以看到動畫可以僅是一個動畫,也可以是一堆動畫的組合。其中<set>
標籤代表AnimatorSet,<translate>
代表平移動畫,<scale>
代表縮放動畫,<rotate>
代表旋轉動畫,<alpha>
代表透明動畫(改變View的透明度)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="true"
android:fillAfter="true">
<translate
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float"/>
<scale
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="float"
android:toYScale="float"
android:pivotX="float"
android:pivotY="float"/>
<rotate
android:fromDegrees="float"
android:toDegrees="float"
android:pivotY="float"
android:pivotX="float"/>
<alpha
android:fromAlpha="float"
android:toAlpha="float"/>
</set>
幀動畫
幀動畫也是View動畫的一種,它會按照順序播放一組預先定義好的圖片。對應類AnimationDrawable。
通過xml定義:
該xml文件創建在res/drawable/ 下,其中根節點<animation-list>
,屬性android:oneshot
表示是否執行一次;子節點<item>
下可設置輪播的圖片資源id和持續時間。例如:
<?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/xxx1" android:duration="500"/>
<item android:drawable="@drawable/xxx2" android:duration="500"/>
<item android:drawable="@drawable/xxx3" android:duration="500"/>
<item android:drawable="@drawable/xxx4" android:duration="500"/>
</animation-list>
在xml聲明好之後,將它作爲View的背景並通過AnimationDrawable來播放。
mView.setBackgroundResource(R.drawable.XXX);
AnimationDrawable animationDrawable = (AnimationDrawable)mView.getBackground();
animationDrawable.start();
屬性動畫
與View動畫不同之處主要在於:(1)View動畫通過不斷圖形變換,而屬性動畫通過動態改變對象屬性;(2)作用對象分別是View和任何對象;(3)如果是通過xml使用的話,存放位置不同:res/anim和res/animator(4)View動畫沒有真正改變View的位置,而屬性動畫真正改變了View的位置。
爲第四點作補充:
補間動畫只是改變了View的視覺效果,而不會真正去改變View的屬性。
如,將屏幕左上角的按鈕 通過補間動畫 移動到屏幕的右下角
點擊當前按鈕位置(屏幕右下角)是沒有效果的,因爲實際上按鈕還是停留在屏幕左上角,補間動畫只是將這個按鈕繪製到屏幕右下角,改變了視覺效果而已。
ValueAnimator與ObjectAnimator
ValueAnimator
ValueAnimator是ObjectAnimator的父類,它繼承自Animator。ValueAnimaotor同樣提供了ofInt、ofFloat、ofObject等靜態方法,傳入的參數是動畫過程的開始值、(中間值可以沒有)、結束值來構造動畫對象。
可以將ValueAnimator看成一個值變化器,通過不斷控制值的變化,再不斷手動賦給對象的屬性,從而實現動畫效果。
ObjectAnimator
直接對對象(自動)的屬性值進行改變操作,從而實現動畫效果,和ValueAnimator相比是直接更改屬性的值。使用ObjectAnimator必須要實現set方法。
自動賦值的思路如下:(1)如果沒有傳遞初值會通過get值來獲取;(2)在動畫執行的過程中通過set更改屬性的值
插值器與估值器
時間插值器:TimeInterpolator。**根據時間流逝的百分比來計算當前的屬性值改變的百分比。**包括:線性插值器(LinearInterpolator 勻速動畫)、加速減速插值器(AccelerateDecelerateInterpolator 動畫兩頭慢中間快)、減速插值器(DecelerateInterpolator 動畫持續減速);
類型估值器(TypeEvaluator):根據當前屬性改變的百分比來計算改變後的屬性值。包括針對整形、浮點型、顏色的估值器。
以下圖爲例,View的x屬性需要在40ms內從0到40,結合時間插值器和類型估值器來完成。
以線性插值器爲例,由於動畫默認是10ms一幀,那麼當運行到第二幀是t=20ms,因此時間流逝了0.5,因此屬性值也應該改變0.5。時間插值器計算出0.5並返回,具體屬性改變多少由類型估值器計算。估值器通過估值小數*(屬性終點-屬性起點)
來計算出改變後的屬性值。
當然插值器與估值器都可以自定義,尤其是在當前類型的估值器沒有的情況下就必須自定義類型估值器。
監聽器
屬性動畫提供了AnimatorLinstenr用於在動畫開始結束取消重複播放時進行回調,
public static interface AnimatorListener {
void onAnimationStart(Animator animation); //動畫開始
void onAnimationEnd(Animator animation); //動畫結束
void onAnimationCancel(Animator animation); //動畫取消
void onAnimationRepeat(Animator animation); //動畫重複播放
}
AnimatorUpdateListener :監聽整個動畫過程。每播放一幀onAnimationUpdate()就會被調用一次,如下:
public interface AnimatorUpdateListener {
void onAnimationUpdate(ValueAnimator var1);//在屬性動畫的屬性值變化是回調。
}
工作流程
(1)創建ValueAnimator或ObjectAnimator,並設置動畫持續時間與開始停止位置;
(2)設置動畫從開始到停止的執行邏輯(設置插值器與估值器,比如線性與非線性的動畫效果等等,線性不需要設置)
注:(1)(2)過程都是在ValueAnimator.ofObject/ofInt/ofFloat中執行的
ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
(3)設置AnimatorUpdateListener監聽器,當動畫每播放一幀時會調用其中的onAnimationUpdate方法,如果是ValueAnimator就需要在其中手動設置該對象的屬性,否則則不需要。最後調用invalidate方法讓View重繪,調用onDraw方法。
在這裏插入代碼片
(4)(如果需要的話)使用AnimatorSet讓兩個動畫同時進行,使用playTogether(Animator A,Animator B)
方法。
(5)如果使用AnimatorSet就要調用其start方法,如果沒有使用,就要調用ValueAnimator對象的start方法。
(6)之後動畫會按照要求執行,直到結束。