官方Canvas and Drawables翻譯以及批注

Android框架提供了一套二維繪圖API,可讓你將自己的自定義圖形渲染到畫布上或修改現(xiàn)有視圖以自定義其外觀和感覺。 你通常以以下方式之一繪制二維圖形:

  • a:在布局中的View對象上繪制你的圖形或動畫。 使用此選項,會調(diào)用系統(tǒng)的渲染管道處理的圖形 (需要在視圖中定義自行定義)
  • b:在Canvas對象中繪制圖形。使用此選項,需要你將畫布傳遞到適當?shù)念?onDraw(Canvas)'方法。你也可以使用Canvas中的繪圖方法。此選項還可以控制任何動畫。
    繪制一個視圖是一個不錯的選擇,當你想繪制簡單的圖形,不需要動態(tài)變化,并且不是性能密集型應用程序,如游戲的一部分。例如,當你要在其他靜態(tài)應用程序中顯示靜態(tài)圖形或預定義的動畫時,你應該將圖形繪制到視圖中。
    當你的應用程序需要定期重繪時,繪制到畫布會更好。 應用程序(如視頻游戲)應該自己繪制到畫布上。 但是,有多種方式可以做到這一點:
  • a:在應用程序的主線程中,你可以在布局中創(chuàng)建自定義視圖組件,調(diào)用invalidate(),然后處理onDraw(Canvas)回調(diào)。
  • b:在管理SurfaceView的工作線程中,使用Canvas的繪圖方法。 你不需要調(diào)用invalidate()。

Draw with a canvas

你可以通過繪制由Canvas類表示的畫布來滿足需要專門繪制和/或控制圖形動畫的應用程序的要求。通過畫布,你的應用程序?qū)⒗L制到位于窗口中的底層Bitmap對象。

  • 1:如果你使用onDraw(Canvas)回調(diào)進行繪制操作,畫布已經(jīng)由系統(tǒng)提供,你只需要在其上繪制圖形。
  • 2:如果你使用的是SurfaceView對象,則可以從lockCanvas()獲取畫布。
    (編者按:lockCanvas的源碼如下:)
/**
     * Gets a {@link Canvas} for drawing into this surface.
     *
     * After drawing into the provided {@link Canvas}, the caller must
     * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
     *
     * @param inOutDirty A rectangle that represents the dirty region that the caller wants
     * to redraw.  This function may choose to expand the dirty rectangle if for example
     * the surface has been resized or if the previous contents of the surface were
     * not available.  The caller must redraw the entire dirty region as represented
     * by the contents of the inOutDirty rectangle upon return from this function.
     * The caller may also pass <code>null</code> instead, in the case where the
     * entire surface should be redrawn.
     * @return A canvas for drawing into the surface.
     *
     * @throws IllegalArgumentException If the inOutDirty rectangle is not valid.
     * @throws OutOfResourcesException If the canvas cannot be locked.
     */
    public Canvas lockCanvas(Rect inOutDirty)
            throws Surface.OutOfResourcesException, IllegalArgumentException {
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mLockedObject != 0) {
                // Ideally, nativeLockCanvas() would throw in this situation and prevent the
                // double-lock, but that won't happen if mNativeObject was updated.  We can't
                // abandon the old mLockedObject because it might still be in use, so instead
                // we just refuse to re-lock the Surface.
                throw new IllegalArgumentException("Surface was already locked");
            }
            mLockedObject = nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
            return mCanvas;
        }
    }

以下內(nèi)容將討論這兩種情況:
如果需要創(chuàng)建一個新的Canvas對象,則必須定義底層Bitmap對象。
以下代碼示例顯示了如何從bitmap設置新的canvas :

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

