Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android API Guide for Animation and Graphics(五)—— 動畫與圖形(畫布和可繪制對象)

Android API Guide for Animation and Graphics(五)—— 動畫與圖形(畫布和可繪制對象)

編輯:關於Android編程

畫布和可繪制對象(Canvas and Drawables)

Android框架接口提供了一組允許你自定義圖形渲染到畫布上或者修改已存在View對象外觀的2D繪圖接口。所以繪制2D圖時,通常采用以下兩種方式之一:

a.將圖形或者動畫繪制到你布局中的一個視圖對象上。這種方式,繪制的圖形由系統的視圖層次結構進程處理,你只需簡單的將圖形聲明到視圖對象中。

b.直接在畫布上繪制你的圖形。這種方式,你需要自己適當的調用類的onDraw方法(傳遞你繪圖的畫布對象),或者調用畫布的draw…()方法(如drawPicture())。這樣做你也可以控制任何動畫。

當繪制不需要動態改變或沒有交互性游戲部分的簡單圖形時,選擇a是最好的方式。例如,當你想在其他靜態應用程序中顯示靜態圖形或預定義動畫時,你應該將圖形繪制到View上。閱讀Drawables更多信息。

當應用程序需要頻繁重繪自身時,b是更好的選擇。例如像視頻游戲就需要繪制到畫布上去。當然,有多種方法可以做到這一點:

在布局創建自定義視圖控件的 UI線程中,調用invalidate()然後處理onDraw()回調。

或者,在單獨管理SurfaceView的線程中盡可能快的實現畫布上的繪制(這樣就不需要調用invalidate())。

在畫布上繪制(Draw with a Canvas)

當應用需要實現實現特殊繪制和控制動畫的圖形時,就應該通過畫布來實現。畫布作為一個虛擬的界面作用於真正的圖形繪制界面上,它持有你所有繪制方法的調用。通過畫布,實際上是在一個放入window的位圖中實現繪制的。

在繪制過程回調onDraw()方法事件中傳給你一個Canvas對象,你只需要在它上面完成你的繪制工作就行了。當處理SurfaceView對象時,你也可以通過SurfaceHolder.lockCanvas()獲取Canvas對象(所有這些使用場景都會在下文中討論)。但是,如果你需要創建一個新的Canvas對象,你得定義在實際完成繪制工作的Bitmap對象上,這種方式,Bitmap對於畫布是必要條件。你可以如下創建一個畫布對象:

Bitmap b = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

現在,畫布將繪制到定義的位圖上。通過畫布在上bitmap上繪圖後,可以使用畫布其中一個方法,Canvas.drawBitmap(Bitmap,…)將Bitmap對象傳給另Canvas對象上。建議通過傳給你的Canvas對象,並調用View.onDraw()或者SurfaceHolder.lockCanvas()來繪制你最終的圖形。(詳情閱讀下文)

畫布有自己的一套繪制方法可以給你使用,如drawBitmap(…),drawRect(),drawText()等等。其它你可能使用的類也有draw()方法。比如,你可能想把一個Drawable對象繪制到畫布上,Drawable對象就有自己的draw()方法,它將Canvas作為參數。

在View上繪制(On a View)

如果應用程序不需要大量的處理或高的幀速率要求(比如是棋牌游戲、貪吃蛇游戲或者慢動畫的應用程序),那你應該考慮創建自定義的視圖組件並使用畫布在View.onDraw()方法中進行繪制。這麼做的好處就是Android框架預先提供給你一個在繪制時需要使用到的Canvas對象,這樣你就不需要自己創建了。

首先,繼承View類(或View的子類)然後聲明onDraw回調方法。Android框架調用onDraw方法去請求你的View繪制自身。這就是通過onDraw()回調方法傳給你Canvas對象後實現所有繪制調用的地方。

Android框架只會在需要時調用onDraw()。每次應用程序准備繪制時,你得調用invalidate()來讓你的View無效。這暗示你的視圖將被重新繪制,然後Android框架再調用onDraw方法(盡管不能保證這個回調會被立刻執行)。

