1. 程式人生 > >Android-動畫繪製2D繪製和硬體加速的不支援問題

Android-動畫繪製2D繪製和硬體加速的不支援問題

前言

好久沒寫部落格了,現在都是一個月才寫一些,也是因為自己沒什麼料,技術仍然有待提高

問題描述

canvas是我們常用的功能,我們經常在canvas畫布上繪製各種自己想要的效果。

今天分享一個自己在呼叫canvas繪製動畫效果的時候遇到的一個問題,由於目標動畫效果需要只顯示一塊區域,比方說我們的圖很多,但是我們只想要它顯示一個小圓,這時候我們可以用clipPath方法來實現。

    /**
     * Intersect the current clip with the specified path.
     *
     * @param path The path to intersect with the current clip
     * @return
true if the resulting is non-empty */
public boolean clipPath(@NonNull Path path) { return clipPath(path, Region.Op.INTERSECT); }

如何只顯示一個小圓的區域,可以呼叫最簡單的
Path#addCircle()

    /**
     * Add a closed circle contour to the path
     *
     * @param x   The x-coordinate of the center of a circle to add to the path
     * @param
y The y-coordinate of the center of a circle to add to the path * @param radius The radius of a circle to add to the path * @param dir The direction to wind the circle's contour */
public void addCircle(float x, float y, float radius, Direction dir) { isSimplePath = false
; native_addCircle(mNativePath, x, y, radius, dir.nativeInt); }

也可以繪製一個橢圓區域來實現圓形,我們只需要保證外接矩陣是正方形就行
Path#addOval()

    /**
     * Add a closed oval contour to the path
     *
     * @param oval The bounds of the oval to add as a closed contour to the path
     * @param dir  The direction to wind the oval's contour
     */
    public void addOval(RectF oval, Direction dir) {
        addOval(oval.left, oval.top, oval.right, oval.bottom, dir);
    }

還有一種方式,我們可以自己繪製一段圓弧,在之前的部落格中我提到如何利用貝塞爾曲線繪製一個圓形,只不過過程略顯繁瑣。

當然除了呼叫clipPath,我們還可以利用其他api來實現這種效果。
有時候覺得,做動畫是一件很有意思的事情,但是真正到了過機型的時候,又是一件最煩心的事。

我們發現,在部分機型上,clipPath()方法會失效,明明想要切的是圓形,卻得到的只是矩陣。
看一下我們的示例程式碼:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        testAddCircle(canvas);
    }

    private void testAddCircle(Canvas canvas) {
        mClipPath = new Path();
        mClipPath.addCircle(80, 80, 80, Path.Direction.CCW);
        canvas.clipPath(mClipPath);
        canvas.drawColor(0xFF892342);
    }

在某臺4.0的手機上執行,再看一下結果:

這裡寫圖片描述

後來我們在Overflow上找到的答案:

Canvas.clipPath() support with hardware acceleration has been reintroduced since API 18.

API18以下的機型,當開啟硬體加速的時候,clipPath()方法會不支援且失效。

關於硬體加速

API11(Android3.0)的時候開始支援,開啟硬體加速能夠讓我們的圖形繪製更加流暢,API14的時候,預設所有的Activity都是開啟硬體加速的。

Android 的繪圖模式

當硬體加速開啟的時候,Framework層會採用新的繪圖模式(display lists)來把你的應用程式繪製到螢幕上。

1) 軟體繪圖模式(Software-based drawing model)

在軟體繪圖模式中,檢視通過以下兩部來實現繪製:
呼叫Invalidate重新整理檢視框架
重新繪製檢視框架

這種繪圖模式的隱患:

在每次繪圖的時候,會帶來大量的程式碼執行,例如,當我們的呼叫某個Button的invalidate()方法的時候,如果這個Button處於某個ViewGroup的頂層檢視中,會造成這個ViewGroup也會重新繪製。這樣就造成了無意義的繪製,浪費了大量資源。同時,這樣的繪圖模式可能會在程式中隱藏一些Bug,尤其是在上一種情況中,ViewGroup本來不需要重新繪製,卻因為和Button有了檢視交集,而重新繪製重新整理,可能會出現我們原本不期望出現的情況。

