編輯:關於Android編程
簡介
屬性動畫是API 11新加入的特性,和View動畫不同,它對作用對象進行了擴展,屬性動畫可以對任意對象做動畫,也不像View動畫只支持四種簡單的變化。
屬性動畫的默認時間是300ms,默認頻率是10ms/幀
ObjectAniamtor
先看看一個簡單例子的實現效果,使用ObjectAnimator,它使用起來比較簡單
布局就是一個ImageView,看看實現代碼
//透明度變化動畫 public void alpha(View view) { // 1、通過調用ofFloat()方法創建ObjectAnimator對象,並設置目標對象、需要改變的目標屬性名、初始值和結束值; ObjectAnimator mAnimatorAlpha = ObjectAnimator.ofFloat(mImageView, "alpha", 1.0f, 0.0f); //2、設置動畫的持續時間、是否重復及重復次數屬性; mAnimatorAlpha.setRepeatMode(Animation.REVERSE); mAnimatorAlpha.setRepeatCount(1); mAnimatorAlpha.setDuration(1000); //3、啟動動畫 mAnimatorAlpha.start(); } //翻轉動畫,翻轉360度 public void flip(View view) { ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f); //設置插值器 visibleToInVisable.setInterpolator(new LinearInterpolator()); visibleToInVisable.setDuration(1000); visibleToInVisable.start(); } //縮放動畫 public void scale(View view) { Animator animator = AnimatorInflater.loadAnimator(this, R.animator.scale_anim); animator.setTarget(mImageView); animator.start(); } //平移動畫 public void translate(View view) { ObjectAnimator mAnimatorTranslateX = ObjectAnimator.ofFloat(mImageView, "translationX", 0.0f, screenWidth / 2); mAnimatorTranslateX.setRepeatMode(Animation.REVERSE); mAnimatorTranslateX.setRepeatCount(1); mAnimatorTranslateX.setDuration(1000); ObjectAnimator mAnimatorTranslateY = ObjectAnimator.ofFloat(mImageView, "translationY", 0.0f, screenHeight / 2); mAnimatorTranslateY.setRepeatMode(Animation.REVERSE); mAnimatorTranslateY.setRepeatCount(1); mAnimatorTranslateY.setDuration(1000); mAnimatorTranslateX.start(); mAnimatorTranslateY.start(); } //旋轉動畫 public void rotate(View view) { ObjectAnimator mAnimatorRotate = ObjectAnimator.ofFloat(mImageView, "rotation", 0.0f, 360.0f); mAnimatorRotate.setRepeatMode(Animation.REVERSE); mAnimatorRotate.setRepeatCount(1); mAnimatorRotate.setDuration(2000); mAnimatorRotate.start(); }
就是根據ObjectAnimator的ofFloat(Object target, String propertyName, float... values)構造得到一個ObjectAnimator,可以看到第一個參數是Object類型的,意味著我們可以傳入自定義的類型,而不局限於補間動畫只能用view來做,擴展性也就更好。第二個參數是動畫名稱,可以傳遞系統定義好的,比如上面的幾個,也可以自己隨便寫,然後在屬性變化的監聽裡改變。第三個參數就是屬性值,如果只有一個的話會默認為結束值。
上面縮放動畫是用xml實現的,需要在res下面建一個animator文件夾,然後創建相應的xml動畫,如下
組合動畫
現在要多個動畫效果一起實現怎麼辦,有下面幾種方式
1.PropertyValuesHolder
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f); PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f); ObjectAnimator animator1 = ObjectAnimator.ofPropertyValuesHolder(mImageView, alpha, scaleX); animator1.setDuration(1000); animator1.start();2.監聽屬性變化
ObjectAnimator animator = ObjectAnimator.ofFloat(mImageView, "lzy", 0.0f, 1.0f); animator.setDuration(1000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (float) animation.getAnimatedValue(); mImageView.setAlpha(value); mImageView.setScaleX(1 - value * 0.5f); } }); animator.start();設置屬性變化值在0-1之間,然後在onAnimationUpdate中監聽得到,通過這個值調用view自身的方法改變。
3.AnimatorSet
AnimatorSet mAnimatorSet = new AnimatorSet(); ObjectAnimator mAnimatorSetRotateX = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f); mAnimatorSetRotateX.setDuration(3000); ObjectAnimator mAnimatorSetRotateY = ObjectAnimator.ofFloat(mImageView, "rotationY", 0.0f, 360.0f); mAnimatorSetRotateY.setDuration(3000); ObjectAnimator mAnimatorScaleX = ObjectAnimator.ofFloat(mImageView, "scaleX", 1.0f, 0.2f); mAnimatorScaleX.setRepeatCount(1); mAnimatorScaleX.setRepeatMode(Animation.REVERSE); mAnimatorScaleX.setDuration(1500); ObjectAnimator mAnimatorScaleY = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f, 0.2f); mAnimatorScaleY.setRepeatCount(1); mAnimatorScaleY.setRepeatMode(Animation.REVERSE); mAnimatorScaleY.setDuration(1500); ObjectAnimator mAnimatorScaleY2 = ObjectAnimator.ofFloat(mImageView, "scaleY", 1.0f, 0.2f); mAnimatorScaleY2.setRepeatCount(1); mAnimatorScaleY2.setRepeatMode(Animation.REVERSE); mAnimatorScaleY2.setDuration(1500); mAnimatorSet.play(mAnimatorSetRotateY) .with(mAnimatorScaleX) .with(mAnimatorScaleY) .before(mAnimatorSetRotateX).before(mAnimatorScaleY2); mAnimatorSet.start();通過animationSet來實現,它提供了一個play()方法,傳入Animator返回一個Builder,這個Builder中有以下四個方法
with(Animator anim),表示同時執行
before(Animator anim),表示將現有動畫插入到傳入動畫之前執行,也就是後執行
after(Animator anim),與before相反
after(long delay),表示延遲多久執行
在測試的時候發現不能重復傳入一個動畫,所以又寫了一個mAnimatorScaleY2 。
AnimationSet中還有playTogether(Animator... items)表示同時執行,playSequentially(Animator... items)表示異步執行。
下面做一個讓Button增加寬度的動畫效果
屬性動畫的大致原理:屬性動畫要求動畫作用的對象提供該屬性的get和set方法,就像上面的"scaleX"屬性,它具有getScaleX和setScale方法,屬性動畫會根據傳遞的初始值和結束值,去調用set的方法設置當前的值,如果沒有傳遞初始值會調用get方法去獲取初始值。
然後Button中並沒有我們想要的get和set方法,所以針對這種問題官方文檔給出了下面的解決方法
1.如果有權限的話,給對象加上get和set方法
2.用一個類來包裝原始對象,間接為其提供get和set方法
3.采用ValueAnimator,監聽動畫變化過程,自己實現屬性的變化
我們現在以第二種方式來實現以上效果,代碼如下
mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ViewWrapper wrapper = new ViewWrapper(mButton); ObjectAnimator.ofInt(wrapper, "width", 1000).setDuration(1000).start(); } });
//用此類來封裝View,間接提供get、set方法 private static class ViewWrapper { private View mView; public ViewWrapper(View view) { mView = view; } public void setWidth(int width) { mView.getLayoutParams().width = width; mView.requestLayout(); } public int getWidth() { return mView.getLayoutParams().width; } }在這個類中提供了width的get和set方法,getWidth()就是獲取View當前的寬度,上面說過在沒有指定初始值時會調用獲得初始值,而setWidth()方法會不斷被調用,直至動畫結束去改變View的寬度。
ValueAnimator
ValueAnimator是ObjectAnimator的父類,也就可以理解為ObjectAnimator是一個封裝好的ValueAnimator,使其使用起來更加的簡單。但是作為父類的ValueAnimator也就使用起來更加的靈活多變。ValueAinamtor本身不會作用於任何對象,直接使用不會有動畫效果,可以理解為它對一個值做動畫,我們通過這個不斷變化的值在監聽函數中去修改相應的屬性。
看看ValueAnimator的幾種構造函數:
ofInt(int... values)
ofArgb(int... values)
ofFloat(float... values)
ofPropertyValuesHolder(PropertyValuesHolder... values)
ofObject(TypeEvaluator evaluator, Object... values)
前三種參數都是初始值到結束值的變化范圍,很簡單
ofPropertyValuesHolder代表多種Animator的集合,下面會介紹具體的使用
ofObject很明顯它的參數類型是Object類型,大多是我們自定義的類型,第一個參數TypeEvaluator 是估值器,因為像上面的int之類的系統都有定義相應的估值器所以不需要我們傳入,然後我們自定義的Object並沒有,所以需要自己的來寫。
來看看系統的IntEvaluator
public class IntEvaluator implements TypeEvaluator估值器都需要繼承自TypeEvaluator,實現裡面的evaluate方法,參數分別是完成的百分比,開始值和結束值,文檔解釋也告訴我們返回的值只需要用結束值減去開始值乘以fraction 然後加上開始值就ok,從而實現值的過渡,所以自定義TypeEvaluator也很簡單了。{ public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
下面看例子,還是先看效果在撸代碼
private void marginValueAnimator() { ValueAnimator valueAnimator = ValueAnimator.ofInt(0, width - mImageView.getWidth()); //監聽變化過程 valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //獲取當前值 int animatedValueMargin = (int) animation.getAnimatedValue(); ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) mImageView.getLayoutParams(); layoutParams.leftMargin = animatedValueMargin; mImageView.setLayoutParams(layoutParams); } }); valueAnimator.setDuration(1000); valueAnimator.setRepeatCount(3); valueAnimator.setRepeatMode(ValueAnimator.REVERSE); valueAnimator.setTarget(mImageView); valueAnimator.start(); }這是個簡單的設置view距離左邊的動畫,構造了ValueAnimator後監聽它的更新,在onAnimationUpdate中改變屬性值,通過animation.getAnimatedValue()獲得當前的值,然後用於更新屬性狀態,實現動畫。
public void scaleValueAnimator() { //1.設置目標屬性名及屬性變化的初始值和結束值 PropertyValuesHolder mPropertyValuesHolderScaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.0f); PropertyValuesHolder mPropertyValuesHolderScaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.0f); ValueAnimator mAnimator = ValueAnimator.ofPropertyValuesHolder(mPropertyValuesHolderScaleX, mPropertyValuesHolderScaleY); //2.為目標對象的屬性變化設置監聽器 mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // 3.根據屬性名獲取屬性變化的值分別為ImageView目標對象設置X和Y軸的縮放值 float animatorValueScaleX = (float) animation.getAnimatedValue("scaleX"); float animatorValueScaleY = (float) animation.getAnimatedValue("scaleY"); mImageView.setScaleX(animatorValueScaleX); mImageView.setScaleY(animatorValueScaleY); } }); //4.為ValueAnimator設置自定義的Interpolator mAnimator.setInterpolator(new CustomInterpolator()); //5.設置動畫的持續時間、是否重復及重復次數等屬性 mAnimator.setDuration(2000); mAnimator.setRepeatCount(3); mAnimator.setRepeatMode(ValueAnimator.REVERSE); //6.為ValueAnimator設置目標對象並開始執行動畫 mAnimator.setTarget(mImageView); mAnimator.start(); }這裡通過PropertyValuesHolder來控制實現兩種屬性的變化,其實這裡x和y兩個方向的縮放值都是1.0f到0.0f,可以用一個ValueAnimator.ofFloat()來完成,這裡主要是為了演示
首先是要通過PropertyValuesHolder.ofFloat來創建了兩個PropertyValuesHolder對象,其中第一個參數是屬性名,可以任意取,可以看到在onAnimationUpdate中通過這個屬性名來獲取不同PropertyValuesHolder的變化值。然後再通過ValueAnimator.ofPropertyValuesHolder來構造ValueAnimator對象就可以,也很容易看懂。
上面那個類似拋物線的圓是采用ValueAnimator.ofObject來實現的。
1.首先要定義一個Point類,裡面用於存放x、y坐標
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(int x) { this.x = x; } public float getY() { return y; } public void setY(int y) { this.y = y; } }之前說過要使用ValueAnimator.ofObject需要自定義TypeEvaluator,下面看看定義的PointEvaluator,可以看到和IntEvaluator一樣,主要還是計算變化值
public class PointEvaluator implements TypeEvaluator { //TypeEvaluator簡單來說是實現了初始值和結束值的平滑過渡 @Override public Object evaluate(float fraction, Object startValue, Object endValue) { Point start = (Point) startValue; Point end = (Point) endValue; //開始值減去結束值乘以fraction再加上開始值就是當前的值 float x = start.getX() + fraction * (end.getX() - start.getX()); float y = start.getY() + fraction * (end.getY() - start.getY()); Point point = new Point(x, y); return point; }下面自定義一個CircleView類
public class CircleView extends View { private static final float Radius = 40.0f; private Point mPoint; private Paint mPaint; public CircleView(Context context) { super(context); } public CircleView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setColor(Color.RED); // mPoint = new Point(50, 50); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { if (mPoint == null) { mPoint = new Point(50, 50); canvas.drawCircle(mPoint.getX(), mPoint.getY(), Radius, mPaint); startAnimation(); } else { canvas.drawCircle(mPoint.getX(), mPoint.getY(), Radius, mPaint); } } private void startAnimation() { //創建開始和結束點坐標 Point start = new Point(50, 50); Point end = new Point(getWidth(), getHeight()); ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(), start, end); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //把每一幀的值傳給mPoint,繪制界面 mPoint = (Point) animation.getAnimatedValue(); invalidate(); } }); animator.setDuration(5000); animator.start(); } }這個類也比較簡單,主要就是在onDraw裡面畫一個圓,然後根據Point的變化不斷調用onDraw方法來改變圓的位置,實現動畫,這也就是ValueAnimator.ofObject的用法。
Android 的菜單機制,在 Android 3.0 之前和之後有很大的去別,Android 3.0 推出 ActionBar ,導航的 UI 交互有很大的變化,但菜單
Android系統支持原生動畫,這為應用開發者開發絢麗的界面提供了極大的方便,有時候動畫是很必要的,當你想做一個滑動的特效的時候,如果苦思冥想都搞不定,那麼你可以考慮下動
模仿支付寶輸入效果,實現很簡單,就是畫個矩形框和圓形,其他的通過組合view來實現所有功能,雖然簡單但是封裝起來,方便以後使用,也分享一下,希望對別人也有點幫助。&nbs
第1節 概述主題theme與風格style是兩個很相近的概念,經常把它們混亂著稱呼。它們都定義在xml文件中,都使用標簽。主題與風格是包含與被包含的關系。例如同一個主題中