Android 2D Graphics

Android 爲我們提供了一個用來繪製圖片與動畫2D的圖像庫,這兩個包分別是android.graphics.drawable 和 android.view.animation ,在這兩個包中可以找到相同的類去呈現繪圖與動畫的兩個不同面。 在這個文檔中將介紹如何在你的Android應用程序中使用這個庫。我們將討論基礎類Drawable對象如何繪圖,如何使用一對Drawable的子類,還有如何去創建圖片和動畫。

1 Drawable Drawable

是一個通用的抽象類,它的目的是告訴你什麼東西是可以畫的。你會發現基於Drawable類擴展 出各種繪圖的類包括:BitmapDrawable ShapeDrawable PictureDrawable LayerDrawable,當然你 可以繼承它來創建你自己的繪圖類. 有三種方法可以定義和實例化一個Drawable,保存一個圖片到你工程資源中,使用XML文件來描述 Drawable屬性或者用一個正常的類去構造。下面我們將討論兩種技術(對一個有開發經驗的開發者來說構 造並不是最新的技術) 

 

1.1 從資源圖像文件中創建 一個比較簡單的方法是添加一個圖片到你的程序中,然後通過資源文件引用這個文件,支持的文件類型 有PNG(首選的) JPG(可接受的)GIF(不建議),顯然這種對於顯示應用程序的圖標跟來說是首選的方 法,也可以用來顯示LOGO,其餘的圖片可以用在例如遊戲中。 把一個圖片資源,添加你的文件到你工程中res/drawable/目錄中去,從這裏,你就可以引用它到你的代碼 或你的XML佈局中,另一種方法,引用它也可以用資源編號,比如你選擇一個文件只要去掉後綴就可以了 (例如:my_image.png 引用它是就是my_image) 

1.2 從XML文件中創建 到如今,你應該比較熟悉按Android的原則去開發一個用戶接口,因此,你也應該理解了定義一個XML文 件對於對象的作用與靈活的重要性。這個理念無數次用於Drawables 如果你想創建一個Drawable對象,而這個對象並不依賴於變量或用戶的交換,把它定義到XML中去應該 是一個不錯的方法。即使你期望在你的應用程序中改變其屬性來增加用戶體驗。你應該考慮把對象放入 XML中,因爲你可以隨時修改其屬性。 當你在你的XML中定義了一個Drawable,保存這個XML文件到你工程目錄下res/drawable目錄中,然 後通過調用Resource.getDrawable()來檢索並實例化,傳遞給它XML文件中的資源ID號。 任何Drawable的子類都支持inflate這個方法,這個方法會通過XML來實例化你的程序。任何Drawable 都支持XML的擴展來利用特殊的XML屬性來幫助定義對象的屬性,可以查看任何Drawable子類文檔來看 如何定義XML文件 下面的XML定義了TransitionDrawable:  

 

<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/image_expand">
<item android:drawable="@drawable/image_collapse">

</transition> 


  2 ShapeDrawable

當你想去畫一些動態的二維圖片,一個ShapeDrawable對象可能會對你有很大的幫助。通過ShapeDrawable,你可以通過編程畫出任何你想到的圖像與樣式
ShapeDrawable繼承了Drawable,所以你可以調用Drawable裏有的函數,比如視圖的背景,通過setBackgroundDrawable()設置。當然,你可以在自定義的視圖佈局中畫你的圖形,因爲ShapeDrawable有自己的draw()方法。你可以創建一個視圖的子類去畫ShapeDrawable在View.OnDraw()方法期間。這是一個基本的視圖擴展類去畫ShapeDrawable

 

複製代碼
public class CustomDrawableView extends View {
private ShapeDrawable mDrawable;
public CustomDrawableView(Context context) {
super(context);
int x = 10;
int y = 10;
int width = 300;
int height = 50;
mDrawable = new ShapeDrawable(new OvalShape());
mDrawable.getPaint().setColor(0xff74AC23);
mDrawable.setBounds(x, y, x + width, y + height);
}
protected void onDraw(Canvas canvas) {
mDrawable.draw(canvas);
}
}
複製代碼

 

在這個構造函數中,ShapeDrawable是定義了個ovalShape類型,然後又設置了顏色與邊界,如果你不去設置邊界,這個圖形是不會畫出來的,但是,如果你不設置顏色,它默認爲黑色。通過視圖的自定義,你可以通過各種方法畫出你喜歡的東東來,下面是個簡單的例子。

 