Note: Android檢視會自動呼叫invalidate在其屬性更改時,如背景顏色或TextView中的文字。

2) 硬體繪圖模式(Hardware accelerated drawing model)

硬體加速開啟之後,會啟用硬體繪圖模式,主要有以下步驟:

Invalidate the hierarchy 呼叫invalidate重新整理檢視框架
Record and update display lists 記錄和更新展示層集合
Draw the display lists 繪製展示層集合

Android框架依然會使用invalidate方法和draw方法來重新整理和繪製檢視,只不過不在呼叫後立刻重新繪製。Disaplay Lists是一個繪製命令緩衝區,也就是說,當View的成員函式onDraw被呼叫時,我們呼叫通過引數傳遞進來的Canvas的drawXXX成員函式繪製圖形時,我們實際上只是將對應的繪製命令以及引數儲存在一個Display List中。接下來再通過Display List Renderer執行這個Display List的命令,這個過程稱為Display List Replay。

引進Display List的概念有什麼好處呢?

主要是兩個好處。第一個好處是在下一幀繪製中,如果一個View的內容不需要更新,那麼就不用重建它的DisplayList,也就是不需要呼叫它的onDraw成員函式。第二個好處是在下一幀中,如果一個View僅僅是一些簡單的屬性發生變化,例如位置和Alpha值發生變化,那麼也無需要重建它的Display List,只需要在上一次建立的DisplayList中修改一下對應的屬性就可以了,這也意味著不需要呼叫它的onDraw成員函式。這兩個好處使用在繪製應用程式視窗的一幀時,省去很多應用程式程式碼的執行,也就是大大地節省了CPU的執行時間。

然而,不是所有canvas繪圖都支援硬體加速的,從developer開發者網站我可以看到:
https://developer.android.com/guide/topics/graphics/hardware-accel.html#model
clipPath()方法從API18開始才能支援硬體加速,另外,drawBitmapMesh()方法也是從API18才開始支援硬體加速,更多的2D繪製支援版本我們可以瀏覽開發者網站。

解決方案

Overflow上的開發者提供了兩個解決方案:

1) 低版本放棄硬體加速

我們都知道3.0之後硬體加速是預設開啟的,它能為我們提供更加流暢的介面效果,當我們需要繪製複雜動畫的時候,就需要開啟硬體加速,否則動畫效果會極差,出現卡頓現象。
這時候,如果我們只是需要切一個靜態的圖片,例如我們要實現一個圓角頭像圖片,就可以放棄硬體加速。關閉硬體加速可以從三個途徑來實現,這裡我們直接從View層來禁用硬體加速。

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2
        && Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
    setLayerType(LAYER_TYPE_SOFTWARE, null);
}

2) 低版本放棄clipPath()方法

前面說到如果是複雜動畫,在關閉硬體加速的情況下,會失去動畫的流暢性。這時候我們只能放棄clipPath()方法。

if (doClip) {
    try {
        canvas.clipPath(clipPath);
    } catch (UnsupportedOperationException e) {
        Log.e(TAG, "clipPath() not supported");
        doClip = false;
    }
}

捕捉到異常之後,我們利用其他方法來實現類似的效果,例如通過Xfermode或者BitmapShader的方式來實現。

3) 不放棄clipPath()方法也不放棄硬體加速(11.17在看硬體加速原理的時候更新)