可以通過使用drawBitmap()方法之一在不同的canvas 中使用bitmap 。但是,我們建議您使用由onDraw(Canvas)回調(diào)或lockCanvas()方法提供的canvas 。 有關更多信息,請參閱Drawing on a ViewDrawing on a SurfaceView
Canvas類有自己的繪圖方法,包括drawBitmap(),drawRect(),drawText()等等。你可能使用的其他類也有draw()方法。例如,您可能有一些Drawable對象要放在畫布上。Drawable類有自己的 draw(Canvas)方法,它將你的canvas 作為參數(shù)。

Drawing on a view

如果你的應用程序不需要大量處理或較高的幀速率(例如象棋游戲,貪吃蛇游戲或其他慢動畫應用程序),那么你應該考慮在View.onDraw(Canvas)回調(diào)中使用畫布創(chuàng)建自定義視圖和繪圖。 最方便的方面是,Android框架提供了一個可以執(zhí)行繪圖操作的預定義畫布。
要開始的話,創(chuàng)建一個View的子類并實現(xiàn)onDraw(Canvas)回調(diào),Android框架調(diào)用它來繪制視圖。 然后通過框架提供的Canvas對象執(zhí)行繪圖操作。
Android框架僅在必要時調(diào)用onDraw(Canvas)。 當你重新繪制應用程序時,必須通過調(diào)用invalidate()來使視圖無效。 調(diào)用invalidate()表示你希望繪制視圖。 然后,Android會調(diào)用您的視圖的onDraw(Canvas)方法,盡管不能保證是即時的。(編者按:會在未來的某個時間調(diào)用,但是不一定馬上就可以調(diào)用到)
在視圖的onDraw(Canvas)方法中,調(diào)用畫布上的繪圖方法或者可以將畫布作為參數(shù)的其他類的方法。 一旦你的onDraw()完成,Android框架將使用你的canvas繪制由系統(tǒng)處理的bitmap。
注意:要從除應用程序主線程以外的線程中使視圖無效,您必須調(diào)用postInvalidate()而不是invalidate()。

Drawing on a SurfaceView

SurfaceView是View的一個特殊子類,它在視圖層次結構中提供了一個專用的繪圖面。目標是向應用程序的工作線程提供此繪圖面。這樣,應用程序不需要等到系統(tǒng)的視圖層次結構準備繪制(なるほど) 。具有對SurfaceView 對象的引用的工作線程可以以其自己的節(jié)奏繪制到其它的畫布上。
首先,你需要創(chuàng)建一個擴展SurfaceView的新類。 該類還應該實現(xiàn)SurfaceHolder.Callback接口,該接口提供在底層Surface對象中發(fā)生的事件,例如創(chuàng)建,更改或銷毀。這些事件讓你知道何時可以開始繪制,無論你是否需要根據(jù)新的Surface屬性進行調(diào)整,以及何時停止繪圖并可能終止某些任務。擴展SurfaceView的類也是定義工作線程的好地方,它調(diào)用畫布中的所有繪圖過程。
你應該通過SurfaceHolder處理the Surface對象而不是直接處理它。在你的SurfaceView對象初始化之后,你可以通過調(diào)用getHolder()獲取一個SurfaceHolder對象。 你應該注冊您的SurfaceView對象,通過調(diào)用addCallback()來接收來自SurfaceHolder的通知。 然后在你的SurfaceView類中實現(xiàn)每個SurfaceHolder.Callback抽象方法。
你可以從具有訪問SurfaceHolder對象的工作線程繪制到the surface canvas 。 每次應用程序需要重新繪制表面時,請從工作線程中執(zhí)行以下步驟:

  • 1:使用lockCanvas()來取回canvas 。
  • 2:在畫布上執(zhí)行繪圖操作。
  • 3:通過調(diào)用 unlockCanvasAndPost(Canvas) 來解鎖畫布。
    The surface繪制畫布考慮到你在上面執(zhí)行的所有操作
    注意:每次從SurfaceHolder檢索畫布時,畫布的先前狀態(tài)都將被保留。為了正確地動畫你的圖形,你必須重繪整個表面。例如,你可以使用drawColor()方法填充顏色或使用drawBitmap()方法設置背景圖像來清除畫布的上一個狀態(tài)。 否則,你的畫布可以顯示以前的圖紙的痕跡。