CustomDrawableView mCustomDrawableView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCustomDrawableView = new CustomDrawableView(this);
setContentView(mCustomDrawableView);
}

 

如果你想用XML文件配置來取代原有佈局來畫自定義的drawable,於是你自定義的Drawable類必須重載view(Context,AttributeSet)構造函數。下面是範例:

 

<com.example.shapedrawable.CustomDrawableView 
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

ShapeDrawable類(像很多其他Drawable類型在android.graphics.drawable包)允許你定義drawable公共方法的各種屬性。有些屬性你可以需要調整,包括,透明度,顏色過濾,不透明度,顏色。

 

3 NinePatchDrawable
NinePatchDrawable 繪畫的是一個可以伸縮的位圖圖像,Android會自動調整大小來容納顯示的內容。一個例子就是NinePatch爲背景,使用標準的Android按鈕,按鈕必須伸縮來容納長度變化的字符NinePatchDrawable是一個標準的PNG圖像,它包括額外的1個像素的邊界,你必須保存它後綴爲.9.png,並且保持到工程的res/drawable目錄中。
這個邊界是用來確定圖像的可伸縮和靜態區域。你可以在左邊和上邊的線上畫一個或多個黑色的1個像素指出可伸縮的部分(你可以需要很多可伸縮部分),它的相對位置在可伸縮部分相同,所以大的部分總是很大的。

 

你還有可以在圖像的右邊和下邊畫一條可選的drawable區域(有效的,內邊距線)。如果你的視圖對象設置NinePath爲背景然後指定特殊的視圖字體,它將自行伸縮使所有的文本來適應根據右線與底部線設計好的區域(如果有的話),當然內邊距線不包括其中,Android可以使用左邊的線與上面的線來定義一個drawable區域。我們來澄清一下這兩條不同的線,左邊跟頂部的線來定義哪些圖像的像素允許在伸縮時被複制。底部與右邊的線用來定義一個相對位置內的圖像,視圖的內容就放入其中。下面是一個例子用NinePatch 文件來定義個按鈕。NinePath通過左邊的線與頂部的線定義一個可伸縮的區域而底部的線與右邊的線可以定義個可畫區域,在上面的圖片中,灰色點的線定義了個圖像的區域爲了圖像的伸縮,圖像底部的粉紅色的矩形框定義了個視圖內向允許呈現的區域,如果內容不適合這個區別,它們將會將圖像拉伸。

 

