在前面的文章中我們寫了view中的圖形的繪製以及水波紋的效果,從中我們可以看出做一些稍微複雜的效果都需要用到動畫的功能,在android系統api1中提供了視圖動畫,在api11的時候提供了屬性動畫,接下來我們就開始講講基本的動畫功能。
-
視圖動畫使用
通過官網我們可以看到視圖動畫,可以簡單分爲兩類,補間動畫和幀動畫,補間動畫包括位移,旋轉,透明度變化,縮放變化。 幀動畫就是提供一些列的drawable連續播放。
作用 | 類名 |
---|---|
對view進行位移 | TranslateAnimation |
對view進行旋轉 | RotateAnimation |
對view進行縮放 | ScaleAnimation |
設置view的透明度 | AlphAnimation |
drawable 連續播放 | DrawableAnimation |
在使用動畫之前首先要明確的是,補間動畫只能是view與它的子類可以使用。通過view.startAnimation(Animation) 來實現。所有的動畫都有兩種方式進行實現。一個是在代碼中實現,一個是在xml中創建。 通過AnimationUtils.loadAnimation()來獲取實例。
ImageView iv = findViewById(R.id.rotate_img);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
beginAnimation();
float left = iv.getLeft();
float right = iv.getRight();
int top = iv.getTop();
int bottom = iv.getBottom();
//圍繞圖片中心點進行旋轉,旋轉180度
animation = new RotateAnimation(0,180,(right-left)/2,(bottom-top)/2);
//設置旋轉所需時間
animation.setDuration(2000);
//fillafter屬性設置爲true,表示動畫執行完畢保存執行之後的狀態,不會復位
animation.setFillAfter(true);
//爲這個動畫設置一個時間插入器,決定動畫執行的快慢方式。
animation.setInterpolator(new AccelerateDecelerateInterpolator());
//開始動畫
iv.startAnimation(animation);
}
});
這就是一個簡單的通過代碼來設置旋轉動畫,又或者我們可以通過創建xml的方式來實現動畫,動畫的資源文件放在/res/anim目錄下, 比如我們做一個平移動畫translate.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="100"
android:fromYDelta="0"
android:toXDelta="400"
android:toYDelta="600"
/>
</set>
然在在代碼中來實現:
final ImageView iv = findViewById(R.id.scale_img);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
iv.startAnimation(animation);
}
});
animation = AnimationUtils.loadAnimation(this,R.anim.translate);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
animation.setDuration(2000);
animation.setFillAfter(true);
這就是兩種動畫實現方式的簡單展示。在使用動畫的時候我們可以通過設置監聽的方式對動畫的開始,結束,以及重複進行監聽,在回調函數裏做出相應的動作。
在大多數的情況下,一個單一的動畫無法實現我們的需求。 當需要多個動畫組合執行的時候,可以用AnimationSet來講所有動畫集合起來執行。 其實AnimationSet就可以看做是一個動畫的集合。 下面就是使用set的一個簡單例子。 當時set也可以通過xml文件實現。
private void beginAnimation() {
// 創建動畫集合
AnimationSet aniSet = new AnimationSet(false);
float left = iv.getLeft();
float right = iv.getRight();
int top = iv.getTop();
int bottom = iv.getBottom();
Log.e("tag","the left="+left+", right="+right+", top="+top+", bottom="+bottom);
// 透明度動畫
AlphaAnimation alpha = new AlphaAnimation(0.5f, 1);
alpha.setDuration(time);
aniSet.addAnimation(alpha);
// 旋轉動畫
RotateAnimation rotate = new RotateAnimation(0,720,(right-left)/2,(bottom-top)/2);
rotate.setDuration(time);
aniSet.addAnimation(rotate);
// 縮放動畫
ScaleAnimation scale = new ScaleAnimation(0.1f, 1, 0.1f, 1f,(right-left)/2,(bottom-top)/2);
scale.setDuration(time);
aniSet.addAnimation(scale);
TranslateAnimation translate = new TranslateAnimation(right, left, bottom, top);
translate.setDuration(time);
aniSet.addAnimation(translate);
// 動畫監聽
aniSet.setAnimationListener(new Animation.AnimationListener() {
// 動畫開始
@Override
public void onAnimationStart(Animation animation) {
}
// 動畫結束,一般在這裏實現頁面跳轉邏輯
@Override
public void onAnimationEnd(Animation animation) {
// 動畫結束後,跳轉到主頁面
// startActivity(new Intent(RotateActivity.this, MainActivity.class));
}
// 動畫重複
@Override
public void onAnimationRepeat(Animation animation) {
}
});
// 把動畫設置給llGroup
iv.startAnimation(aniSet);
}
這幾個可視動畫都是繼承Animation,其實動畫可以解析成幾個模塊,首先做什麼樣的動作(平移,旋轉等)。 第二時間(需要多久執行完),第三時間插入器(Interpolator )它決定動畫執行的速率。加速執行還是減速或者勻速執行。最後可以添加動畫監聽。而其實我們所做的各個動畫,其實本質上可以看做是矩陣的變換執行結果。想對view動畫進行重寫必須對矩陣Matrix清楚。這裏有一篇矩陣的博客寫的很好, 希望大家看看這篇博客,會對矩陣有更清晰的認識。
-
qq抖動
下面我們就通過繼承Animation來實現自己所需動畫的功能。首先我們分析Animation.java的源碼發現子類必須繼承實現applyTransformation()這個方法
/**
* Helper for getTransformation. Subclasses should implement this to apply
* their transforms given an interpolation value. Implementations of this
* method should always replace the specified Transformation or document
* they are doing otherwise.
*
* @param interpolatedTime The value of the normalized time (0.0 to 1.0)
* after it has been run through the interpolation function.
* @param t The Transformation object to fill in with the current
* transforms.
*/
protected void applyTransformation(float interpolatedTime, Transformation t) {
}
通過註釋說明可以看出interpolatedTime是一個時間因子,他是一個範圍從0到1的float類型,它表示動畫已經執行的時間佔總時間的百分比。transformation表示的相當於在某個時間點的動畫的轉換。我們可以通過它獲取當前變換的矩陣,然後做自己的所需要的操作。
public class MyTranslateAnimation extends Animation{
float x,y,tox,toy;
//這個類的本意是模擬實現translateAnimation,當用作抖動動畫使用的時候,參數無意義
public MyTranslateAnimation(float fromx,float tox,float fromy,float toy) {
this.x = fromx;
this.y = fromy;
this.tox = tox;
this.toy = toy;
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t){
//模擬的translateAnimation實現
/* float dx = x + (tox -x)*interpolatedTime;
float dy = y + (toy - y)*interpolatedTime;
t.getMatrix().setTranslate(dx, dy);*/
//因爲interpolatedTime爲0到1,所以乘以10π,表示5個sin週期,這個值越大,晃動的頻率就越
//高,
//因爲Math.sin(interpolatedTime*10*Math.PI)他的值爲[-1,1]所以以向左向右10像素移動。
t.getMatrix().setTranslate((float)(Math.sin(interpolatedTime*10*Math.PI)*10),
(float)Math.sin(interpolatedTime*10*Math.PI)*5);
super.applyTransformation(interpolatedTime,t);
}
}
直接通過以下代碼就可以實現都圖片的抖動:
public class TranslateActivity extends AppCompatActivity {
MyTranslateAnimation animation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_translate);
final ImageView iv = findViewById(R.id.translate_img);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
iv.startAnimation(animation);
}
});
animation = new MyTranslateAnimation(0,1000,0,500);
animation.setFillAfter(true);
animation.setDuration(2000);
}
其實無論是想實現什麼樣的動畫,都是需要通過時間因子與matrix相互結合然後實現的。就像上面模擬位移動畫一樣,我們可以看看translateAnimation的源碼:
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
float dx = mFromXDelta;
float dy = mFromYDelta;
if (mFromXDelta != mToXDelta) {
dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
}
if (mFromYDelta != mToYDelta) {
dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
}
t.getMatrix().setTranslate(dx, dy);
}
原理上與我們自己寫的是一致的, 如果有空可以看看其他幾個動畫的源碼。你就發現其實並不難。
-
總結
實現自己需要的動畫,一定要了解矩陣的知識,推薦:https://www.cnblogs.com/fordreamxin/p/4721497.html這個作者寫的。