在前面一個系列的文章中,我們以窗口為單位,分析了WindowManagerService服務的實現。同時,在再前面一個系列的文章中,我們又分析了窗口的組成。簡單來說,窗口就是由一系列的視圖按照一定的布局組織起來的。實際上,每一個視圖都是一個控件,這些控制可以將自己的UI繪制在窗口的繪圖表面上,同時還可以與用戶進行交互,即獲得用戶的鍵盤或者觸摸屏輸入。在本文中,我們就詳細分析窗口控件的上述實現原理。
老羅的新浪博客:http://weibo.com/shengyangluo,歡迎關注!
由于Android系統提供的控件比較多,因此我們只能挑一個比較有代表的控件進行分析。這個比較有代表性的控件便是TextView,其它的一些基礎控件,例如Button、EditText和CheckBox等,都是直接或者間接地以它為父類的。每一個控件的實現都是相當復雜的,不過基本上都是一些細節問題,而且不同的控件有不同的實現細節,因此,本文并不打算詳細地分析TextView的具體實現,而是從所有控件為了實現自己的功能而需要的東西出發,去分析TextView的實現框架。
那么,控件為了實現自己的功能而需要的東西是什么呢?有兩個材料是必不可少的。第一個材料是畫布,第二個材料是用戶輸入。有畫布才能繪制UI,而有用戶輸入才能與用戶進行交互。因此,接下來我們主要分析TextView的繪制流程,以及它獲得用戶輸入的過程。用戶輸入主要包括鍵盤輸入以及觸摸屏輸入,本文主要關注的是鍵盤輸入。觸摸屏輸入與鍵盤輸入的獲取過程是類似的,讀者如果有興趣的話,可以參照本文的內容來自己研究一下。
從前面Android應用程序窗口(Activity)實現框架簡要介紹和學習計劃這個系列的文章可以知道,應用程序窗口,即Activity窗口,是由一個PhoneWindow對象,一個DecorView對象,以及一個ViewRoot對象來描述的。其中,PhoneWindow對象用來描述窗口對象,DecorView對象用來描述窗口的頂層視圖,ViewRoot對象除了用來與WindowManagerService服務通信之外,還用來接收用戶輸入。窗口控件本身也是一個視圖,即一個View對象,它們是以樹形結構組織在一起形成整個窗口的UI的。為了簡單起見,本文假設要分析的TextView控件是直接以窗口的頂層視圖為父視圖的,即以DecorView為父視圖,如圖1所示:
圖1 窗口結構示意圖以及DecorView、TextView的類關系圖
圖1顯示的是一個包含了TextView控件的Activity窗口的結構示意圖以及DecorView、TextView的簡單類關系圖,從中可以看出:
1. 用戶輸入首先是由ViewRoot接收,然后再分發給TextView處理;
2. DecorView是一個視圖容器,因此,它是從ViewGroup繼承下來,而ViewGroup本身又是從View繼承下來的;
3. TextView是一個簡單視圖,因此,它是直接繼承了View。
接下來,我們就以圖1所示的Activity窗口為例,來分析TextView控件的UI繪制框架及其獲得鍵盤輸入的過程。
一. TextView控件的UI繪制框架
從前面Android應用程序窗口(Activity)的測量(Measure)、布局(Layout)和繪制(Draw)過程分析一文可以知道,Activity窗口的UI繪制操作分為三步來走,分別是測量、布局和繪制。
1. 測量
為了能告訴父視圖自己的所占據的空間的大小,所有控件都必須要重寫父類View的成員函數onMeasure。
TextView類的成員函數onMeasure的實現如下所示:
[java] view plaincopyprint?
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
......
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//計算TextView控件的寬度和高度
......
setMeasuredDimension(width, height);
}
......
}
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
......
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//計算TextView控件的寬度和高度
......
setMeasuredDimension(width, height);
}
......
}
這個函數定義在文件frameworks/base/core/java/android/widget/TextView.java中。
參數widthMeasureSpec和heightMeasureSpec分別用來描述寬度測量規范和高度測量規范。測量規范使用一個int值來表法,這個int值包含了兩個分量。
第一個是mode分量,使用最高2位來表示。測量模式有三種,分別是MeasureSpec.UNSPECIFIED(0)、MeasureSpec.EXACTLY(1)、和MeasureSpec.AT_MOST(2)。
第二個是size分量,使用低30位來表示。當mode分量等于MeasureSpec.EXACTLY時,size分量的值就是父視圖要求當前控件要設置的寬度或者高度;當mode分量等于MeasureSpec.AT_MOST時,size分量的值就是父視圖限定當前控件可以設置的最大寬度或者高度;當mode分量等于MeasureSpec.UNSPECIFIED時,父視圖不限定當前控件所設置的寬度或者高度,這時候當前控件一般就按照實際需求來設置自己的寬度和高度。
原文轉自:http://blog.csdn.net/luoshengyang/article/details/8636153