Drawables

Android框架提供了一個用于繪制形狀和圖像的自定義2D圖形庫。 android.graphics.drawable包中包含用于繪制二維的常用類。
本節(jié)討論使用可繪制對象繪制圖形的基本知識,以及如何使用Drawable類的幾個子類。 有關如何使用drawable進行逐幀動畫的信息,請參閱可 Drawable Animation
Drawable是可以繪制的東西的一般抽象。 Android框架提供了一組Drawable的直接和間接子類,你可以在各種場景中使用。 你還可以擴展這些類以定義你自己的以可以獨特的方式運行的自定義可繪制對象。
除了使用標準類構造函數(shù)之外,還有兩種方法來定義和實例化一個Drawable:

  • 1:使用項目中保存的資源圖像。
  • 2:使用可繪制屬性的XML資源。

Creating drawables from resource images

你可以通過引用項目資源中的圖像文件來將圖形添加到應用程序。 支持的文件類型是PNG(首選),JPG(可接受)和GIF(不鼓勵)。 應用程序圖標,icon和其他圖形,如游戲中使用的圖形,非常適合這種技術。
以下代碼片段演示了如何構建使用從可繪制資源創(chuàng)建的圖像的ImageView,并將其添加到布局中:

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);

  // set the ImageView bounds to match the Drawable's dimensions
  i.setAdjustViewBounds(true);
  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);

注意:你的項目中的每個唯一資源只能維護一個狀態(tài),無論你為其實例化了多少個不同的對象。 例如,如果你從同一個圖像資源中實例化兩個Drawable對象,并更改一個對象的屬性(例如alpha),那么它也會影響另一個對象。 處理圖像資源的多個實例時,而不是直接轉換Drawable對象,你應該執(zhí)行補間動畫。
下面的XML代碼片段顯示了如何在XML布局中將可繪制的資源添加到ImageView中:

<ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/my_image" />

Creating drawables from XML resources

如果你想要創(chuàng)建一個Drawable對象,最初不依賴于你的代碼或用戶交互定義的變量,那么在XML中定義Drawable是一個很好的選擇。 即使你希望Drawable在用戶與應用程序交互期間更改其屬性,你應該考慮以XML定義對象,因為你可以在實例化之后修改屬性。(編者按:你也可以實現(xiàn)如此實現(xiàn)動畫)

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

然后,通過調(diào)用Resources.getDrawable()來檢索和實例化對象,并傳遞XML文件的資源ID。 任何支持inflate()方法的Drawable子類可以在XML中定義,并由你的應用程序?qū)嵗?支持XML擴展的每個可繪制類都使用特定的XML屬性來幫助定義對象屬性。 以下代碼實例化TransitionDrawable并將其設置為ImageView對象的內(nèi)容:

Resources res = mContext.getResources();
TransitionDrawable transition =
    (TransitionDrawable) res.getDrawable(R.drawable.expand_collapse);

ImageView image = (ImageView) findViewById(R.id.toggle_image);
image.setImageDrawable(transition);

// Then you can call the TransitionDrawable object's methods
transition.startTransition(1000);

Shape drawables

當你想要動態(tài)繪制二維圖形時,ShapeDrawable對象可以是一個很好的選擇。 你可以以編程方式在ShapeDrawable對象上繪制原始形狀,并應用您的應用程序需要的樣式。ShapeDrawable是Drawable的一個子類。 因此,你可以使用一個ShapeDrawable,無論何時可以使用Drawable。 例如,你可以使用ShapeDrawable對象通過將視圖傳遞給視圖的setBackgroundDrawable()方法來設置視圖的背景。 您還可以將自己的形狀繪制為自己的自定義視圖,并將其添加到應用程序中的布局。
因為ShapeDrawable具有自己的draw()方法,所以可以創(chuàng)建一個在onDraw()事件期間繪制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());
    // If the color isn't set, the shape uses black as the default.
    mDrawable.getPaint().setColor(0xff74AC23);
    // If the bounds aren't set, the shape can't be drawn.
    mDrawable.setBounds(x, y, x + width, y + height);
  }

  protected void onDraw(Canvas canvas) {
    mDrawable.draw(canvas);
  }
}

