導航:首頁 > 操作系統 > androidview繪圖

androidview繪圖

發布時間:2023-01-04 12:04:05

1. android自定義View(8) 《繪制文本》

在Android中,我們經常會需要去繪制一些自己需要的控制項,所以繼承自View的自定義View就產生了。這篇文章主要介紹如何在我們的自定義View中繪制Text

先舉例是為了更清楚的看到繪制text時的幾個重要的基準線,上圖運行結果中總共有5條線,它們從上至下分別是

這里參數很容易看懂,但是這個x,y分別意味著啥呢,它就是繪制文本的基準

除了baseline是我們定義的,其他的幾個參考線都是通過我們設置的文字大小來變化的,所以我們在繪制時一定要記住我們繪制文字的基準線是baseline而不是我們的文字框的最底端,記住這一點在我們繪制文字居中是非常有用的。

今天的繪制文字就總結到這里吧,下篇再學別的有意思的~

2. Android自定義View(3) 《Canvas繪制簡單的圖形》

在Android中,我們經常會需要去繪制一些自己需要的控制項,所以繼承自View的自定義View就產生了。這篇文章主要介紹在View中的重要類,Canvas類。

Canvas是自定義View中幾個最重要的類之一,可以理解為畫布,但是我理解下來,這只是單一的圖層,每次Canvas繪制時其實都是在canvas類所定義出的一個圖層上進行繪制圖形,一旦canvas變化,則圖層也發生變化,圖層變化之前繪制的結果不變,但之後的繪制結果會以新的圖層為基準進行繪制

在這里繪制線條調用Canvas.drawLine(float startX, float startY, float stopX, float stopY,Paint paint)方法,參數分別如下

繪制矩形調用canvas.drawRect(RectF rect, Paint paint),矩形Rect的構造參數的四個參數分別為左邊界,上邊界,右邊界,下邊界的值,單位為px,RectF與Rect不同之處在於構造參數的類型一個全為float,一個全為int,運行結果如下

繪制橢圓主要調用drawOval(RectF oval, Paint paint),以一個矩形的邊界來繪制一個橢圓,運行結果如下

繪制圓主要使用Canvas.drawCircle(float cx, float cy, float radius, Paint paint),

繪制Path的方法比較多,這里只簡單示例繪制線條路徑構建一個三角形的用法,後續會再詳細介紹繪制別的類型路徑的用法,該例運行結果如下

這里我們繪制了一段文字和一根線段這里是為了演示出Canvas中的文字的繪制方法只能實現水平方向上的居中顯示,垂直方向上的垂直是需要我們再進行實現的,繪制文字有很多個,我們這里只簡單調用了設置起始點的繪制方法,關於文字的繪制其實內容還是比較多的,這里我們就只簡單的介紹,後續有時間會專門出一個文字繪制的文章,該示例運行結果如下

今天總結了Canvas中一些簡單圖形的繪制,下篇我們介紹Canvas中的一些其他內容。

3. Android - View 繪制流程

我們知道,在 Android 中,View 繪制主要包含 3 大流程:

Android 中,主要有兩種視圖: View 和 ViewGroup ,其中:

雖然 ViewGroup 繼承於 View ,但是在 View 繪制三大流程中,某些流程需要區分 View 和 ViewGroup ,它們之間的操作並不完全相同,比如:

對 View 進行測量,主要包含兩個步驟:

對於第一個步驟,即求取 View 的 MeasureSpec ,首先我們來看下 MeasureSpec 的源碼定義:

MeasureSpec 是 View 的一個公有靜態內部類,它是一個 32 位的 int 值,高 2 位表示 SpecMode(測量模式),低 30 位表示 SpecSize(測量尺寸/測量大小)。
MeasureSpec 將兩個數據打包到一個 int 值上,可以減少對象內存分配,並且其提供了相應的工具方法可以很方便地讓我們從一個 int 值中抽取出 View 的 SpecMode 和 SpecSize。