Draw9-path工具提供了一個非常簡單的方法去創建你的NinePath圖像,. 利用WYSIWY圖形編輯器,如果你定義的區域可伸縮區域有超出圖紙範圍的危險它甚至會提出警告。下面是一個簡單的XML來演示如何在一對按鈕上添加NinePatch(NinePath圖片保存在res/drawable/my_button_background.9.png

 

複製代碼
<Button id="@+id/tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerInParent="true"
android:text="Tiny"
android:textSize="8sp"
android:background="@drawable/my_button_background"/>
<Button id="@+id/big"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"
android:text="Biiiiiiig text!"
android:textSize="30sp"
android:background="@drawable/my_button_background"/>
複製代碼

 

需要注意的是寬度與高度需要設置爲wrap_content,使按鈕適合文字。

 

下面是兩個按鈕基於XML跟NinePath圖片的呈現,隨着按鈕通過文字大小的變化,背景圖像也會去適應。

 

4 Tween Animation 

一個tween動畫將對視圖對象中的內容進行一系列簡單的轉換(位置,大小,旋轉,透明性)。如果你有 一個文本視圖對象,你可以移動它,旋轉它,讓它變大或讓它變小,如果文字下面還有背景圖像,背景 圖像也會隨着文件進行轉換。Animation package 提供了所有的類來供tween 動畫使用。 Tween 動畫定義了一個動畫指令的隊列,定義可以使用XML也可以再Android的代碼中,像定義佈局一 樣,我們建議使用XML來定義,因爲它具備的閱讀性,重用性,可以交換性大大超過了硬編碼。在下面 的例子中,我們使用XML(參考AnimationSet 類或者其它的動畫類來學習如何在代碼中定義) 動畫的指令定義了你想要發生什麼樣的轉換,當他們發生了,應該執行多長時間,轉換可以是連續的也 可以使同時的。例如,你讓文本內容從左邊移動到右邊,然後旋轉180度,或者在移動的過程中同時旋 轉,沒個轉換需要設置一些特殊的參數(開始和結束的大小尺寸的大小變化,開始和結束的旋轉角度等 等,也可以設置些基本的參數(例如,開始時間與週期),如果讓幾個轉換同時發生,可以給它們設置 相同的開始時間,如果按序列的話,計算開始時間加上其週期。 動畫的XML文件還是在你工程中res/anim目錄,這個文件必須包含一個根元素,可以使<alpha> <scale> <translate> <rotate> 插值元素或者是把上面的元素都放入<set>元素組中,默認情況下,所以的動畫指令都是同時發生的, 爲了讓他們按序列發生,需要設置一個特殊的屬性startOffset,下面的例子會演示。 下面的ApiDemos XML文件定義了視圖對象的伸縮功能,同時發生旋轉 。

複製代碼
<set android:shareInterpolator="false"><scale
android:interpolator="@android:anim/
accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="700" />
<set android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400"
android:fillBefore="false" />
<rotate
android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400" />
</set></set> 
複製代碼


屏幕座標(本例中沒有使用)是(0,0)在左上角,並向右增加下去。還有許多值,像pivotX指定對象是相對自己還是父類。必須使用正確的格式來設置(”50” 是相對於父
類50% “50%”相對於自己%50)您可以決定如何轉變在一段時間內適用於指定的插值,Android包括了幾個插值子類指定不同的速度曲線,例如:AccelerateInterpolator 告訴了開始比較慢,速度慢慢的變快,每個都有一個屬性值,可以用於在XML中。保存hyperspace_jump.xml到你工程中的res/anim目錄,下面的JAVA代碼將引用它來佈局一
個ImageView對象。

 

ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this,
R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

 

作爲替代startAnimation(),你可以通過Animation.setStartTime來定義開始時間,通過view.setAnimationl來指定這個動畫關於XML的參數屬性有效的標記,可以參見Available Resources中動畫的討論。

 

注意:你的動畫不顧後果的移動或調整大小,視圖不會去適應你的動畫而去自動調整,即使如此,動畫將會超出視圖的範圍,但不會被截斷,然而,截斷髮生在你的動畫如果超出了父類的視圖。 

 

5 Frame Animation

創建一個連續不同的圖片的序列是傳統動畫的場景,按照指令播放,就像滾動的電影,在Android中基礎類AnimationDrawable就是專門來負責幀動畫。 

 

雖然你可以在代碼中定義幀動畫,可以使用AnimationDrawable類的API.,它是非常簡單通過XML文件 列出動畫中的所有幀,像上面的動畫tween,這種類別動畫的XML文件放入工程中的res/anim目錄。既 然這樣,指令按照週期去執行每幀動畫。 在XML文件包含一個<animation-list>根節點元素和好幾個子節點<item>來定義每幀。一個資源分別定 義了幀的名字與幀的持續時間。下面爲範例:

 

複製代碼
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"android:oneshot="true">
<item android:drawable="@drawable/rocket_thrust1"
android:duration="200" />
<item android:drawable="@drawable/rocket_thrust2"
android:duration="200" />
<item android:drawable="@drawable/rocket_thrust3"
android:duration="200" />
</animation-list> 
複製代碼

 

這個動畫播放三個幀動畫,通過設置android:oneshot屬性爲true,它將會在最後一幀停下來,如果設置 爲false這個動畫將循環播放。這個文件保存到工程目錄res/anim目錄下爲rocket_thrust.xml,你也 可以添加一個背景圖片到視圖中,然後開始播放。下面爲範例:

 

複製代碼
AnimationDrawable rocketAnimation; 

publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.anim.rocket_thrust);
rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
rocketAnimation.start();
return true;
}
return super.onTouchEvent(event);
}

複製代碼

 

  一個比較需要特別注意的是,在AnimationDrawable調用onCreate()過程中不能調用start(),這是因 爲AnimationDrawable不能在不完全的窗口上運行,如果你想立即播放動畫,沒有必要的交互,你可以 再onWindowFocusChanged()方法中調用它。這樣它將成爲窗口焦點。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章