一.View動(dòng)畫的介紹
Android的動(dòng)畫分為兩種:View動(dòng)畫(幀動(dòng)畫也屬于View動(dòng)畫)漾岳,屬性動(dòng)畫墓卦。
View動(dòng)畫的作用對(duì)象是View十酣,它支持4種動(dòng)畫效果:平移動(dòng)畫灾杰,縮放動(dòng)畫,旋轉(zhuǎn)動(dòng)畫飘蚯,透明度動(dòng)畫馍迄。幀動(dòng)畫也屬于View動(dòng)畫,但幀動(dòng)畫的表現(xiàn)形式和上面的四種變換效果不太一樣局骤。
本文將介紹:
- View動(dòng)畫的四種類型以及動(dòng)畫集合set
- View動(dòng)畫的使用
- View動(dòng)畫的監(jiān)聽
- 自定義View動(dòng)畫
- 幀動(dòng)畫
- View動(dòng)畫的特殊使用場(chǎng)景
二.View動(dòng)畫的種類
View動(dòng)畫四種變換效果對(duì)應(yīng)著Animation的四個(gè)子類:
- TranslateAnimation(平移動(dòng)畫,<translate>)
- ScaleAnimation(縮放動(dòng)畫,<scale>)
- RotateAnimation(旋轉(zhuǎn)動(dòng)畫,<rotate>)
- AlphaAnimation(透明度動(dòng)畫<alpha>)攀圈。
這四種動(dòng)畫既可以通過XML來定義,也可以通過代碼來動(dòng)態(tài)創(chuàng)建峦甩。對(duì)于View動(dòng)畫來說赘来,建議采用XML來定義動(dòng)畫,因?yàn)閄ML格式的動(dòng)畫可讀性更好凯傲。
View動(dòng)畫的XML格式的文件路徑為:res/anim/**.xml犬辰。
三.View動(dòng)畫的四種類型的xml格式基本屬性
所有value值都為float類型。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:repeatMode="reverse"
android:shareInterpolator="true">
<translate
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="300"
android:toYDelta="300" />
<alpha
android:fromAlpha="0.1"
android:toAlpha="1.0" />
<scale
android:fromXScale="1.0"
android:fromYScale="0.5"
android:pivotX="10"
android:pivotY="10"
android:toXScale="1.5"
android:toYScale="1.5" />
<rotate
android:fromDegrees="0"
android:pivotX="10"
android:pivotY="10"
android:toDegrees="180" />
</set>
1.動(dòng)畫集合set
對(duì)應(yīng)AnimationSet類,可以包含若干個(gè)動(dòng)畫冰单,并且它的內(nèi)部也是可以嵌套其他動(dòng)畫集合的
- android:interpolator:表示動(dòng)畫集合所采用的插值器幌缝,插值器影響動(dòng)畫的速度,比如非勻速動(dòng)畫就需要通過插值器來控制動(dòng)畫的播放過程诫欠。該屬性可以不指定涵卵,默認(rèn)為@android:anim/accelerate_decelerate_interpolator浴栽,即加速減速插值器。
- android:shareInterpolator:表示集合中的動(dòng)畫是否和集合共享同一個(gè)插值器轿偎。如果不指定插值器典鸡,那么子動(dòng)畫就需要單獨(dú)指定所需的插值器或者使用默認(rèn)值。
2.1 平移動(dòng)畫translate
對(duì)應(yīng)TranslateAnimation類贴硫,View在水平和豎直方向的平移
- android:fromXDelt:表示x的起始值
- android:fromYDelt:表示y的起始值
- android:toXDelta:表示x的結(jié)束值
- android:toYDelta:表示y的結(jié)束值
2.2透明度動(dòng)畫alpha
對(duì)應(yīng)AlphaAnimation類椿每,改變View的透明度
- android:fromAlpha:表示透明度的起始值,比如0.1
- android:toAlpha:表示透明度的結(jié)束值英遭,比如1
2.3縮放動(dòng)畫scale
對(duì)應(yīng)ScaleAnimation類,使View具有放大和縮小的動(dòng)畫效果
- android:fromXScale:水平方向縮放的起始值
- android:fromYScale:豎直方向縮放的起始值
- android:pivotX:縮放的軸點(diǎn)x坐標(biāo)亦渗,會(huì)影響縮放的效果
- android:pivotY:縮放的軸點(diǎn)y坐標(biāo)挖诸,會(huì)影響縮放的效果
- android:toXScale:水平方向的結(jié)束值
- android:toYScale:豎直方向的結(jié)束值
2.4旋轉(zhuǎn)動(dòng)畫rotate
對(duì)應(yīng)RotateAnimation類,使View具有旋轉(zhuǎn)的動(dòng)畫效果
- android:fromDegrees:旋轉(zhuǎn)開始的角度
- android:pivotX:旋轉(zhuǎn)的軸點(diǎn)x坐標(biāo)
- android:pivotY:旋轉(zhuǎn)的軸點(diǎn)y坐標(biāo)
- android:toDegrees:旋轉(zhuǎn)結(jié)束的角度
View動(dòng)畫還有一些常用的屬性:
- android:duration:動(dòng)畫的持續(xù)時(shí)間法精;
- android:fillAfter:動(dòng)畫結(jié)束以后View是否停留在結(jié)束位置多律,true表示View停留在結(jié)束位置,false則不停留搂蜓。
四.View 動(dòng)畫的使用
1.使用xml格式定義View動(dòng)畫
TextView tv = findViewById(R.id.tv);
Animation animation = AnimationUtils.loadAnimation(this,R.anim.animation_tv);
tv.startAnimation(animation);
2.使用通過代碼創(chuàng)建的View 動(dòng)畫
/**這里寫一個(gè)透明度動(dòng)畫狼荞,其他類型動(dòng)畫寫法類似*/
AlphaAniamation alphaAnimation = new AlphaAnimation(0,1);//透明度由0到1
alphaAnimation.setDuration(300);//動(dòng)畫時(shí)間300ms
tv.startAnimation(alphaAnimation);
五.View 動(dòng)畫的監(jiān)聽
通過Animation的setAnimationListener給View動(dòng)畫添加過程監(jiān)聽。
public static interface AnimationListener {
void onAnimationStart(Animation animation);
void onAnimationEnd(Animation animation);
void onAnimationRepeat(Animation animation);
}
六.自定義View 動(dòng)畫
自定義view動(dòng)畫既簡(jiǎn)單又復(fù)雜帮碰。簡(jiǎn)單是因?yàn)橹恍枥^承Animation這個(gè)抽象類相味,然后重寫initialize和applyTransformation方法就好了,在initialize方法中做一些初始化工作殉挽,在applyTransformation中進(jìn)行相應(yīng)的矩陣變換即可丰涉,里面經(jīng)常需要采用Camera來簡(jiǎn)化矩陣變換的過程。復(fù)雜是因?yàn)樽远xview動(dòng)畫的過程主要是矩陣變換的過程斯碌,需要有線性代數(shù)的功底一死。
我們就來個(gè)例子吧,谷歌 APIDemo 里的自定義3d動(dòng)畫Rotate3dAnimation傻唾。圍繞y軸旋轉(zhuǎn)并且同時(shí)沿著z軸平移從而實(shí)現(xiàn)類似于3d的效果投慈。先上效果圖,效果圖如下冠骄。
代碼量很少伪煤,如下圖。
public class Rotate3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private final float mDepthZ;
private final boolean mReverse;
private Camera mCamera;
float scale = 1; // <------- 像素密度
/**
* 創(chuàng)建一個(gè)繞y軸旋轉(zhuǎn)的3D動(dòng)畫效果猴抹,旋轉(zhuǎn)過程中具有深度調(diào)節(jié)带族,可以指定旋轉(zhuǎn)中心。
* @param context <------- 添加上下文,為獲取像素密度準(zhǔn)備
* @param fromDegrees 起始時(shí)角度
* @param toDegrees 結(jié)束時(shí)角度
* @param centerX 旋轉(zhuǎn)中心x坐標(biāo)
* @param centerY 旋轉(zhuǎn)中心y坐標(biāo)
* @param depthZ 最遠(yuǎn)到達(dá)的z軸坐標(biāo)
* @param reverse true 表示由從0到depthZ蟀给,false相反
*/
public Rotate3dAnimation(Context context, float fromDegrees, float toDegrees,
float centerX, float centerY, float depthZ, boolean reverse) {
mFromDegrees = fromDegrees;
mToDegrees = toDegrees;
mCenterX = centerX;
mCenterY = centerY;
mDepthZ = depthZ;
mReverse = reverse;
// 獲取手機(jī)像素密度 (即dp與px的比例)
scale = context.getResources().getDisplayMetrics().density;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
// 調(diào)節(jié)深度
if (mReverse) {
camera.translate(0.0f, 0.0f, mDepthZ * interpolatedTime);
} else {
camera.translate(0.0f, 0.0f, mDepthZ * (1.0f - interpolatedTime));
}
// 繞y軸旋轉(zhuǎn)
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
// 修正失真蝙砌,主要修改 MPERSP_0 和 MPERSP_1
float[] mValues = new float[9];
matrix.getValues(mValues); //獲取數(shù)值
mValues[6] = mValues[6]/scale; //數(shù)值修正
mValues[7] = mValues[7]/scale; //數(shù)值修正
matrix.setValues(mValues); //重新賦值
// 調(diào)節(jié)中心點(diǎn)
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
使用:我這里是在fragment里寫的阳堕,布局文件這里沒貼出來,因?yàn)榫褪且粋€(gè)簡(jiǎn)單的ImageView择克。
ImageView imageView = view.findViewById(R.id.iv);
//括號(hào)內(nèi)參數(shù)分別為(上下文恬总,開始角度,結(jié)束角度肚邢,x軸中心點(diǎn)壹堰,y軸中心點(diǎn),深度骡湖,是否扭曲)
final Rotate3dAnimation rotation = new Rotate3dAnimation(Objects.requireNonNull(getContext()),
0, 360, 150, 75, 0f, true);
rotation.setDuration(3000); //設(shè)置動(dòng)畫時(shí)長(zhǎng)
rotation.setRepeatCount(5); //設(shè)置重復(fù)次數(shù)
rotation.setFillAfter(true); //保持旋轉(zhuǎn)后效果
rotation.setInterpolator(new LinearInterpolator()); //設(shè)置插值器
imageView.startAnimation(rotation);
七.幀動(dòng)畫
幀動(dòng)畫也是屬于View動(dòng)畫贱纠。幀動(dòng)畫是順序播放一組預(yù)先定義好的圖片。系統(tǒng)提供了單獨(dú)一個(gè)類AnimationDrawable來使用幀動(dòng)畫响蕴。幀動(dòng)畫使用比較簡(jiǎn)單谆焊,通過XML來定義一個(gè)AnimationDrawable,其地址為:res/drawable/***.xml浦夷。
在drawable目錄下新建一個(gè)以根節(jié)點(diǎn)為animaion-list的xml文件辖试。一個(gè)item對(duì)應(yīng)一幀圖片。android:oneshot="true",只播放一次劈狐,android:oneshot="false"無限循環(huán)罐孝,如果不設(shè)置則值默認(rèn)為false無限循環(huán)。代碼如下圖所示肥缔。
<?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/img1"
android:duration="500" />
<item
android:drawable="@drawable/img2"
android:duration="500" />
<item
android:drawable="@drawable/img3"
android:duration="500" />
</animation-list>
將上述的drawable作為View的背景并通過Draw able來播放動(dòng)畫即可莲兢。使用如下圖。
View v = findViewById(R.id.iv);
v.setBackgroundResource(R.drawable.animation_frame);
AnimationDrawable animationDrawable = (AnimationDrawable) v.getBackground();
animationDrawable.start();
幀動(dòng)畫使用簡(jiǎn)單辫继,但是注意圖片不要太大怒见,否則容易引起OOM。
八.View動(dòng)畫的特殊使用場(chǎng)景
View動(dòng)畫除了上面介紹的四種以外姑宽,還可以在一些特殊的場(chǎng)景下使用遣耍,比如:
在ViewGroup中控制子元素的出場(chǎng)效果。
在Activity中實(shí)現(xiàn)不同Activity之間的切換炮车。
1.ViewGroup中控制子元素的出場(chǎng)效果——LayoutAnimation
LayoutAnimaton作用于ViewGroup舵变,為View Group指定一個(gè)動(dòng)畫,這樣當(dāng)它的子元素出場(chǎng)時(shí)都會(huì)具有這種動(dòng)畫效果瘦穆。
LayoutAnimaton有四個(gè)屬性:animationOrder纪隙,animation,delay扛或,interpolator绵咱。
- animationOrder:有三個(gè)選項(xiàng):normal,reverse,random。normal表示順序顯示(即排在前面的子元素先開始播放入場(chǎng)動(dòng)畫)熙兔,reverse表示逆向顯示(即排在后面的子元素先開始播放入場(chǎng)動(dòng)畫)悲伶,randow表示隨機(jī)播放入場(chǎng)動(dòng)畫艾恼。
- animation:為子元素指定具體的入場(chǎng)動(dòng)畫。
- delay:表示子元素開始動(dòng)畫的時(shí)間延遲麸锉。即delay=第一個(gè)子元素動(dòng)畫時(shí)間/第二個(gè)子元素動(dòng)畫時(shí)間钠绍。怎么理解呢,打個(gè)比方花沉,如果delay的值為0.5柳爽,那么第一個(gè)子元素動(dòng)畫執(zhí)行到一半的時(shí)候,第二個(gè)子元素動(dòng)畫開始碱屁;如果delay的值為1磷脯,那么第一個(gè)子元素動(dòng)畫執(zhí)行完后才開始緊接著執(zhí)行;
- interpolator:插值器娩脾,控制動(dòng)畫變化的速率争拐,不寫則默認(rèn)為是加速減速插值器。
我們有個(gè)經(jīng)典的例子——RecycleView的item都以一定的動(dòng)畫的形式出現(xiàn)晦雨。我們先上效果圖。
我們現(xiàn)在就開始寫RecycleView的item的動(dòng)畫啦隘冲,關(guān)鍵步驟和代碼如下:
1.在res/anim目錄中新建一個(gè)根節(jié)點(diǎn)為set的anim_recycle_view_item.xml文件闹瞧,這里將其作為子元素入場(chǎng)動(dòng)畫(每個(gè)Item的動(dòng)畫),代碼如下圖展辞。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fillAfter="false"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<translate
android:fromXDelta="-500"
android:toXDelta="0" />
</set>
2.在res/anim目錄中新建一個(gè)根節(jié)點(diǎn)為layoutAnimation 的anim_recycle_view.xml文件奥邮,在里面引用步驟1寫好的子元素動(dòng)畫即可。代碼如下圖罗珍。
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/anim_recycle_view_item"
android:animationOrder="normal"
android:delay="0.5">
</layoutAnimation>
3.給RecycleView添加layoutAnimation有2種方式洽腺,布局中添加或者java代碼添加。
- 方式一:在布局文件中對(duì)RecycleView添加layoutAnimation屬性覆旱,引用步驟2寫好的layoutAnimation即可,代碼如下:
<?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:background="#ebf2fa"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layoutAnimation="@anim/anim_recycle_view"
android:layout_height="match_parent"
android:padding="5dip" />
</LinearLayout>
- 方式二:在java代碼中對(duì)RecycleView添加layoutAnimation屬性蘸朋,引用步驟2寫好的layoutAnimation即可,代碼如下:
RecyclerView recyclerView=findViewById(R.id.recycleView);
Animation animation= AnimationUtils.loadAnimation(this,R.anim.recycle_view);
LayoutAnimationController controller=new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
recyclerView.setLayoutAnimation(controller);
4.其它代碼就是正常的RecycleView的使用了扣唱,比如java代碼和item的布局代碼這里就不貼啦∨号鳎現(xiàn)在運(yùn)行一下,就可以看見跟效果圖的一樣的動(dòng)畫啦噪沙。
2.Activity的切換效果
Activity是有默認(rèn)的切換效果炼彪,我們也可以自定義這個(gè)Activity的切換效果,主要用到overridePendingTransition(int enterAnim,int exitAnim)這個(gè)方法正歼,但是該方法必須寫在在startActivity(Intent intent)或者finish()之后才能生效辐马,不然動(dòng)畫效果將不起作用。
- enterAnim——Activity被打開時(shí)局义,所需要的動(dòng)畫資源id喜爷。
- exitAnim——Activity被暫停時(shí)所需要的動(dòng)畫資源id冗疮。
啟動(dòng)Activity時(shí)添加自定義的切換效果,代碼如下:
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
退出Activity時(shí)添加自定義的切換效果贞奋,代碼如下:
@Override
public void finish() {
super.finish();
overridePendingTransition(R.anim.enter_anim,R.anim.exit_anim);
}