一個 MeasureSpec 表達的是:該 View 在該種測量模式(SpecMode)下對應的測量尺寸(SpecSize)。其中,SpecMode 有三種類型:

對 View 進行測量,最關鍵的一步就是計算得到 View 的 MeasureSpec ,子View 在創建時,可以指定不同的 LayoutParams (布局參數), LayoutParams 的源碼主要內容如下所示:

其中:

LayoutParams 會受到父容器的 MeasureSpec 的影響,測量過程會依據兩者之間的相互約束最終生成子View 的 MeasureSpec ,完成 View 的測量規格。

簡而言之,View 的 MeasureSpec 受自身的 LayoutParams 和父容器的 MeasureSpec 共同決定( DecorView 的 MeasureSpec 是由自身的 LayoutParams 和屏幕尺寸共同決定,參考後文)。也因此,如果要求取子View 的 MeasureSpec ,那麼首先就需要知道父容器的 MeasureSpec ,層層逆推而上,即最終就是需要知道頂層View(即 DecorView )的 MeasureSpec ,這樣才能一層層傳遞下來,這整個過程需要結合 Activity 的啟動過程進行分析。

我們知道,在 Android 中, Activity 是作為視圖組件存在,主要就是在手機上顯示視圖界面,可以供用戶操作, Activity 就是 Andorid 中與用戶直接交互最多的系統組件。

Activity 的基本視圖層次結構如下所示:

Activity 中,實際承載視圖的組件是 Window (更具體來說為 PhoneWindow ),頂層View 是 DecorView ,它是一個 FrameLayout , DecorView 內部是一個 LinearLayout ,該 LinearLayout 由兩部分組成(不同 Android 版本或主題稍有差異): TitleView 和 ContentView ,其中, TitleView 就是標題欄,也就是我們常說的 TitleBar 或 ActionBar , ContentView 就是內容欄,它也是一個 FrameLayout ,主要用於承載我們的自定義根布局,即當我們調用 setContentView(...) 時,其實就是把我們自定義的布局設置到該 ContentView 中。

當 Activity 啟動完成後,最終就會渲染出上述層次結構的視圖。

因此,如果我們要求取得到子View 的 MeasureSpec ,那麼第一步就是求取得到頂層View(即 DecorView )的 MeasureSpec 。大致過程如下所示:

經過上述步驟求取得到 View 的 MeasureSpec 後,接下來就可以真正對 View 進行測量,求取 View 的最終測量寬/高:

Android 內部對視圖進行測量的過程是由 View#measure(int, int) 方法負責的,但是對於 View 和 ViewGroup ,其具體測量過程有所差異。

因此,對於測量過程,我們分別對 View 和 ViewGroup 進行分析:

綜上,無論是對 View 的測量還是 ViewGroup 的測量,都是由 View#measure(int widthMeasureSpec, int heightMeasureSpec) 方法負責,然後真正執行 View 測量的是 View 的 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法。

具體來說, View 直接在 onMeasure(...) 中測量並設置自己的最終測量寬/高。在默認測量情況下, View 的測量寬/高由其父容器的 MeasureSpec 和自身的 LayoutParams 共同決定,當 View 自身的測量模式為 LayoutParams.UNSPECIFIED 時,其測量寬/高為 android:minWidth / android:minHeight 和其背景寬/高之間的較大值,其餘情況皆為自身 MeasureSpec 指定的測量尺寸。

而對於 ViewGroup 來說,由於布局特性的豐富性,只能自己手動覆寫 onMeasure(...) 方法,實現自定義測量過程,但是總的思想都是先測量 子View 大小,最終才能確定自己的測量大小。

當確定了 View 的測量大小後,接下來就可以來確定 View 的布局位置了,也即將 View 放置到屏幕具體哪個位置。

View 的布局過程由 View#layout(...) 負責,其源碼如下:

View#layout(...) 主要就做了兩件事:

ViewGroup 的布局流程由 ViewGroup#layout(...) 負責,其源碼如下:

可以看到, ViewGroup#layout(...) 最終也是通過 View#layout(...) 完成自身的布局過程,一個注意的點是, ViewGroup#layout(...) 是一個 final 方法,因此子類無法覆寫該方法,主要是 ViewGroup#layout(...) 方法內部對子視圖動畫效果進行了相關設置。

由於 ViewGroup#layout(...) 內部最終調用的還是 View#layout(...) ,因此, ViewGroup#onLayout(...) 就會得到回調,用於處理 子View 的布局放置,其源碼如下:

由於不同的 ViewGroup ,其布局特性不同,因此 ViewGroup#onLayout(...) 是一個抽象方法,交由 ViewGroup 子類依據自己的布局特性,擺放其 子View 的位置。

當 View 的測量大小,布局位置都確定後,就可以最終將該 View 繪制到屏幕上了。

View 的繪制過程由 View#draw(...) 方法負責,其源碼如下:

其實注釋已經寫的很清楚了, View#draw(...) 主要做了以下 6 件事:

我們知道,在 Activity 啟動過程中,會調用到 ActivityThread.handleResumeActivity(...) ,該方法就是 View 視圖繪制的起始之處:

可以看到, ActivityThread.handleResumeActivity(...) 主要就是獲取到當前 Activity 綁定的 ViewManager ,最後調用 ViewManager.addView(...) 方法將 DecorView 設置到 PhoneWindow 上,也即設置到當前 Activity 上。 ViewManager 是一個介面, WindowManager 繼承 ViewManager ,而 WindowManagerImpl 實現了介面 WindowManager ,此處的 ViewManager.addView(...) 實際上調用的是 WindowManagerImpl.addView(...) ,源碼如下所示:

WindowManagerImpl.addView(...) 內部轉發到 WindowManagerGlobal.addView(...) :

在 WindowManagerGlobal.addView(...) 內部,會創建一個 ViewRootImpl 實例,然後調用 ViewRootImpl.setView(...) 將 ViewRootImpl 與 DecorView 關聯到一起:

ViewRootImpl.setView(...) 內部首先關聯了傳遞過來的 DecorView (通過屬性 mView 指向 DecorView 即可建立關聯),然後最終調用 requestLayout() ,而 requestLayout() 內部又會調用方法 scheleTraversals() :

ViewRootImpl.scheleTraversals() 內部主要做了兩件事:

Choreographer.postCallback(...) 會申請一次 VSYNC 中斷信號,當 VSYNC 信號到達時,便會回調 Choreographer.doFrame(...) 方法,內部會觸發已經添加的回調任務, Choreographer 的回調任務有以下四種類型:

因此, ViewRootImpl.scheleTraversals(...) 內部通過 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null) 發送的非同步視圖渲染消息就會得到回調,即回調 mTra

閱讀全文

與androidview繪圖相關的資料

熱點內容
現在最流行的單片機 瀏覽:88
機頂盒刷機源碼 瀏覽:985
編碼pdf下載 瀏覽:944
隔壁同學app怎麼 瀏覽:299
c語言宏命令 瀏覽:542
php卡死源碼 瀏覽:574
time庫中的clock函數python 瀏覽:989
cad視覺移動命令怎麼打開 瀏覽:821
安卓java調用python 瀏覽:395
java標准時間 瀏覽:137
華為伺服器湖北渠道商雲主機 瀏覽:30
韓式面部護理解壓視頻 瀏覽:301
pdf換成jpg圖片 瀏覽:897
dh加密演算法 瀏覽:107
安卓手機如何隱藏微信信息提示 瀏覽:632
nodejs解壓縮 瀏覽:262
直流雙轉子壓縮機 瀏覽:952
pythonxmlstring 瀏覽:822
用私鑰加密之後可以用公鑰解密 瀏覽:788
ug如何啟動伺服器 瀏覽:444