Canvas
保存相關的標誌在瞭解Canvas
的保存以前,咱們先看一下和保存相關的標誌的定義,它們定義了保存的類型,這些標誌定義在Canvas.java
當中,一共有六個標誌。java
/**
* Restore the current matrix when restore() is called.
*/
public static final int MATRIX_SAVE_FLAG = 0x01;
/**
* Restore the current clip when restore() is called.
*/
public static final int CLIP_SAVE_FLAG = 0x02;
/**
* The layer requires a per-pixel alpha channel.
*/
public static final int HAS_ALPHA_LAYER_SAVE_FLAG = 0x04;
/**
* The layer requires full 8-bit precision for each color channel.
*/
public static final int FULL_COLOR_LAYER_SAVE_FLAG = 0x08;
/**
* Clip drawing to the bounds of the offscreen layer, omit at your own peril.
* <p class="note"><strong>Note:</strong> it is strongly recommended to not
* omit this flag for any call to <code>saveLayer()</code> and
* <code>saveLayerAlpha()</code> variants. Not passing this flag generally
* triggers extremely poor performance with hardware accelerated rendering.
*/
public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
/**
* Restore everything when restore() is called (standard save flags).
* <p class="note"><strong>Note:</strong> for performance reasons, it is
* strongly recommended to pass this - the complete set of flags - to any
* call to <code>saveLayer()</code> and <code>saveLayerAlpha()</code>
* variants.
*/
public static final int ALL_SAVE_FLAG = 0x1F;
複製代碼
從上面的定義能夠看出,flag
是用一個32
位的int
型變量來定義的,它的低5
位的每一位用來表示須要保存Canvas
當前哪部分的信息,若是所有打開,那麼就是所有保存,也就是最後定義的ALL_SAVE_FLAG
,這5
位分別對應:android
xxxx1
:保存Matrix
信息,例如平移、旋轉、縮放、傾斜等。xxx1x
:保存Clip
信息,也就是裁剪。xx1xx
:保存Alpha
信息。x1xxx
:保存8
位的顏色信息。1xxxx
:Clip drawing to the bounds of the offscreen layer
,不太明白是什麼意思。若是須要多選以上的幾個信息進行保存,那麼對多個標誌位執行或操做便可。canvas
save()
和save(int saveFlags)
下面是這兩個方法的定義:bash
/**
* Saves the current matrix and clip onto a private stack.
* <p>
* Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
* clipPath will all operate as usual, but when the balancing call to
* restore() is made, those calls will be forgotten, and the settings that
* existed before the save() will be reinstated.
*
* @return The value to pass to restoreToCount() to balance this save()
*/
public int save() {
return native_save(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
}
/**
* Based on saveFlags, can save the current matrix and clip onto a private
* stack.
* <p class="note"><strong>Note:</strong> if possible, use the
* parameter-less save(). It is simpler and faster than individually
* disabling the saving of matrix or clip with this method.
*
* @param saveFlags flag bits that specify which parts of the Canvas state
* to save/restore
* @return The value to pass to restoreToCount() to balance this save()
*/
public int save(@Saveflags int saveFlags) {
return native_save(mNativeCanvasWrapper, saveFlags);
}
複製代碼
註釋已經很好地說明了save()
和save(int saveFlags)
的做用:當調用完save
方法以後,例如平移、縮放、旋轉、傾斜、拼接或者裁剪這些操做,都是和原來的同樣,而當調用完restore
方法以後,在save()
到restore()
之間的全部操做都會被遺忘,而且會恢復調用save()
以前的全部設置。此外還能夠得到如下信息:app
native_save
方法,而無參方法save()
默認是保存Matrix
和Clip
這兩個信息。save()
方法,而不是使用有參的save(int saveFlags)
方法傳入別的Flag
。index
,以後能夠在restoreToCount(int saveCount)
中傳入它來說在它之上的全部保存圖層都出棧。native_save
來對這個mNativeCanvasWrapper
變量,咱們會發現,全部對於Canvas
的操做,其實最終都是操做了mNativeCanvasWrapper
這個對象。XXX_SAVE_FLAG
的命名來看,帶有參數的save(int saveFlags)
方法只容許保存MATRIX_/CLIP_/ALL_
這三種狀態,而HAS_ALPHA_LAYER/FULL_COLOR_LAYER_/CLIP_TO_LAYER_
這三種狀態,則是爲後面的saveLayer/saveLayerAlpha
提供的。restore()
restoreToCount(int count)
getSaveCount()
這三個方法用來恢復圖層信息,也就是將以前保存到棧中的元素出棧,咱們看一下這幾個方法的定義:less
/**
* This call balances a previous call to save(), and is used to remove all
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
*/
public void restore() {
boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
native_restore(mNativeCanvasWrapper, throwOnUnderflow);
}
/**
* Returns the number of matrix/clip states on the Canvas' private stack. * This will equal # save() calls - # restore() calls. */ public int getSaveCount() { return native_getSaveCount(mNativeCanvasWrapper); } /** * Efficient way to pop any calls to save() that happened after the save * count reached saveCount. It is an error for saveCount to be less than 1. * * Example: * int count = canvas.save(); * ... // more calls potentially to save() * canvas.restoreToCount(count); * // now the canvas is back in the same state it was before the initial * // call to save(). * * @param saveCount The save level to restore to. */ public void restoreToCount(int saveCount) { boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated(); native_restoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow); } 複製代碼
這個幾個方法很好理解:ide
restore()
方法用來恢復,最近一次調用save()
以前的Matrix/Clip
信息,若是調用restore()
的次數大於save()
的一次,也就是棧中已經沒有元素,那麼會拋出異常。getSaveCount()
:返回的是當前棧中元素的數量。restoreToCount(int count)
會將saveCount()
之上對應的全部元素都出棧,若是count < 1
,那麼會拋出異常。native
的方法。下面,咱們用一個簡單的例子,來更加直觀的瞭解一下save()
和restore()
,咱們重寫了一個View
當中的onDraw()
方法:佈局
Matrix
信息private void saveMatrix(Canvas canvas) {
//繪製藍色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_blue_light));
canvas.drawRect(0, 0, 200, 200, mPaint);
//保存
canvas.save();
//裁剪畫布,並繪製紅色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_red_light));
//平移.
//canvas.translate(100, 0);
//縮放.
//canvas.scale(0.5f, 0.5f);
//旋轉
//canvas.rotate(-45);
//傾斜
canvas.skew(0, 0.5f);
canvas.drawRect(0, 0, 200, 200, mPaint);
//恢復畫布
canvas.restore();
//繪製綠色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
canvas.drawRect(0, 0, 50, 200, mPaint);
}
複製代碼
咱們對畫布分別進行了平移、縮放、旋轉、傾斜,獲得的結果爲: 性能
能夠看到,當咱們調用上面這些方法時,其實就是對Canvas
先進行了一些移動,旋轉,縮放的操做,而後再在它這個新的狀態上進行繪製,以後調用restore()
以後,又恢復回了調用save()
以前的狀態。ui
Clip
信息private void saveClip(Canvas canvas) {
//繪製藍色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_blue_light));
canvas.drawRect(0, 0, 200, 200, mPaint);
//保存.
canvas.save();
//裁剪畫布,並繪製紅色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_red_light));
canvas.clipRect(150, 0, 200, 200);
canvas.drawRect(0, 0, 200, 200, mPaint);
//恢復畫布
canvas.restore();
//繪製綠色矩形
mPaint.setColor(getResources().getColor(android.R.color.holo_green_light));
canvas.drawRect(0, 0, 50, 200, mPaint);
}
複製代碼
最終的結果以下所示:
canvas
的大小和View
的大小是同樣的,咱們以(0,0)
爲原點座標,繪製了一個大小爲200px * 200px
的藍色矩形。(150, 0)
爲原點座標,裁剪成了一個大小爲50px * 200px
的新畫布,對於這個裁剪的過程能夠這麼理解:就是咱們之前上學時候用的那些帶鏤空的板子,上面有各類的形狀,而這一裁剪,其實就是在原來的canvas
上蓋了這麼一個鏤空的板子,鏤空部分就是咱們定義的裁剪區域,當咱們進行繪製時,就是在這個板子上面進行繪製,而最終在canvas
上展示的部分就是這些鏤空部分和以後繪製部分的交集。(0, 0)
爲原點繪製一個大小爲200px * 200px
的紅色矩形,可是此時因爲(0, 0) - (150, 200)
這部分被蓋住了,因此不咱們畫不上去,實際畫上去的只有(50, 0) - (200, 200)
這一部分。restore()
方法,就至關於把板子拿掉,那麼這時候就可以像以前那樣正常的繪製了。saveLayer
saveLayerAlpha
除了save()
方法以外,canvas
還提供了saveLayer
方法
saveLayer
和
saveLayerAlpha
,它們最終都是調用了兩個
native
方法: 對於
saveLayer
,若是不傳入
saveFlag
,那麼默認是採用
ALL_SAVE_FLAG
:
native_saveLayer(mNativeCanvasWrapper, left, top, right, bottom, paint != null ? paint.getNativeInstance() : 0, saveFlags);
複製代碼
對於saveLayerAlpha
,若是不傳入saveFlag
,那麼默認是採用ALL_SAVE_FLAG
,若是不傳入alpha
,那麼最終調用的alpha = 0
。
native_saveLayerAlpha(mNativeCanvasWrapper, left, top, right, bottom, alpha, saveFlags);
複製代碼
save()
方法的區別關於save()
和saveLayer()
的區別,源碼當中是這麼解釋的,也就是說它會新建一個不在屏幕以內的bitmap
,以後的全部繪製都是在這個bitmap
上操做的。
This behaves the same as save(), but in addition it allocates and redirects drawing to an offscreen bitmap.
而且這個方法是至關消耗資源的,由於它會致使內容的二次渲染,特別是當canvas
的邊界很大或者使用了CLIP_TO_LAYER
這個標誌時,更推薦使用LAYER_TYPE_HARDWARE
,也就是硬件渲染來進行Xfermode
或者ColorFilter
的操做,它會更加高效。
* this method is very expensive,
*
* incurring more than double rendering cost for contained content. Avoid
* using this method, especially if the bounds provided are large, or if
* the {@link #CLIP_TO_LAYER_SAVE_FLAG} is omitted from the
* {@code saveFlags} parameter. It is recommended to use a
* {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
* to apply an xfermode, color filter, or alpha, as it will perform much
* better than this method.
複製代碼
當咱們在以後調用restore()
方法以後,那麼這個新建的bitmap
會繪製回Canvas
的當前目標,若是當前就位於canvas
的最底層圖層,那麼就是目標屏幕,不然就是以前的圖層。
* All drawing calls are directed to a newly allocated offscreen bitmap.
* Only when the balancing call to restore() is made, is that offscreen
* buffer drawn back to the current target of the Canvas (either the
* screen, it's target Bitmap, or the previous layer). 複製代碼
再回頭看下方法的參數,這兩大類方法分別傳入了Paint
和Alpha
這兩個變量,對於saveLayer
來講,Paint
的下面這三個屬性會在新生成的bitmap
被從新繪製到當前畫布時,也就是調用了restore()
方法以後,被採用:
* Attributes of the Paint - {@link Paint#getAlpha() alpha},
* {@link Paint#getXfermode() Xfermode}, and
* {@link Paint#getColorFilter() ColorFilter} are applied when the
* offscreen bitmap is drawn back when restore() is called.
複製代碼
而對於saveLayerAlpha
來講,它的Alpha
則會在被從新繪製回來時被採用:
* The {@code alpha} parameter is applied when the offscreen bitmap is
* drawn back when restore() is called.
複製代碼
對於這兩個方法,都推薦傳入ALL_SAVE_FLAG
來提升性能,它們的返回值和save()
方法的含義是相同的,都是用來提供給restoreToCount(int count)
使用。 總結一下:就是調用saveLayer
以後,建立了一個透明的圖層,以後在調用restore()
方法以前,咱們都是在這個圖層上面進行操做,而save
方法則是直接在原先的圖層上面操做,那麼對於某些操做,咱們不但願原來圖層的狀態影響到它,那麼咱們應該使用saveLayer
。
saveLayer
示例和前面相似,咱們建立一個繼承於View
的SaveLayerView
,並重寫onDraw(Canvas canvas)
方法:
首先,由於咱們前面整個一整節獲得的結論是saveLayer
會建立一個新的圖層,而驗證是否產生新圖層的方式就是採用Paint#setXfermode()
方法,經過它和下面圖層的結合關係,咱們就能知道是否生成了一個新的圖層了。當使用saveLayer
時:
@Override
protected void onDraw(Canvas canvas) {
useSaveLayer(canvas);
}
private void useSaveLayer(Canvas canvas) {
//1.先畫一個藍色圓形.
canvas.drawCircle(mRadius, mRadius, mRadius, mBlueP);
//canvas.save();
//2.這裏產生一個新的圖層
canvas.saveLayer(0, 0, mRadius + mRadius, mRadius + mRadius, null);
//3.現先在該圖層上畫一個綠色矩形
canvas.drawRect(mRadius, mRadius, mRadius + mRadius, mRadius + mRadius, mGreenP);
//4.設爲取下面的部分
mRedP.setXfermode(mDstOverXfermode);
//5.再畫一個紅色圓形,若是和下面的圖層有交集,那麼取下面部分
canvas.drawCircle(mRadius, mRadius, mRadius/2, mRedP);
}
複製代碼
當咱們使用saveLayer()
方法時,獲得的是:
save()
方法,獲得的則是:
只因此產生不一樣的結果,是由於第4步的時候,咱們給紅色畫筆設置了
DST_OVER
模式,也就是底下部分和即將畫上去的部分有重合的時候,取底下部分。當咱們在第2步當中使用
saveLayer
的時候,按咱們的假設,會產生一個新的圖層,那麼第3步的綠色矩形就是畫在這個新的透明圖層上的,所以第5步畫紅色圓形的時候,
DST
是按綠色矩形部分來算的,重疊部分只佔了紅色圓形的
1/4
,所以最後畫出來的結果跟第一張圖同樣。 而不使用
saveLayer
時,因爲沒有產生新的圖層,所以在第5步繪製的時候,
DST
實際上是由藍色圓形和綠色矩形組成的,這時候和紅色圓形的重疊部分佔了整個紅色圓形,因此最後畫上去的時候就看不到了。 這就很好地驗證了
saveLayer
會建立一個新的圖層。
saveLayerAlpha
下面,咱們再來看一下saveLayerAlpha
,這個方法能夠用來產生一個帶有透明度的圖層:
private void useSaveLayerAlpha(Canvas canvas) {
//先劃一個藍色的圓形.
canvas.drawCircle(mRadius, mRadius, mRadius, mBlueP);
//canvas.save();
//這裏產生一個新的圖層
canvas.saveLayerAlpha(0, 0, mRadius + mRadius, mRadius + mRadius, 128);
//現先在該圖層上畫一個矩形
canvas.drawRect(mRadius, mRadius, mRadius + mRadius, mRadius + mRadius, mGreenP);
}
複製代碼
最終,咱們就獲得了下面的帶有透明度的綠色矩形覆蓋在上面:
HAS_ALPHA_LAYER_XXX
和FULL_COLOR_LAYER_XXX
HAS_ALPHA_LAYER
表示圖層結合的時候,沒有繪製的地方會是透明的,而對於FULL_COLOR_LAYER_XXX
,則會強制覆蓋掉。 首先,咱們先看一下整個佈局爲一個黑色背景的Activity
,裏面有一個背景爲綠色的自定義View
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context="com.example.lizejun.repocanvaslearn.MainActivity">
<com.example.lizejun.repocanvaslearn.SaveLayerView
android:background="@android:color/holo_green_light"
android:layout_width="200dp"
android:layout_height="200dp" />
</RelativeLayout>
複製代碼
下面,咱們重寫onDraw(Canvas canvas)
方法:
private void useSaveLayerHasAlphaOrFullColor(Canvas canvas) {
//先劃一個藍色的圓形.
canvas.drawRect(0, 0, mRadius * 2, mRadius * 2, mBlueP);
//這裏產生一個新的圖層
canvas.saveLayer(0, 0, mRadius, mRadius, null, Canvas.FULL_COLOR_LAYER_SAVE_FLAG);
//canvas.saveLayer(0, 0, mRadius, mRadius, null, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
//繪製一個紅色矩形.
canvas.drawRect(0, 0, mRadius / 2, mRadius / 2, mRedP);
}
複製代碼
當採用FULL_COLOR_LAYER_SAVE_FLAG
時,對應的結果爲下圖:
HAS_ALPHA_LAYER_SAVE_FLAG
時,對應的結果爲:
能夠看到,當使用
FULL_COLOR_LAYER_SAVE_FLAG
,不只下一層本來繪製的藍色沒有用,連
View
自己的綠色背景也沒有了,最後透上來的是
Activity
的黑色背景。 關於這個方法,有幾個須要注意的地方:
View
中禁用硬件加速。setLayerType(LAYER_TYPE_SOFTWARE, null);
複製代碼
FULL_COLOR_LAYER_SAVE_FLAG
爲準。saveLayer
而且只指定MATRIX_SAVE_FLAG
或者CLIP_SAVE_FLAG
時,默認的合成方式是FULL_COLOR_LAYER_SAVE_FLAG
。CLIP_TO_LAYER_SAVE_FLAG
它在新建bitmap
前,先把canvas
給裁剪,一旦畫板被裁剪,那麼其中的各個畫布就會被受到影響,而且它是沒法恢復的。當其和CLIP_SAVE_FLAG
共用時,是能夠被恢復的。
ALL_SAVE_FLAG
對於save()
來講,它至關於MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG
。 對於saveLayer()
來講,它至關於MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG|HAS_ALPHA_LAYER_SAVE_FLAG
。