在View組件的onDraw()方法中使用傳給你的Canvas對象,或者在其它類攜帶Canvas對象的draw()方法中,完成所有的繪制工作。通過Canvas作為參數傳遞給你,然後通過它來調用不同的畫布繪制方法。一旦onDraw()的工作完成,Android框架就使用Canvas繪制一個給系統處理的bitmap位圖對象。

Note:為了在子線程中請求invalidate方法,而不是在UI線程中,你得調用postInvalidate().

在SurfaceView上繪制(On a SurfaceView)

SurfaceView是View的特殊子類,在視圖層次結構中提供了專門的界面繪圖功能。SurfaceView的目的就是將繪制的界面提供給應用程序的輔助線程,以至於應用程序不需要等到系統視圖層次結構准備繪制的階段。取而代之的是,輔助線程持有SurfaceView的引用,所以可以先繪制到它的畫布上。

首先,需要創建一個繼承SurfaceView的子類,這個子類需要實現SurfaceHolder.Callback.子類Callback是告訴你一些關於Surface底層信息的接口,比如Surface的創建,改變或銷毀。這些事件對於你准備開始繪制工作,不管是否為新Surface的屬性做調整,還是停止繪制工作以及殺掉一些潛在的任務是非常重要的。在SurfaceView類中定義輔助線程實現所有在畫布上的繪制工作也是一個不錯的選擇。

你應該通過SurfaceHolder處理Surface對象,而不是直接處理它。所以,當你的SurfaceView初始化之後,通過調用getHolder()獲取SurfaceHolder對象。然後你應該通過調用addCallback()方法通知SurfaceHolder你想接收SurfaceHolder的回調(SurfaceHolder.Callback的回調)。然後在SurfaceView的子類中重寫SurfaceHolder.Callback的回調函數。

為了在輔助線程的畫布上進行繪制,你得傳遞SurfaceHandler線程並通過lockCanvas()檢索Canvas對象。現在,你可以通過SurfaceHolder傳遞的Canvas對象將你所需圖形繪制到畫布上。一旦使用畫布完成繪制,調用unlockCanvasAndPost()傳遞你的Canvas對象。Surface就會繪制出你在畫布上所繪制的圖形。每次重新繪制你都需要按這個順序調用locking跟unlocking。

Note:每次傳遞從SurfaceHolder檢索出的Canvas對象,前一個Canvas的狀態都會被保留。為了確保圖形的正確繪制,你得重新繪制整個界面。比如,你可以調用drawColor()填充顏色或者drawBitmap()設置圖片背景來清理前一個Canvas保留下來的狀態。否則,你將看到繪制出前一個畫布的軌跡。

可繪制圖(Drawables)

Android提供可自定義繪制形狀,圖片的2D圖形繪制庫。在android.graphics.drawable包中你可以找到常見的2D繪圖類。

本文將討論Drawable對象以及一些Drawable的子類的基本使用。獲取更多通過Drawable實現幀動畫的信息,參考Drawable Animation.

Drawable是一個可繪制圖形的抽象。你會發現Drawable類擴展出一些各種特定類型的可繪制圖形,包括 BitmapDrawable, ShapeDrawable, PictureDrawable, LayerDrawable等等。當然,你可以通過你獨特的方式擴展你自定義的可繪制對象。

通過資源文件創建(Creating from resource images)

一種通過項目資源引用圖片文件添加圖形的簡單方式。這種方式支持的類型有png(首選),jpg(可接受),gif(不推薦)。這種方式明顯也是應用程序圖標,logo,或者比如游戲等其他圖形的首選。

要使用圖片資源,只需要將圖片文件添加到項目的res/drawable/目錄中,然後你就可以在代碼或者xml布局中引用了。無論哪種方式,使用資源id是首選,因為它不需要文件的後綴名。(eg:my_image.png通過my_image來引用)。

Note:放置在res / drawable /中的圖片資源可以在構建過程期間通過aapt工具自動無損圖像壓縮優化。例如,不需要多於256種顏色的真彩色PNG可以被轉換為具有色板的8位PNG。這可以讓同等質量的圖片卻需要更少的內存。所以注意二進制圖片的放置位置,因為它可能在構建期間改變。如果你計劃將圖像讀取為位流,以便將其轉換為位圖,請將圖像放在res / raw /文件夾中,而不進行優化。
示例代碼:

LinearLayout mLinearLayout;

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Create a LinearLayout in which to add the ImageView
  mLinearLayout = new LinearLayout(this);

  // Instantiate an ImageView and define its properties
  ImageView i = new ImageView(this);
  i.setImageResource(R.drawable.my_image);
  i.setAdjustViewBounds(true); // set the ImageView bounds to match the Drawable's dimensions
  i.setLayoutParams(new Gallery.LayoutParams(LayoutParams.WRAP_CONTENT,
      LayoutParams.WRAP_CONTENT));

  // Add the ImageView to the layout and set the layout as the content view
  mLinearLayout.addView(i);
  setContentView(mLinearLayout);
}

在一些情況,你可以將圖片作為Drawable對象進行處理。方式如下:

Resources res = mContext.getResources();
Drawable myImage = res.getDrawable(R.drawable.my_image);

通過xml資源創建(Creating from resource XML)

現在,你已經很熟悉Android的UI開發的規則了。因此你明白Android的強大以及在XML中定義對象的靈活性。這些思想從Views參透到Drawables.如果你想創建初始化不依賴於代碼或用戶交互的Drawable對象,在XML中定義Drawable是個不錯的選擇。即使你想應用程序在用戶體驗時改變Drawable屬性,你也應該考慮將其定義在XML中,因為你仍然可以在初始化之後修改Drawable的屬性。

XML定義好Drawable後,將文件存放於項目的res/drawable/目錄下。然後調用Resources.getDrawable()並傳入XML資源文件的ID來初始化、獲取。

任何定義在XML並在應用程序中初始化的Drawable子類都支持inflate()方法。每個Drawable都支持在XML中利用指定的XMl屬性進行填充。
示例:這是一個定義TransitionDrawable的XML


    
    

下面是這個XML的使用:

Resources res = mContext.getResources();
TransitionDrawable transition = (TransitionDrawable)
    res.getDrawable(R.drawable.expand_collapse);
ImageView image = (ImageView) findViewById(R.id.toggle_image);
image.setImageDrawable(transition);

這個過度動畫可以通過如下代碼運行1秒:

transition.startTransition(1000);

可繪制形狀(Shape Drawable)

當你想動態繪制一些2D圖形,ShapeDrawable對象可能適合你的需求。

ShapeDrawable是Drawable的擴展類,任何使用Drawable的地方也可以用它。如可以為View對象通過setBackgroundDrawable()設置背景。當然,你也可以繪制某個形狀作為自定義View,並添加到布局中。因為ShapeDrawable用它自己的draw()方法,你可以在創建View子類的時候在View.onDraw()方法中創建ShapeDrawable。下面是一個基本的View類擴展實現上述方法,繪制的ShapeDrawable作為一個View:

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,然後設置它的顏色和邊界。如果不設置邊界,形狀將不會被繪制,但是如果不設置顏色,它將默認設置為黑色。

自定義View定義好後,你可以用任何你喜歡的方式進行繪制。如上的代碼,可以在Activity中進行繪制:

CustomDrawableView mCustomDrawableView;

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

  setContentView(mCustomDrawableView);
}

如果你想在XML而不是在Activity中繪制自定義的Drawable。你需要重寫CustomDrawableView類的構造函數View(Context, AttributeSet),因為當填充XML布局的時候會調用這個構造函數來初始化。XML添加CustomDrawable如下所示:

Nine-patch

這裡省略了.9圖的相關介紹,獲取更多信息參考(剛好前兩天Google開發者大會開放了國內Android官方網站,不需要翻牆)。
https://developer.android.google.cn/guide/topics/graphics/2d-graphics.html

可繪制向量圖(Vector Drawables)

VectorDrawable是一組點,線,曲線以及關聯的顏色信息所定義在XML文件的向量圖。從Android 5.0(API 21)開始,有兩個類支持向量圖作為Drawable資源:VectorDrawable和AnimatedVectorDrawable。先於Android5.0,23.2或更高版本的支持庫更全的支持了向量圖和動畫向量圖。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved