編輯:關於Android編程
最近在看一些關於Material Design的東西,還記得在博客《你所不知道的Activity轉場動畫——ActivityOptions》中,我們介紹了一種優雅的activity過度動畫。如果大家看了最後給出的參考鏈接,會發現還有很多內容是值得我們學習的,所以這篇博客,我們來學習一下這一頁上剩下的東西。
大家都知道,在Material Design中,觸摸反饋的效果非常絢麗,是一種漣漪的效果,令我們高興的是,這種效果也是可以自定義的。
上面的代碼,我們定義了兩個Button,不同的是它們的background屬性,selectableItemBackground
代表了當點擊後會在當前Button區域發生一個漣漪效果
,而selectableItemBackgroundBorderless
的效果不會局限於Button本身的大小
。當然,雖然是大白話,但是還是不容易理解,我們來看看效果就一目了然了。
恩,效果很贊,但是這個背景能不能自定義呢?答案是當然能了,那麼這裡要引進一個新的Drawable
-RippleDrawable
了。RippleDrawable
是android5新增加的一種Drawable
,它的效果就是我們一直在提及的漣漪效果,下面我們來學習一下RippleDrawable
的時候。既然RippleDrawable
也是一種Drawable
,那麼它肯定也是可以在xml中定義的,來看代碼,
-
-
我們在drawable目錄中創建兩個xml文件,這兩個drawable都是ripple類型的,可以看到他們的根節點都是一個ripple。這兩個文件唯一的區別在於第二個的id指定了一個id是@android:id/mask
,這兩者的區別在於,
如果不指定id為
@android:id/mask
,那麼在顯示的時候會首頁顯示出item指定的drawable。
如果指定id為@android:id/mask
,那麼默認是不會顯示該drawable,而是在點擊的時候出現。
如何使用呢? 只需要指定控件的background就ok.
看一下效果怎麼樣,
通過觀察效果,我們可以發現,
漣漪的效果是在我們給item的drawable的非透明區域發生的。 當我們指定item的id為@android:id/mask
時,默認背景是不顯示的。
ok,到這裡,我們已經學會自定義點擊的漣漪效果了。我們繼續學習,接下來就來到了一個更贊的效果。
新的sdk給我們提供了一個類-ViewAnimationUtils
,該類就提供了一個靜態的方法createCircularReveal
,通過這個方法,我們可以給任何一個layout或者view一個漣漪的顯示或者消失過程。首先我們來看看要實現的效果,
效果確實很贊,代碼應該怎麼寫呢? 其實也很簡單,主要還是我們將要學習的ViewAnimationUtils
和它唯一的方法createCircularReveal
,
public void change(View view) {
int centerX = mImageView.getWidth() / 2;
int centerY = mImageView.getHeight() / 2;
int maxRadius = Math.max(mImageView.getWidth(), mImageView.getHeight());
if(mImageView.getVisibility() == View.VISIBLE) {
Animator anim = ViewAnimationUtils.createCircularReveal(mImageView,
centerX, centerY, maxRadius, 0);
anim.setDuration(1000);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mImageView.setVisibility(View.GONE);
}
});
anim.start();
}else {
Animator anim = ViewAnimationUtils.createCircularReveal(mImageView,
centerX, centerY, 0, maxRadius);
anim.setDuration(1000);
mImageView.setVisibility(View.VISIBLE);
anim.start();
}
}
在解釋代碼之前,我們來看看這個方法的詳細說明:
public static Animator createCircularReveal (View view, int centerX, int centerY, float startRadius, float endRadius)
參數部分,
View view, 指定了為哪個View執行動畫 int centerX, 漣漪效果的中心x軸位置 int centerY, 漣漪效果的中心y軸位置 float startRadius, 開始的半徑 float endRadius, 結束的半徑
在解釋完了參數之後,發現也不難,那麼下面我們就來看上面的代碼了。
首先,我們計算了centerX、centerY,還有一個最大的半徑,最小的半徑就不需要計算了,因為我們知道我們需要的效果就是到0.
接下來一個判斷是判斷ImageView的顯示狀態,如果為顯示狀態,那麼肯定就是一個從有到無的動畫,如果是隱藏狀態,那麼就是一個從無到有的動畫。
第一個狀態中,我們首先調用ViewAnimationUtils.createCircularReveal()
方法創建了一個Animator
,半徑是從maxRadius
到0的變化。
然後就是對Animator
的操作了,並且監聽了動畫的結束,在動畫結束後,我們會將該View的狀態設置為隱藏,最後啟動動畫,這裡就是我們剛才看到的那個隱藏的效果了。
那麼顯示呢?其實和隱藏是一樣的,只不過顯示半徑是從0到maxRadius
。
這一部分我們在前面的博客《你所不知道的Activity轉場動畫——ActivityOptions》
中介紹過了,大家可以去參考這篇博客,這裡就不再重復介紹了。
什麼是路徑動畫? 其實在官方文檔上,這一節叫自作Curved Motion
,我把它叫做路徑動畫,是因為這一節的內容的核心是基於Path
實現的。
Android 5 的sdk中給我們提供了一個新的插值器PathInterpolator
, 利用該插值器,我們可以輕松的定義出路徑動畫,
這段代碼定義了一個x和y分別從0.4和0到1的動畫,不過我認為這種方式局限性太大,所以這裡不過多介紹,重點我們放在在java代碼中如果利用Path去構建一個路徑動畫。
在新的sdk中,ObjectAnimator
多了很多帶有Path參數的方法,這些方法中我們可以傳遞一個Path
對象,然後我們就可以在動畫中獲取基於這個Path
的值。
public static ObjectAnimator ofFloat(Object target, String xPropertyName, String yPropertyName, Path path);
這個構造中需要我們傳遞兩個屬性值,這裡對象這Path中x和y, 最後一個參數就是最重要的那個路徑了。來看看我們的demo,這個demo,我們想讓一段文字沿著一個Z
的路徑移動,最後還原到原來的位置。
TextView tv = (TextView) findViewById(R.id.tv);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Path path = new Path();
path.moveTo(100, 100);
path.lineTo(600, 100);
path.lineTo(100, 600);
path.lineTo(600, 600);
path.close();
ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.X, View.Y, path);
anim.setDuration(5000);
anim.start();
}
});
代碼很簡單,首先我們構建了一個簡單的Path
,然後利用ObjectAnimator
的新的ofFloat
方法為為該View的x和y指定了沿path移動,最後讓動畫跑起來。
在之前,我們在使用selector
定義狀態時,僅僅只能提供一個生硬的狀態變化,在新的sdk中這裡發生了改變,我們完全可以定義一個帶動畫效果的狀態!
首先我們定義一個drawable文件,
-
-
還是我們熟悉的selector
,不過它的item是一個objectAnimator
, 這裡我們指定了translationZ
的變化,然後我們在布局中使用它,
這裡我們引入了一個新的屬性android:stateListAnimator
,我們通過這個屬性來設置我們剛才定義的selector
, 來看看效果,
仔細觀察效果,我們在點擊的過程中Button的translationZ
有一個動畫的效果。
說到這裡,如果我們可以給它的背景一個動畫就更爽了, 當然這也是可以辦到的!首先還是定義一個drawablew文件,不過這裡的文件是一個AnimatedStateListDrawable
,我們在xml中使用animated-selector
。
-
-
-
-
-
在這裡,我們首先定義了三個item, 對應了三種狀態,並且都制定了id, 接下來是一個transition
節點,這個節點下我們制定了fromId
和toId
,這裡代表了動畫從哪個狀態到哪個狀態執行,這個節點下又是一個animation-list
節點,我們指定了兩個item
,這裡要注意一下這兩個item
的執行順序,如果是從fromId
到toId
,這裡是從正常狀態到按下,則會正序執行,如果從toId
到fromId
,這裡是從按下狀態到正常,則會反序執行。這裡我們給每個item 200ms的停留時間。
從效果中可能看不出我何時點擊何時松開的,這裡每個顏色都會有一段停留時間的。
在新的sdk中, Google終於提供了原生的對SVG的支持,而這一切都是一個VectorDrawable
的功勞, 當然, VectorDrawable
也是一個Drawable
,
所以我們還是可以在xml中定義它,
這裡我們定義了一個drawable
文件,它的根節點是一個vector
,代表著它是一個VectorDrawable
,看看它的內容,又一個group
和多個path
組成,一個group
可以包含多個path
,當然這裡我們僅僅寫了一個,值得注意的是path
的android:pathData
屬性,這裡定義了我們圖形的形狀。怎麼使用呢? 就像正常的圖片一樣,給ImageView
的src
屬性賦值就ok。 這是一個什麼形狀呢?
當然,我們可以借助一些工具很輕松的實現一些pathData
。
好了, 現在僅僅是一個簡單的圖形,並沒有動畫效果, 現在我們想讓這個圖形動起來,而且顏色也要不斷變化,該怎麼做?這裡又要引入一個新的東西-animated-vector
。
利用它,我們可以根據上面書寫的name值指定不同的動畫了。
首先,我們定義兩個動畫,一個是旋轉的動畫,一個是顏色值變化的動畫。
這裡我們定義了兩個Animator
,需要注意的是他的android:propertyName
的值, 仔細觀察一下,他們分別就是上面我們定義的那個vector
的group
和path
標簽的其中一個屬性,這兩個動畫分別是一個旋轉和顏色變化的動畫, 那怎麼用呢? 我們還需要一個animated-vector
的drawable
文件。
在這個文件中,我們指定了drawable
為我們先前定義的那個vector
文件, 並且寫了兩個target
標簽,這裡很簡單,只有兩個屬性,他們分別指定了所用的動畫和這個動畫該給誰(還記得我們之前給group
和path
指定的name
屬性嗎)! ok, 現在將ImageView
的src
指定為這個animated-vector
文件, 運行一下,你可能會感到沮喪,因為沒有任何動畫! 那怎麼讓動畫跑起來呢? 很簡單。
final ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Drawable d = iv.getDrawable();
if(d instanceof Animatable) ((Animatable) d).start();
}
});
我們在點擊ImageView
的時候,去獲取這個ImageView
的圖片資源,然後判斷它是不是Animatable
類型的,如果是,直接調用它的start
方法!
最後,我們來看看這時候的效果。
有一個旋轉的動畫, 在旋轉的過程中還伴隨著顏色的變化,棒棒哒。 ok, 到這裡,Material Design動畫,我們就學習完了
最後是demo的代碼下載和參考資料。
這篇文章寫的非常好,深入淺出,關鍵還是一位大三學生自己剖析的心得。這是我喜歡此文的原因。下面請看正文:作為一個大三的預備程序員,我學習android的一大樂趣是可以通過源
android讓人頭疼的適配問題。 --------- Android 中的單位大概有這些: 常用的dip、sp,有時候用到px。 DisplayMetrics
一 概述本文是Android導航分組列表系列上,因時間和篇幅原因分上下,最終上下合璧,完整版效果如下: 上部殘卷效果如下:兩個ItemDecoration,一個
Android React Native 已經將幾個常用的原生組件進行了封裝,比如 ScrollView 和 TextInput,但是並不是所有系統的原始組件都被封裝了,