Android動畫詳解
幀動畫
所謂幀動畫其實很好理解,就是通過在一定的時間間隔內,將一組圖片順序播放出來,從而打到動畫的效果,我們通過簡單的幀動畫例子來說明一下。
下面是幀動畫的xml資源代碼,animation-list就是幀動畫的標籤,它包含的item就是每一幀的圖像。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@mipmap/one"
android:duration="500"/>
<item
android:drawable="@mipmap/two"
android:duration="500"/>
<item
android:drawable="@mipmap/three"
android:duration="500"/>
<item
android:drawable="@mipmap/four"
android:duration="500"/>
<item
android:drawable="@mipmap/five"
android:duration="50"/>
<item
android:drawable="@mipmap/six"
android:duration="50"/>
</animation-list>
接下來我們創建幀動畫例子activity的佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="開始"
android:id="@+id/start"
android:layout_marginTop="20dp"
android:onClick="onStart"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:id="@+id/stop"
android:text="停止"
android:onClick="onStop"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop ="20dp"
android:id="@+id/imageView"/>
</LinearLayout>
然後是activity裏面的代碼
public class FrameAnimationActivity extends AppCompatActivity {
private AnimationDrawable animationDrawable; //此處寫錯了 寫成了Animation
private ImageView imageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.frame_activity);
init();
}
private void init(){
imageView = (ImageView)findViewById(R.id.imageView);
imageView.setBackgroundResource(R.drawable.frame_anim);
animationDrawable = (AnimationDrawable) imageView.getBackground();
}
public void onStart(View view){
if(animationDrawable != null && !animationDrawable.isRunning()){
animationDrawable.start();
}
}
public void onStop(View view){
if(animationDrawable != null && animationDrawable.isRunning()){
animationDrawable.stop();
}
}
}
通過上面的代碼可以知道,幀動畫主要需要記住3點,1是動畫資源文件-drawable裏面的文件,animation-list以及它的子標籤item。2是AnimationDrawable這個類以及這個類的實例對象通過imageView控件的getBackground方法得到的。3是AnimationDrawable的用法(start()、stop()這個幾個方法)。
補間動畫
補間動畫主要有四種操作:透明變化、縮放控制、旋轉控制、移動控制。這四種動畫變化都是通過資源文件來實現的,我們在res文件夾裏面創建anim文件夾。然後根據需要創建相應的xml文件,這裏通過一個例子說明用法。
下面是alpha、scale、rotate、translate、set對應的xml文件,set文件是將上面所有動畫種類選擇性地集合在一起,同時執行。
- alpha.xml
<?xml version="1.0" encoding="utf-8" ?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromAlpha="0"
android:toAlpha="1"
android:duration="4000"/>
<!-- interpolator:表示採用這個算法,設置動畫播放的平緩的程度 -->
<!-- fromAlpha:初始透明度-->
<!-- toAlpha:最終透明度-->
- scale.xml
<?xml version="1.0" encoding="utf-8" ?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromYScale="0"
android:fromXScale="0"
android:toXScale="1"
android:toYScale="1"
android:pivotY="0"
android:pivotX="100%"
android:duration="3000"/>
<!-- 縮放的的基點 pivotX和pivotY 如果在百分號數值後面加一個p,就是控件左上角加上父控件百分之多少的值-->
<!-- fromYScale:初始Y軸的縮放比例-->
<!-- fromXScale:初始X軸的縮放比例-->
<!-- toYScale:最終Y軸的縮放比例-->
<!-- toXScale:最終X軸的縮放比例-->
- rotate.xml
<?xml version="1.0" encoding="utf-8" ?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="30%"
android:fromDegrees="0"
android:toDegrees="360"
android:duration="3000"/>
<!-- 旋轉的的基點 pivotX和pivotY 如果在百分號數值後面加一個p,就是控件左上角加上父控件百分之多少的值-->
- translate.xml
<?xml version="1.0" encoding="utf-8" ?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXDelta="0"
android:fromYDelta="0"
android:toYDelta="500"
android:toXDelta="500"
android:duration="3000"/>
- set.xml
<?xml version="1.0" encoding="utf-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:shareInterpolator="true">
<scale
android:pivotY="50%"
android:pivotX="50%"
android:fromXScale="0"
android:fromYScale="0"
android:toYScale="1"
android:toXScale="1"
android:duration="1000"/>
<!-- 會覆蓋前面的scale動畫-->
<scale
android:pivotY="50%"
android:pivotX="50%"
android:fromXScale="1"
android:fromYScale="1"
android:toYScale="0"
android:toXScale="0"
android:duration="1000"/>
<alpha
android:fromAlpha="0"
android:toAlpha="1"
android:duration="1000"/>
</set>
補間動畫的佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:text="透明動畫"
android:onClick="onAlpha"
android:layout_marginTop="20dp"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:onClick="onScale"
android:text="縮放動畫"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="20dp"
android:onClick="onRotate"
android:text="旋轉動畫"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_marginTop="20dp"
android:onClick="onTranslate"
android:text="位移動畫"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:onClick="onSet"
android:text="動畫組合"/>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
android:id="@+id/imageView"
android:background="@mipmap/five"/>
</LinearLayout>
activity代碼
public class TweenAnimationActivity extends AppCompatActivity {
private Animation animation;
private ImageView imageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tween_activity);
init();
}
private void init(){
imageView = (ImageView)findViewById(R.id.imageView);
}
public void onAlpha(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.alpha);
imageView.startAnimation(animation);
}
public void onScale(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.scale);
imageView.startAnimation(animation);
}
public void onRotate(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.rotate);
imageView.startAnimation(animation);
}
public void onTranslate(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.translate);
imageView.startAnimation(animation);
}
/**
*
* @param view
*
* 對於set的理解就是,set.xml文件裏面定義這四種動畫,然後同時執行,也可以定義一樣的動畫類型,但是會覆蓋前面的同類型動。
*/
public void onSet(View view){
animation = AnimationUtils.loadAnimation(this, R.anim.set);
imageView.startAnimation(animation);
}
}
通過上面示例代碼可以看出。補間動畫主要記住3點,1是補間動畫資源文件的寫法,2是Animation類和AnimationUtils類loadAnimation()方法的用法, 3是插值器,參考這篇文章。
屬性動畫
屬性動畫需要和補間動畫區分開來,因爲前者的變化是真實的,後者的變化是不真實的,補間動畫只是給人一種障眼法,比如當補間動畫移動之後,你點擊它的顯示區域,發現不會響應點擊事件,只有點擊它的原始區域纔會響應點擊事件。而屬性動畫就不一樣了,只要移動在屏幕可顯示區域就可以響應點擊事件。同樣地給出一個例子來說明。
- 佈局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onAlpha"
android:text="透明"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onScale"
android:text="縮放"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onRotate"
android:text="旋轉"/>
<Button
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:onClick="onTranslate"
android:text="移動"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onSet"
android:text="集合"/>
<Button
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="30dp"
android:layout_gravity="center_horizontal"
android:text="我是移動的控件"
android:id="@+id/move_button"/>
</LinearLayout>
- activity代碼
public class AttributeAnimationActivity extends AppCompatActivity {
private ObjectAnimator objectAnimator;
private Button show_button;
private final static String TAG = "提示";
private AnimatorSet animatorSet = new AnimatorSet();
ObjectAnimator rotateObjectAnimator1 = new ObjectAnimator();
ObjectAnimator translateObjectAnimator1 = new ObjectAnimator();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.attribute_activity);
init();
animatorSet.addListener(new AnimatorListenerAdapter() {
private AnimatorSet animatorSet2 = new AnimatorSet();
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
// animation.cancel();
Log.d(TAG, "onAnimationEnd: 動畫完成,開始回來動畫");
ObjectAnimator rotateObjectAnimator2 = new ObjectAnimator();
ObjectAnimator translateObjectAnimator2 = new ObjectAnimator();
rotateObjectAnimator2 = rotateObjectAnimator2.ofFloat(show_button, "rotation", 0f, 360f);
translateObjectAnimator2 = translateObjectAnimator2.ofFloat(show_button, "translationY", 200f, 150f, 100f, 50f, 0f);
animatorSet2.setDuration(2000);
animatorSet2.play(rotateObjectAnimator2).with(translateObjectAnimator2);
animatorSet2.start();
}
});
}
private void init(){
show_button = (Button)findViewById(R.id.move_button);
}
public void onAlpha(View view){
Log.d(TAG, "onAlpha: ");
//value是不定長形參數組。裏面的值代表動畫的變化過程值。
objectAnimator = objectAnimator.ofFloat(show_button, "alpha", 1f, 0.5f, 0f, 0.5f, 1f);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
public void onScale(View view){
Log.d(TAG, "onScale: ");
objectAnimator = objectAnimator.ofFloat(show_button, "scaleY", 0f, 1f, 2f, 1f);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
public void onRotate(View view){
Log.d(TAG, "onRotate: ");
objectAnimator = objectAnimator.ofFloat(show_button, "rotation", 0f, 90f, 360f);
objectAnimator.setDuration(2000);
objectAnimator.setRepeatCount(3);
objectAnimator.setRepeatMode(ValueAnimator.RESTART);
objectAnimator.start();
}
public void onTranslate(View view){
Log.d(TAG, "onTranslate: ");
objectAnimator = objectAnimator.ofFloat(show_button, "translationY", 0f, 50f, 100f, 150f, 200f);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
public void onSet(View view){
Log.d(TAG, "onSet: ");
rotateObjectAnimator1 = rotateObjectAnimator1.ofFloat(show_button, "rotation", 0f, 90f, 360f);
translateObjectAnimator1 = translateObjectAnimator1.ofFloat(show_button, "translationY", 0f, 50f, 100f, 150f, 200f);
animatorSet.setDuration(2000);
animatorSet.play(rotateObjectAnimator1).with(translateObjectAnimator1);
animatorSet.start();
}
}
屬性動畫需要記住3點,1是屬性動畫不依賴xml文件。2是ObjectAnimator類以及它的ofFloat()方法的用法。3是AnimatorSet類的用法。
總結
Android動畫類型從簡單到複雜是,幀動畫-補間動畫-屬性動畫。同時靈活程度也是如此,幀動畫可以實現一些簡單的顯示效果,如果想要移動、旋轉等操作就需要用到補間動畫了,而如果要實現是跟爲複雜或者可響應點擊事件的功能,那就只能選擇屬性動畫了。