編輯:關於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 菜單簡析01(OptionsMenu)
Android 的菜單機制,在 Android 3.0 之前和之後有很大的去別,Android 3.0 推出 ActionBar ,導航的 UI 交互有很大的變化,但菜單
Android動畫使用開源動畫庫nineoldandroids
Android系統支持原生動畫,這為應用開發者開發絢麗的界面提供了極大的方便,有時候動畫是很必要的,當你想做一個滑動的特效的時候,如果苦思冥想都搞不定,那麼你可以考慮下動
Android 仿支付寶密碼輸入框效果
模仿支付寶輸入效果,實現很簡單,就是畫個矩形框和圓形,其他的通過組合view來實現所有功能,雖然簡單但是封裝起來,方便以後使用,也分享一下,希望對別人也有點幫助。&nbs
風格style與主題theme(一)-style、theme的關系與使用
第1節 概述主題theme與風格style是兩個很相近的概念,經常把它們混亂著稱呼。它們都定義在xml文件中,都使用標簽。主題與風格是包含與被包含的關系。例如同一個主題中