你可以在上述代碼示例中使用CustomDrawableView 類,就像使用任何其他自定義視圖一樣。 例如,你可以以編程方式將其添加到應用程序中的活動中,如以下示例所示:

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

ShapeDrawable類與android.graphics.drawable包中的許多其他可繪制類型一樣,允許你使用公共方法定義對象的各種屬性。 您可能要調(diào)整的一些示例屬性包括Alpha透明度,顏色過濾器,抖動,不透明度和顏色。

NinePatch drawables

NinePatchDrawable圖形是一個可伸縮的位圖圖像,可以用作視圖的背景。 Android會自動調(diào)整圖形大小以適應視圖的內(nèi)容。 NinePatch圖像的示例使用是標準Android按鈕使用的背景 - 按鈕必須伸展以適應各種長度的字符串。 NinePatch圖形是包含一個額外的1像素邊框的標準PNG圖像。 必須使用9.png擴展名保存在項目的res / drawable /目錄中。
使用邊框定義圖像的可拉伸和靜態(tài)區(qū)域。 你可以通過在邊框的左側和頂部繪制一個(或多個)1像素寬的黑線(其他邊框像素應完全透明或白色)來指示可伸縮部分。 你可以擁有盡可能多的伸縮部分。 可伸縮部分的相對尺寸保持不變,所以最大部分總是保持最大。
你還可以通過在右側繪制一條線和底部的一條線來定義圖像的可選繪制部分。 如果View對象將NinePatch圖形設置為其背景,然后指定視圖的文本,則它將自動展開,以使所有文本僅占據(jù)由右側和底部指定的區(qū)域(如果包含)。 如果不包括填充行,Android將使用左側和上側的行來定義該可繪制區(qū)域。
為了澄清線之間的差異,左和右線定義允許復制圖像的哪些像素以拉伸圖像。 底部和右側的行定義圖像內(nèi)允許占據(jù)的內(nèi)容的相對區(qū)域。
圖1顯示了用于定義按鈕的NinePatch圖形的示例:

ninepatch_raw.png

虛線灰色線標識復制圖像的區(qū)域以拉伸圖像。 底部圖像中的粉色矩形標識允許視圖內(nèi)容的區(qū)域。 如果內(nèi)容不適合該區(qū)域,則圖像被拉伸以使其適合。
Draw 9補丁工具使用WYSIWYG圖形編輯器提供了一種非常方便的創(chuàng)建NinePatch圖像的方法。 如果您為可伸縮區(qū)域定義的區(qū)域有可能由于像素復制而產(chǎn)生繪圖工件,那么它甚至會引發(fā)警告。
以下示例布局XML演示了如何將NinePatch圖形添加到幾個按鈕。 NinePatch圖像保存到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"/>

請注意,layout_width和layout_height屬性設置為wrap_content以使按鈕整齊地貼合在文本上。
圖2顯示了從上面顯示的XML和NinePatch圖像呈現(xiàn)的兩個按鈕。 請注意按鈕的寬度和高度如何隨著文字而變化,背景圖片會延伸以適應它。


ninepatch_examples.png

Vector drawables

矢量drawable是一個在XML文件中定義的向量圖形,它是一組點,線和曲線以及相關聯(lián)的顏色信息。 Android框架提供了VectorDrawable和AnimatedVectorDrawable類,它們支持矢量圖形作為可繪制的資源。
針對Android版本低于5.0(API級別21)的應用程序可以使用支持庫版本23.2或更高版本來獲取矢量繪圖和動畫矢量繪圖的支持。 有關使用向量可繪制類的更多信息,請參閱Vector Drawable

low_stiffness.gif

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內(nèi)容