動(dòng)畫(huà)效果一直是人機(jī)交互中非常重要的部分,與死板、突兀的顯示效果不同,動(dòng)畫(huà)效果的加入,讓交互變得更加友好,特別是在提示、引導(dǎo)類(lèi)的場(chǎng)景中,合理地使用動(dòng)畫(huà)能讓用戶獲得更加愉悅的使用體驗(yàn) 一、Android View動(dòng)畫(huà)框架 Animation框架定義了透明度、旋轉(zhuǎn)、縮放、位移等幾種常見(jiàn)的動(dòng)畫(huà) 實(shí)現(xiàn)原理: 每次繪制View時(shí),ViewGroup中的drawChild函數(shù)獲取該view的Animation的Transformation值,然后調(diào)用canvas.concat(transformToApply.getMatrix()) 通過(guò)矩陣運(yùn)算完成幀動(dòng)畫(huà),如果動(dòng)畫(huà)沒(méi)有完成,就繼續(xù)調(diào)用invalidate() 函數(shù),啟動(dòng)下次繪制來(lái)驅(qū)動(dòng)動(dòng)畫(huà),從而完成整個(gè)動(dòng)畫(huà)的繪制。 二、幀動(dòng)畫(huà) 幀動(dòng)畫(huà)就是一張張圖片不同的切換,形成的動(dòng)畫(huà)效果。 一般手機(jī)的開(kāi)機(jī)動(dòng)畫(huà),應(yīng)用的等待動(dòng)畫(huà)等都是幀動(dòng)畫(huà),因?yàn)橹恍枰獛讖垐D片輪播,極其節(jié)省資源,如果真的設(shè)計(jì)成動(dòng)畫(huà),那么是很耗費(fèi)資源的事。 在res目錄下新建一個(gè)drawable文件夾并定義xml文件,子節(jié)點(diǎn)為 animation-list,在這里定義要顯示的圖片和每張圖片的顯示時(shí)長(zhǎng)。 <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"><!-- false表示循環(huán)播放,true表示只播放一次 --><item android:drawable="@drawable/g1" android:duration="200" /><item android:drawable="@drawable/g2" android:duration="200" /><item android:drawable="@drawable/g3" android:duration="200" /><item android:drawable="@drawable/g4" android:duration="200" /><item android:drawable="@drawable/g5" android:duration="200" /><item android:drawable="@drawable/g6" android:duration="300" /><item android:drawable="@drawable/g7" android:duration="400" /><!-- 慢動(dòng)作 --><item android:drawable="@drawable/g8" android:duration="500" /><item android:drawable="@drawable/g9" android:duration="200" /><item android:drawable="@drawable/g10" android:duration="200" /><item android:drawable="@drawable/g11" android:duration="200" /></animation-list>
在屏幕上播放幀動(dòng)畫(huà),需要布局文件有一個(gè)ImageView來(lái)顯示動(dòng)畫(huà)圖片 public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ImageView iv = (ImageView) findViewById(R.id.iv);//把動(dòng)畫(huà)文件設(shè)置為imageView的背景iv.setBackgroundResource(R.drawable.frameanimation);AnimationDrawable ad = (AnimationDrawable) iv.getBackground();//播放動(dòng)畫(huà)ad.start();}
}
三、補(bǔ)間動(dòng)畫(huà)(視圖動(dòng)畫(huà)) 組件由原始狀態(tài)向終極狀態(tài)轉(zhuǎn)變時(shí),為了讓過(guò)渡更自然,而自動(dòng)生成的動(dòng)畫(huà)叫做補(bǔ)間動(dòng)畫(huà)。 主要是在Android 3.0之前 最大的缺陷就是不具備交互性 位移、旋轉(zhuǎn)、縮放、透明
public class MainActivity extends Activity {private ImageView iv;private TranslateAnimation ta;private RotateAnimation ra;private ScaleAnimation sa;private AlphaAnimation aa;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv = (ImageView) findViewById(R.id.iv);}//位移動(dòng)畫(huà)public void translate(View v){//創(chuàng)建位移動(dòng)畫(huà)的對(duì)象,設(shè)置動(dòng)畫(huà)的初始位置和結(jié)束位置//10,100表示,從imageview的真實(shí)坐標(biāo)的左上角 x+10,移動(dòng)到 x+100的位置//20,200表示,從imageview的真實(shí)坐標(biāo)的左上角 y+20,移動(dòng)到 y+200的位置//ta = new TranslateAnimation(10, 100, 20, 200);//Animation.RELATIVE_TO_SELF相對(duì)于自己//對(duì)于x,表示,相對(duì)于自己,起點(diǎn)是 imageview的真是坐標(biāo)的左上角 x+ 0.5*iv的寬度 到 x+ 2*iv的寬度//y 就是乘以 iv 的高度,也可以相對(duì)于父控件,但是用的比較少ta = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 2);//設(shè)置播放時(shí)間ta.setDuration(2000);//設(shè)置重復(fù)次數(shù),播放一次,重復(fù)一次ta.setRepeatCount(1);//設(shè)置重復(fù)放的模式ta.setRepeatMode(Animation.REVERSE);//動(dòng)畫(huà)播放完畢后,組件停留在動(dòng)畫(huà)結(jié)束的位置上ta.setFillAfter(true);//播放動(dòng)畫(huà)iv.startAnimation(ta);}//旋轉(zhuǎn)動(dòng)畫(huà)public void rotate(View v){//20表示開(kāi)始的角度,180表示結(jié)束的角度,默認(rèn)的旋轉(zhuǎn)圓心在iv左上角//ra = new RotateAnimation(20, 180);//指定圓心坐標(biāo),相對(duì)于自己, iv真實(shí)的坐標(biāo)左上角 x+ 0.5*iv寬度, y+0.5*iv高度ra = new RotateAnimation(20, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);ra.setDuration(2000);ra.setRepeatCount(1);ra.setRepeatMode(Animation.REVERSE);iv.startAnimation(ra);}//縮放動(dòng)畫(huà)public void scale(View v){//sa = new ScaleAnimation(fromX, toX, fromY, toY);//改變縮放的中心點(diǎn),相對(duì)于自己的中心坐標(biāo),iv的真是坐標(biāo)左上角 x+0.5*iv寬度, y+0.5*iv高度sa = new ScaleAnimation(0.5f, 2, 0.1f, 3, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);sa.setDuration(2000);sa.setRepeatCount(1);sa.setRepeatMode(Animation.REVERSE);iv.startAnimation(sa);}//透明動(dòng)畫(huà)public void alpha(View v){//0為完全透明,1為完全透明aa = new AlphaAnimation(0, 1);aa.setDuration(2000);aa.setRepeatCount(1);aa.setRepeatMode(Animation.REVERSE);iv.startAnimation(aa);}//所有動(dòng)畫(huà)一起飛public void fly(View v){//創(chuàng)建動(dòng)畫(huà)集合 false表示每個(gè)動(dòng)畫(huà)的時(shí)間校準(zhǔn)有動(dòng)畫(huà)自己決定, true表示有動(dòng)畫(huà)集合決定AnimationSet set = new AnimationSet(false);set.addAnimation(aa);set.addAnimation(ra);set.addAnimation(sa);set.addAnimation(ta);iv.startAnimation(set);}}
四、屬性動(dòng)畫(huà) 補(bǔ)間動(dòng)畫(huà),只是一個(gè)動(dòng)畫(huà)效果,組件其實(shí)還在原來(lái)的位置上,xy沒(méi)有改變。 屬性動(dòng)畫(huà)是組件的位置發(fā)生了真實(shí)的改變,而且在動(dòng)畫(huà)的過(guò)程中組件的位置是實(shí)時(shí)改變的,可以相應(yīng)組件事件。 使用最多的就是 AnimatorSet和ObjectAnimator配合: 使用ObjectAnimator進(jìn)行更精細(xì)化控制,只控制一個(gè)對(duì)象的一個(gè)屬性值 使多個(gè)ObjectAnimator組合到AnimatorSet形成一個(gè)動(dòng)畫(huà) ObjectAnimator可以自動(dòng)驅(qū)動(dòng): 調(diào)用setFrameDelay()設(shè)置動(dòng)畫(huà)幀之間的間隙時(shí)間,調(diào)整幀率,減少動(dòng)畫(huà)繪制過(guò)程中頻繁繪制,在不影響動(dòng)畫(huà)效果的情況下減少CPU資源消耗 屬性動(dòng)畫(huà)基本可以實(shí)現(xiàn)左右動(dòng)畫(huà) 但是View的該屬性一定要具有 set和get 方法 1.ObjectAnimator 內(nèi)部是通過(guò)反射機(jī)制實(shí)現(xiàn)的,所以該屬性一定要具有 set和get方法 一些常用的可以直接使用的屬性: translationX 和translationY: 控制View從父容器的左上角的偏移 rotation、rotationX、rotationY : 控制View圍繞 “支點(diǎn)”進(jìn)行2D和3D旋轉(zhuǎn) scaleX、scaleY : 控制View圍繞 “支點(diǎn)”進(jìn)行2D縮放 pivotX、pivotY : 控制View的“支點(diǎn)”位置,默認(rèn)為View的中心點(diǎn) x、y : 這兩個(gè)屬性描述了View對(duì)象在父容器中的最終位置,它是最初左上角坐標(biāo)和translationX 、translationY值的累計(jì)和 alpha : 透明度
public class MainActivity extends Activity {private ImageView iv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);iv = (ImageView) findViewById(R.id.iv);iv.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(MainActivity.this, "點(diǎn)不到我", 0).show();}});}public void translate(View v){//target:動(dòng)畫(huà)作用于哪個(gè)組件//protertyName指定要改變組件的哪個(gè)屬性, //屬性動(dòng)畫(huà)是真正的改變組件的屬性的,每個(gè)組件對(duì)應(yīng)一個(gè)java類(lèi),所以,protertyName是指組件Java類(lèi)中具有g(shù)et,set方法的成員變量//iv.setXXX//values 是可變參數(shù),就是賦予屬性的新的值,可以往回走 //ObjectAnimator oa = ObjectAnimator.ofFloat(target, propertyName, values);ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 10, 70, 20, 100);oa.setDuration(2000);oa.setRepeatCount(1);oa.setRepeatMode(ValueAnimator.REVERSE);oa.start();}public void scale(View v){ObjectAnimator oa = ObjectAnimator.ofFloat(iv,"scaleX", 1, 1.6f, 1.2f, 2);oa.setDuration(2000);oa.start();}public void alpha(View v){ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "alpha", 0, 0.6f, 0.2f, 1);oa.setDuration(2000);oa.start();}public void rotate(View v){ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "rotationY", 0, 180, 90, 360);oa.setDuration(2000);oa.setRepeatCount(1);oa.setRepeatMode(ValueAnimator.REVERSE);oa.start();}public void fly(View v){AnimatorSet set = new AnimatorSet();ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 10, 70, 20, 100);oa1.setDuration(2000);oa1.setRepeatCount(1);oa1.setRepeatMode(ValueAnimator.REVERSE);ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "translationY", 10, 70, 20, 100);oa2.setDuration(2000);oa2.setRepeatCount(1);oa2.setRepeatMode(ValueAnimator.REVERSE);ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1, 1.6f, 1.2f, 2);oa3.setDuration(2000);oa3.setRepeatCount(1);oa3.setRepeatMode(ValueAnimator.REVERSE);ObjectAnimator oa4 = ObjectAnimator.ofFloat(iv, "rotation", 0, 180, 90, 360);oa4.setDuration(2000);oa4.setRepeatCount(1);oa4.setRepeatMode(ValueAnimator.REVERSE);//設(shè)置挨個(gè)飛//set.playSequentially(oa1, oa2, oa3, oa4);//設(shè)置一起飛set.playTogether(oa1, oa2, oa3, oa4);set.start();}//使用xml文件配置屬性動(dòng)畫(huà)public void xml(View v){//加載屬性動(dòng)畫(huà)文件Animator animator = AnimatorInflater.loadAnimator(this, R.animator.objanimator);//設(shè)置作用于哪個(gè)組件animator.setTarget(iv);animator.start();}
}
可以用xml配置屬性動(dòng)畫(huà)只需要在res目錄下創(chuàng)建一個(gè)property animator屬性動(dòng)畫(huà)文件 <set xmlns:android="http://schemas.android.com/apk/res/android" ><objectAnimator android:propertyName="translationX"android:duration="200"android:repeatCount="1"android:repeatMode="reverse"android:valueFrom="-100"android:valueTo="100"></objectAnimator></set>
2. 如果屬性沒(méi)有set 和 get方法的解決方法 方案一: 通過(guò)自定義一個(gè)屬性類(lèi)或者包裝類(lèi),類(lèi)間接的給這個(gè)屬性增加 get、set方法 /*** Created at: 2016/8/5 14:21.* by author: mwp* 描述:使用屬性動(dòng)畫(huà)時(shí),給沒(méi)有set和get方法的屬性包裝工具*/
public class WrapperView {private View mTarget;public WrapperView(View target) {this.mTarget = target;}/**包裝寬度屬性*/public int getWidth(){return mTarget.getLayoutParams().width;}public void setWidth(int width){mTarget.getLayoutParams().width = width;mTarget.requestLayout();}//使用方法public void use(){WrapperView wrapper = new WrapperView(mButton);ObjectAnimator.ofInt(wrapper,"width",500).setDuration(5000).start();}}
方案二: 通過(guò)ValueAnimator實(shí)現(xiàn) 3. ValueAnimator(值動(dòng)畫(huà)) ObjectAnimator 也是集成自ValueAnimator ValueAnimator本身不提供任何動(dòng)畫(huà)效果,它更像一個(gè)數(shù)值發(fā)生器,用來(lái)產(chǎn)生具有一定規(guī)律的數(shù)字,調(diào)用者通過(guò)這個(gè)過(guò)程來(lái)執(zhí)行自己特定的動(dòng)畫(huà)邏輯 ValueAnimator animator = ValueAnimator.ofInt(0,100);
animator.setTarget(view);
animator.setDuration(1000).start();
//最核心的方法
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 進(jìn)度百分比f(wàn)loat value = animation.getAnimatedFraction();}
});
4. 動(dòng)畫(huà)監(jiān)聽(tīng) 監(jiān)聽(tīng)動(dòng)畫(huà)的過(guò)程執(zhí)行一些操作 ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha",0.5f);
anim.addListener(new Animator.AnimatorListener() {@Overridepublic void onAnimationStart(Animator animation) {}@Overridepublic void onAnimationEnd(Animator animation) {}@Overridepublic void onAnimationCancel(Animator animation) {}@Overridepublic void onAnimationRepeat(Animator animation) {}
});anim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationStart(Animator animation) {super.onAnimationEnd(animation);}
});
這里邊有一個(gè)重要的思想,如果一個(gè)接口需要實(shí)現(xiàn)的方法比較多,而通常時(shí)候又不需要實(shí)現(xiàn)那么多方法,導(dǎo)致代碼亂亂的,這個(gè)時(shí)候,可以寫(xiě)一個(gè)抽象類(lèi)來(lái)實(shí)現(xiàn)這個(gè)接口的所有方法,由于接口的實(shí)現(xiàn)類(lèi)也是接口類(lèi)型,所以使用的時(shí)候就可以只復(fù)寫(xiě)這個(gè)抽象類(lèi)中感興趣的方法就好了 5. View的animate方法 在3.0之后,Google給View增加了animate方法來(lái)直接驅(qū)動(dòng)屬性動(dòng)畫(huà) view.animate().alpha(0).scaleX(1).x(300).y(200).setDuration(1000).withStartAction(new Runnable() {@Overridepublic void run() {//動(dòng)畫(huà)開(kāi)始}}).withEndAction(new Runnable() {@Overridepublic void run() {//動(dòng)畫(huà)結(jié)束runOnUiThread(new Runnable() {@Overridepublic void run() {//執(zhí)行一些主線程方法}});}}).start();
6. Android 布局動(dòng)畫(huà) 布局動(dòng)畫(huà)是指作用在ViewGroup上,給ViewGroup增加View時(shí)添加一個(gè)動(dòng)畫(huà)過(guò)渡效果 最簡(jiǎn)單的布局動(dòng)畫(huà)是在ViewGroup的xml中使用如下屬性打開(kāi)布局動(dòng)畫(huà) android:animateLayoutChanges="true" 但是這個(gè)默認(rèn)的動(dòng)畫(huà)效果無(wú)法替換 使用LayoutAnimationController類(lèi)來(lái)自定義一個(gè)過(guò)渡效果 LinearLayout ll = (LinearLayout)findViewById(R.id.ll);
//設(shè)置過(guò)渡動(dòng)畫(huà)
ScaleAnimation sa = new ScaleAnimation(0,1,0,1);
sa.setDuration(2000);
//設(shè)置布局動(dòng)畫(huà)的顯示屬性,第二個(gè)參數(shù)是每個(gè)子View顯示的delay時(shí)間
LayoutAnimationController lac = new LayoutAnimationController(sa,0.5f);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);//順序,隨機(jī),倒序
//為ViewGroup設(shè)置布局動(dòng)畫(huà)
ll.setLayoutAnimation(lac);
7. Interpolators(插值器 ) 插值器是動(dòng)畫(huà)中一個(gè)非常重要的概念,通過(guò)插值器可以定義動(dòng)畫(huà)變換速率,其作用主要是控制目標(biāo)變量的變化值進(jìn)行對(duì)應(yīng)的變化 AccelerateDecelerateInterpolator開(kāi)始與結(jié)束的地方速率改變比較慢,在中間的時(shí)候加速 AccelerateInterpolator開(kāi)始的地方速率改變比較慢,然后開(kāi)始加速 AnticipateInterpolator開(kāi)始的時(shí)候向后然后向前甩 AnticipateOvershootInterpolator開(kāi)始的時(shí)候向后然后向前甩一定值后返回最后的值 BounceInterpolator動(dòng)畫(huà)結(jié)束的時(shí)候彈起 CycleInterpolator循環(huán)播放特定的次數(shù),速率改變沿著正弦曲線 DecelerateInterpolator在開(kāi)始的地方快然后慢 創(chuàng)建的時(shí)候,可以傳factor值,如DecelerateInterpolator(2f): LinearInterpolator以常量速率改變 OvershootInterpolator向前甩一定值后再回到原來(lái)位置 創(chuàng)建的時(shí)候,可以傳tension值,OvershootInterpolator(0.8f): 五、自定義動(dòng)畫(huà) 就是現(xiàn)有的透明度,旋轉(zhuǎn),平移,縮放等行為組合起來(lái)仍然不能滿足你的話,可以自定義一些更炫的動(dòng)畫(huà) public class CustomAnim extends Animation {private int mCenterWidth;private int mCenterHeight;private Camera mCamera = new Camera();private float mRotateY = 0.0f;@Overridepublic void initialize(int width,int height,int parentWidth,int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);// 設(shè)置默認(rèn)時(shí)長(zhǎng)setDuration(2000);// 動(dòng)畫(huà)結(jié)束后保留狀態(tài)setFillAfter(true);// 設(shè)置默認(rèn)插值器setInterpolator(new BounceInterpolator());mCenterWidth = width / 2;mCenterHeight = height / 2;}// 暴露接口-設(shè)置旋轉(zhuǎn)角度public void setRotateY(float rotateY) {mRotateY = rotateY;}@Overrideprotected void applyTransformation(float interpolatedTime,Transformation t) {final Matrix matrix = t.getMatrix();mCamera.save();// 使用Camera設(shè)置旋轉(zhuǎn)的角度mCamera.rotateY(mRotateY * interpolatedTime);// 將旋轉(zhuǎn)變換作用到matrix上mCamera.getMatrix(matrix);mCamera.restore();// 通過(guò)pre方法設(shè)置矩陣作用前的偏移量來(lái)改變旋轉(zhuǎn)中心matrix.preTranslate(mCenterWidth, mCenterHeight);matrix.postTranslate(-mCenterWidth, -mCenterHeight);}
}
六、Android 5.X SVG 矢量動(dòng)畫(huà)機(jī)制 可伸縮矢量圖形(Scalable Vector Graphics) 定義用于網(wǎng)絡(luò)的基于矢量的圖形 使用XML格式定義圖形 圖像在放大或改變尺寸的情況下其圖形質(zhì)量不會(huì)有損失 與Bitmap對(duì)比,SVG最大的優(yōu)點(diǎn)就是方法不失真,而且不需要為不同分辨率設(shè)計(jì)多套圖標(biāo) 七、點(diǎn)擊view顯示隱藏其他View帶動(dòng)畫(huà)的一個(gè)小例子 public class DropTest extends Activity {private LinearLayout mHiddenView;private float mDensity;private int mHiddenViewMeasuredHeight;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.drop);mHiddenView = (LinearLayout) findViewById(R.id.hidden_view);// 獲取像素密度mDensity = getResources().getDisplayMetrics().density;// 獲取布局的高度mHiddenViewMeasuredHeight = (int) (mDensity * 40 + 0.5);}public void llClick(View view) {if (mHiddenView.getVisibility() == View.GONE) {// 打開(kāi)動(dòng)畫(huà)animateOpen(mHiddenView);} else {// 關(guān)閉動(dòng)畫(huà)animateClose(mHiddenView);}}private void animateOpen(final View view) {view.setVisibility(View.VISIBLE);ValueAnimator animator = createDropAnimator(view,0,mHiddenViewMeasuredHeight);animator.start();}private void animateClose(final View view) {int origHeight = view.getHeight();ValueAnimator animator = createDropAnimator(view, origHeight, 0);animator.addListener(new AnimatorListenerAdapter() {public void onAnimationEnd(Animator animation) {view.setVisibility(View.GONE);}});animator.start();}private ValueAnimator createDropAnimator(final View view, int start, int end) {ValueAnimator animator = ValueAnimator.ofInt(start, end);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {int value = (Integer) valueAnimator.getAnimatedValue();ViewGroup.LayoutParams layoutParams =view.getLayoutParams();layoutParams.height = value;view.setLayoutParams(layoutParams);}});return animator;}
}
轉(zhuǎn)載于:https://www.cnblogs.com/rocomp/p/5742056.html
總結(jié)
以上是生活随笔 為你收集整理的Android 动画机制与使用技巧 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。