在前面分析開啟硬體加速的時候,Android是通過硬體模式來繪製,onDraw()中我們寫入的繪製程式碼都不會立刻執行,而會一條一條儲存在DisplayList中,也就是通過onDraw()傳入引數canvas呼叫的繪製方法,最後會使用硬體加速來繪製。
那麼,對於使用了GPU不支援的2DUI繪製命令的View,我們的做法是建立一個新的Canvas,這個Canvas的底層是一個Bitmap,也就是說,我們那些不支援的2DUI繪製都發生在這個Bitmap上。繪製完成之後,再把這個Bitmap再被記錄在其Parent View的Display List中。
而當Display List的命令被執行時,記錄在裡面的Bitmap再通過Open GL命令來繪製。

 void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mTempCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        mTempCanvas.clipPath(mClipPath);
        mTempCanvas.drawBitmap(mTargetBitmap, null, mRect, null);
        Rect rect = new Rect(0, 0, sceneWidth, sceneHeight);
        canvas.drawBitmap(mTempBitmap, rect, rect, null);
}

最後,連結一篇文章,其中詳細地講解了硬體加速的由來以及原理:

老羅寫的部落格真是太賞心悅目了。

相關推薦

Android-動畫繪製2D繪製硬體加速支援問題

前言 好久沒寫部落格了,現在都是一個月才寫一些,也是因為自己沒什麼料,技術仍然有待提高 問題描述 canvas是我們常用的功能,我們經常在canvas畫布上繪製各種自己想要的效果。 今天分享一個自己在呼叫canvas繪製動畫效果的時候遇到的一個問題,

Android view layer type(hardware layer硬體加速的區別)

誤解: 之前混淆了 硬體加速 和 view hardware layer。就奇怪 硬體加速 是API 14後預設的,那為什麼動畫結束時,又要吧layer type設定為none,說這樣可以節省記憶體(video memory) 硬體加速繪製模型(hard

android中如何使用GPU實現硬體加速,3D渲染

已開通新的部落格,後續文字都會發到新部落格 http://www.0xfree.top --- 首先來看一些名詞解釋     GPU:Graphic  Processing Unit (圖形處理器)&nb

HenCoder Android 自定義 View 1-8 硬體加速

硬體加速這個詞每當被提及,很多人都會感興趣。這個詞給大部分人的概念大致有兩個:快速、不穩定。對很多人來說,硬體加速似乎是一個只可遠觀而不可褻玩的高階科技:是,我聽說它很牛逼,但我不敢「亂」用,因為我怕 hold 不住。 今天我試著就把硬體加速的外衣脫掉(並沒有),聊一聊它的

Android 動畫類的特點區別

DVM指dalivk的虛擬機器。每一個Android應用程式都在它自己的程序中執行,都擁有一個獨立的Dalvik虛擬機器例項。而每一個DVM都是在Linux中的一個程序,所以說可以認為是同一個概念。  1、sim卡的EF檔案有何作用。 sim卡的檔案系統有自己規範,主要是

webview的白屏,硬體加速

      Android從3.0(API Level 11)開始,在繪製View的時候支援硬體加速,充分利用GPU的特性,使得繪製更加平滑,但是會多消耗一些記憶體。       開啟或關閉硬體加速:       由於硬體加速自身並非完美無缺,所以Android提供選項來開啟或者關閉硬體加速,預設是關閉。可

AutoCAD2016硬體加速識別GTX970M獨立顯示卡的解決方法

先附上AutoCAD2016  本體安裝檔案&升級補丁的下載地址 連結:https://pan.baidu.com/s/1rrrn7xTm9eyStEiXuRbJ-g 密碼:t2h3   正確的安裝順序是   1.AutoCAD_2016_S

linux系統時鐘硬體時鐘一致

在做DB2 叢集複製的時候要求兩臺主機想時間一致。 但是在一臺主機上系統時間和硬體時間相差12個小時左右;手動同步後,重啟後又相差12個小時左右。 為什麼會是這樣的,先介紹下系統時鐘和硬體時鐘的區別:

在安裝Win7時,出現提示“Windows無法安裝到這個磁碟。這臺計算機的硬體可能支援啟動到此磁碟。請確保在計算機的bios選單中啟用了磁碟的控制器。”

        博主為小白,我在通過U盤安裝win7系統的過程中遇到了很多問題。在這裡向大家分享其中之一的解決方法。        博主電腦型號:話說(ASUS) VM590Z題外話,在安裝win7時需要將U盤插入usb2.0的插口中,避免安裝過程中的USB驅動問題。接下來回

自定義瀏覽器滾動條的樣式,打造屬於你的滾動條風格——相容IEwebkit(ff支援)

前段時間,到網上找素材時,看到了一個很個性的滾動條式,開啟Chrome的除錯工具看了一下,發現不是用JavaScript來模擬實現的,覺得 有必要折騰一下。於是在各大瀏覽器中對比了一下,發現只用Chrome適用,也就是說這個用的是Chrome的私有CSS屬性。便百之谷之後,發現原來不 僅僅只用Chrome,

Android中GPU硬體加速控制及其在2D圖形繪製上的侷限

圖形的渲染可分為兩種:軟體渲染和硬體渲染。軟體渲染是靠CPU計算各種座標並繪製,主要是佔用記憶體;硬體渲染是靠GPU,主要佔用視訊記憶體,一般的3D圖形程式(OpenGL、DirectX)都是GPU加速的。 在Android3.0之前,2D繪圖API只支援軟體

Android大圖繪製——硬體加速限制分析與方案

最近在做PhotoView圖片的效果定製時,在載入展示圖片情境下,統一把圖片按照螢幕寬度作為固定值,計算寬高的縮放比然後對Bitmap進行伸縮,這樣可以避免一般情況下的大圖載入產生——OOM和trying to draw too large(xxxbyte

Android:使用PathMeasure繪製動畫效果的搜尋按鈕

首先上效果圖: 該搜尋按鈕有4種狀態: 1. 預設狀態:一個靜態的放大鏡; 2. 開始搜尋狀態:放大鏡逐漸縮小為一個點; 3. 正在搜尋狀態:一個動態的圈; 4. 結束搜尋狀態:放大鏡由一個點恢復初始狀態。 此處點選一下模擬開始搜尋,再點選一下搜

Android自定義View之使用Path繪製手勢軌跡水波效果

先看下效果圖: 繪製軌跡 繪製手指的軌跡主要是攔截View的onTouchEvent()方法,並根據手指的軌跡繪製path。path中有兩種可以實現的方法 1、Path.lineTo(x,y)方法 public class MoveP

Android 動畫fillAfterfillBefore

targe als 評論 href fill final javase 動畫 mage fillBefore是指動畫結束時畫面停留在此動畫的第一幀; fillAfter是指動畫結束是畫面停留在此動畫的最後一幀。 Java代碼設置如下: /*****動畫結束時,停留在最後一幀

CSS動畫的效能分析瀏覽器GPU加速

此文已由作者袁申授權網易雲社群釋出。 歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。 有數的資料大屏可以在一塊螢幕上展示若干張不同的圖表,以炫酷的方式展示各種業務資料。其中有些圖表使用CSS實現了餅圖輪播、地圖示記點閃爍等動畫,然而在一張大屏上同時顯示了許多張圖表時,持續的動畫效果有時會出現掉幀、卡頓的

小白python學習——matplotlib篇——繪製簡單點直線、顏色,字型大小改變

1.直線: import matplotlib.pyplot as plt input_values=[1,2,3,4,5] squares = [1,4,9,16,25] #設定圖表標題,並給座標軸加上標籤 plt.plot(input_values,squares,linewidth=5)

Android開發 ImageView上繪製旋轉圓環(透明度不同的旋轉圓環,利用canvas drawArc實現)

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

android動畫四·InterpolatorViewPropertyAnimator的用法

動畫 Interpolator的用法 使用屬性動畫時,系統預設的Interpolator其實就是一個先加速後減速的Interpolator,對應的實現類就是AccelerateDecelerateInterpolator。 9種插值器:(系統預設的)

android動畫三·ValueAnimatorObjectAnimator的高階用法

內容 動畫 ValueAnimator的高階用法 假如:我們有一個自定義的View,在這個View當中有一個Point物件用於管理座標,然後在onDraw()方法當中就是根據這個Point物件的座標值來進行繪製的。如果可以對Point物件進行動畫操作,那麼整個自定義