導航:首頁 > 源碼編譯 > androidview類源碼解析

androidview類源碼解析

發布時間:2023-03-05 14:51:52

Ⅰ View繪制流程(一)

最近在學習 View 的繪制流程,看了幾篇不錯的博客( ViewRootImpl的獨白,我不是一個View(布局篇) 、 android應用層View繪制流程與源碼分析 )自己對照源碼,梳理了一遍。

在Activity的onResume之後,當前Activity的Window對象中的View會被添加在WindowManager中。

整個View樹的繪圖流程是在 ViewRootImpl 類的 performTraversals() 方法(這個方法巨長)開始的,該方法做的執行過程主要是根據之前設置的狀態,判斷是否重新計算視圖大小 (measure) 、是否重新放置視圖的位置 (layout) 、以及是否重繪 (draw) ,其核心也就是通過判斷來選擇順序執行這三個方法。

ViewRootImpl 調用 performMeasure 執行Window對應的View的測量。

int widthMeasureSpec :他由兩部分組成, 高2位表示MODE ,定義在MeasureSpec類(View的內部類)中,有三種類型, MeasureSpec.EXACTLY 表示確定大小, MeasureSpec.AT_MOST 表示最大大小, MeasureSpec.UNSPECIFIED 不確定。 低30位表示size ,也就是父View的大小。對於系統Window類的DecorVIew對象Mode一般都為MeasureSpec.EXACTLY ,而size分別對應屏幕寬高。對於子View來說大小是由父View和子View共同決定的。

默認的尺寸大小即傳入的參數都是通過 getDefaultSize 返回的,我們就看一下該方法的實現。

到此一次最基礎的元素View的 measure 過程就完成了。

View實際是嵌套的,而且measure是遞歸傳遞的,所以每個View都需要 measure ,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了 measureChildren , measureChild , measureChildWithMargins 方法來對子視圖進行測量, measureChildren 內部實質只是循環調用 measureChild , measureChild 和 measureChildWithMargins 的區別就是是否把 margin padding 也作為子視圖的大小。 ViewGroup 本身不調用 measureChildWithMargins 和 measureChildren 方法,由繼承類通過for循環調用此方法進行子View的測量。下面看一下ViewGroup中稍微復雜的 measureChildWithMargins 方法。

getChildMeasureSpec 的邏輯是通過其父View提供的 MeasureSpec 參數得到 specMode 和 specSize ,然後根據計算出來的 specMode 以及子View的 childDimension (layout_width或layout_height)來計算自身的 measureSpec ,如果其本身包含子視圖,則計算出來的 measureSpec 將作為調用其子視圖 measure 函數的參數,同時也作為自身調用 setMeasuredDimension 的參數,如果其不包含子視圖則默認情況下最終會調用 onMeasure 的默認實現,並最終調用到 setMeasuredDimension 。

Activity 的 onResume 之後,當前 Activity Window 對象中的View(DecorView)會被添加在 WindowManager 中。也就是在 ActivityThread 的 handleResumeActivity 方法中調用 wm.addView(decor, l); 將DecorView添加到 WindowManager 中;

WindowManager 繼承 ViewManager ,它的實現類為 WindowManagerImpl ,該類中的方法的具體實現是由其代理類 WindowManagerGlobal 實現的;

在它的 addView 方法中會創建 ViewRootImpl 的實例,然後將Window對應的View(DecorView),ViewRootImpl,LayoutParams順序添加在WindowManager中,最後將Window所對應的View設置給創建的ViewRootImpl,通過 ViewRootImpl 來更新界面並完成Window的添加過程;

設置view調用的是 ViewRootImpl 的 setView 方法,在該方法中調用 requestLayout(); 方法來非同步執行view的繪制方法;之後將 Window 添加到屏幕,通過 WMS (跨進程通信)

在 requestLayout 方法中最終會調用 ViewRootImpl 的 performTraversals(); 方法,該方法做的執行過程主要是根據之前設置的狀態,判斷是否重新計算視圖大小 (measure) 、是否重新放置視圖的位置 (layout) 、以及是否重繪 (draw) ,其核心也就是通過判斷來選擇順序執行這三個方法: performMeasure 、 performLayout 、 performDraw ;

在 performMeasure 方法中調用的是 View 的 measure 方法,該方法是 final 修飾,不能被子類重寫,在該方法中實際調用的是 View 的 onMeasure 方法,子類可以重寫 onMeasure 方法來實現自己的測量規則。

View 默認的 onMeasure 方法很簡單只是調用了 setMeasuredDimension 方法,該方法的作用是給 View 的成員變數 mMeasuredWidth mMeasuredHeight 賦值,View的測量主要就是給這兩個變數賦值,這兩個變數一旦賦值,也就意味著測量過程的結束。

setMeasuredDimension 方法傳入的尺寸是通過 getDefaultSize(int size, int measureSpec); 方法返回的,在
getDefaultSize 方法中解析 measureSpec Mode Size ,如果Mode為 MeasureSpec.AT_MOST 或者 MeasureSpec.EXACTLY ,最終的size的值為解析後的size;如果 Mode MeasureSpec.UNSPECIFIED ,最終的size為建議的最小值= getSuggestedMinimumWidth ,該方法的具體實現為 return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); ,建議的最小寬度和高度都是由View的Background尺寸與通過設置View的 miniXXX 屬性共同決定的

measureSpec 是由 getRootMeasureSpec 方法決定的: measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY); 根布局的大小是 Window 的大小,Window大小是不能改變的,總是全屏的。

View實際是嵌套的,而且measure是遞歸傳遞的,所以每個View都需要measure,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了 measureChildren , measureChild , measureChildWithMargins 方法來對子視圖進行測量, measureChildren 內部實質只是循環調用 measureChild , measureChild 和 measureChildWithMargins 的區別就是是否把 margin padding 也作為子視圖的大小。

measureChildWithMargins 方法的作用就是對 父View 提供的 measureSpec 參數結合 子View LayoutParams 參數進行了調整,然後再來調用 child.measure() 方法,具體通過方法 getChildMeasureSpec 方法來進行參數調整。計算出來自身的 measureSpec 作為調用其子視圖 measure 方法的參數,同時也作為自身調用 setMeasuredDimension 的參數,如果其不包含子視圖則默認情況下最終會調用 onMeasure 的默認實現,並最終調用到 setMeasuredDimension 。

最終決定 View measure 大小是 View 的 setMeasuredDimension 方法,該方法就是設置mMeasuredWidth和mMeasuredHeight的大小,ViewGroup在 onMeasure 方法調用 setMeasuredDimension 之前調整了 measureSpec

Ⅱ Android的View類是怎樣定義的源代碼是什麼

view的定義還真不是一兩句話能說清楚的。源碼里代碼2萬多行,最前面的注釋有500多行。

如果你用android studio,直接Ctrl 點擊View應該就能看到源碼。
當然也可以在網頁里查看源碼
http ://androidxref.com

Ⅲ Android TV 焦點原理源碼解析

相信很多剛接觸AndroidTV開發的開發者,都會被各種焦點問題給折磨的不行。不管是學技術還是學習其他知識,都要學習和理解其中原理,碰到問題我們才能得心應手。下面就來探一探Android的焦點分發的過程。

Android焦點事件的分發是從ViewRootImpl的processKeyEvent開始的,源碼如下:

源碼比較長,下面我就慢慢來講解一下具體的每一個細節。

dispatchKeyEvent方法返回true代表焦點事件被消費了。

ViewGroup的dispatchKeyEvent()方法的源碼如下:

(2)ViewGroup的dispatchKeyEvent執行流程

(3)下面再來瞧瞧view的dispatchKeyEvent方法的具體的執行過程

驚奇的發現執行了onKeyListener中的onKey方法,如果onKey方法返回true,那麼dispatchKeyEvent方法也會返回true

可以得出結論:如果想要修改ViewGroup焦點事件的分發,可以這么干:

注意:實際開發中,理論上所有焦點問題都可以通過給dispatchKeyEvent方法增加監聽來來攔截來控制。

(1)dispatchKeyEvent方法返回false後,先得到按鍵的方向direction值,這個值是一個int類型參數。這個direction值是後面來進行焦點查找的。

(2)接著會調用DecorView的findFocus()方法一層一層往下查找已經獲取焦點的子View。
ViewGroup的findFocus方法如下:

View的findFocus方法

說明:判斷view是否獲取焦點的isFocused()方法, (mPrivateFlags & PFLAG_FOCUSED) != 0 和view 的isFocused()方法是一致的。

其中isFocused()方法的作用是判斷view是否已經獲取焦點,如果viewGroup已經獲取到了焦點,那麼返回本身即可,否則通過mFocused的findFocus()方法來找焦點。mFocused其實就是ViewGroup中獲取焦點的子view,如果mView不是ViewGourp的話,findFocus其實就是判斷本身是否已經獲取焦點,如果已經獲取焦點了,返回本身。

(3)回到processKeyEvent方法中,如果findFocus方法返回的mFocused不為空,說明找到了當前獲取焦點的view(mFocused),接著focusSearch會把direction(遙控器按鍵按下的方向)作為參數,找到特定方向下一個將要獲取焦點的view,最後如果該view不為空,那麼就讓該view獲取焦點。

(4)focusSearch方法的具體實現。

focusSearch方法的源碼如下:

可以看出focusSearch其實是一層一層地網上調用父View的focusSearch方法,直到當前view是根布局(isRootNamespace()方法),通過注釋可以知道focusSearch最終會調用DecorView的focusSearch方法。而DecorView的focusSearch方法找到的焦點view是通過FocusFinder來找到的。

(5)FocusFinder是什麼?

它其實是一個實現 根據給定的按鍵方向,通過當前的獲取焦點的View,查找下一個獲取焦點的view這樣演算法的類。焦點沒有被攔截的情況下,Android框架焦點的查找最終都是通過FocusFinder類來實現的。

(6)FocusFinder是如何通過findNextFocus方法尋找焦點的。

下面就來看看FocusFinder類是如何通過findNextFocus來找焦點的。一層一層往下看,後面會執行findNextUserSpecifiedFocus()方法,這個方法會執行focused(即當前獲取焦點的View)的findUserSetNextFocus方法,如果該方法返回的View不為空,且isFocusable = true && isInTouchMode() = true的話,FocusFinder找到的焦點就是findNextUserSpecifiedFocus()返回的View。

(7)findNextFocus會優先根據XML里設置的下一個將獲取焦點的View ID值來尋找將要獲取焦點的View。

看看View的findUserSetNextFocus方法內部都幹了些什麼,OMG不就是通過我們xml布局裡設置的nextFocusLeft,nextFocusRight的viewId來找焦點嗎,如果按下Left鍵,那麼便會通過nextFocusLeft值里的View Id值去找下一個獲取焦點的View。

可以得出以下結論:

1. 如果一個View在XML布局中設置了focusable = true && isInTouchMode = true,那麼這個View會優先獲取焦點。

2. 通過設置nextFocusLeft,nextFocusRight,nextFocusUp,nextFocusDown值可以控制View的下一個焦點。

Android焦點的原理實現就這些。總結一下:

為了方便同志們學習,我這做了張導圖,方便大家理解~

Ⅳ Android RecyclerView的布局管理器 GridLayoutManager源碼分析<三>

Android RecyclerView繪制頁面的源碼分析<一>

Android RecyclerView布局管理器LinearLayoutManager源碼分析<二>

以上兩篇講述了RecycerView LinearLayoutManager 頁面繪制以及子條目的布局,LinearLayoutManager 是一個線性的管理器即是控制垂直以及水平展示 一個條目表示一行,然而顯示生活中有很多需求是一行展示多個子條目這個時候就用到了 GridLayoutManager 表格布局管理器 來實現這種需求
GridLayoutManager :繼承於LinearLayoutManager的網格狀布局管理器 默認一行展示一個

GridLayoutManager網格布局管理器 實現一行展示多個條目,本章解釋了GridLayoutManager的簡單源碼實現表格布局的大概調用,下一章展示復雜的表格布局即展示不顧地條目數的表格布局

Ⅳ Android源碼解析Window系列第(一)篇---Window的基本認識和Activity的載入流程

您可能聽說過View ,ViewManager,Window,PhoneWindow,WindowManager,WindowManagerService,可是你知道這幾個類是什麼關系,幹嘛用的。概括的來說,View是放在Window中的,Window是一個抽象類,它的具體實現是PhoneWindow,PhoneWindow還有個內部類DecorView,WindowManager是一個interface,繼承自ViewManager,它是外界訪問Window的入口,,提供了add/remove/updata的方法操作View,WindowManager與WindowManagerSerice是個跨進程的過程,WindowManagerService的職責是對系統中的所有窗口進行管理。如果您不太清楚,建議往下看,否則就不要看了。

Android系統的Window有很多種,大體上來說,Framework定義了三種窗口類型;

這就是Framework定義了三種窗口類型,這三種類型定義在WindowManager的內部類LayoutParams中,WindowManager講這三種類型 進行了細化,把每一種類型都用一個int常量來表示,這些常量代表窗口所在的層,WindowManagerService在進行窗口疊加的時候,會按照常量的大小分配不同的層,常量值越大,代表位置越靠上面, 所以我們可以猜想一下,應用程序Window的層值常量要小於子Window的層值常量,子Window的層值常量要小於系統Window的層值常量。 Window的層級關系如下所示。

上面說了Window分為三種,用Window的type區分,在搞清楚Window的創建之前,我們需要知道怎麼去描述一個Window,我們就把Window當做一個實體類,給我的感覺,它必須要下面幾個欄位。

實際上WindowManager.LayoutParams對Window有很詳細的定義。

提取幾個重要的參數

Window是一個是一個抽象的概念,千萬不要認為我們所看到的就是Window,我們平時所看到的是視圖,每一個Window都對應著一個View,View和Window通過ViewRootImpl來建立聯系。有了View,Window的存在意義在哪裡呢,因為View不能單獨存在,它必須依附著Window,所以有視圖的地方就有Window,比如Activity,一個Dialog,一個PopWindow,一個菜單,一個Toast等等。

通過上面我們知道視圖和Window的關系,那麼有一個問題,是先有視圖,還是先有Window。這個答案只有在源碼中找了。應用程序的入口類是ActivityThread,在ActivityThread中有performLaunchActivity來啟動Activity,這個performLaunchActivity方法內部會創建一個Activity。

如果activity不為null,就會調用attach,在attach方法中通過PolicyManager創建了Window對象,並且給Window設置了回調介面。

PolicyManager的實現類是Policy

這樣Window就創建出來了, 所以先有Window,後有視圖,視圖依賴Window存在 ,再說一說視圖(Activity)為Window設置的回調介面。

Activity實現了這個回調介面,當Window的狀態發生變化的時候,就會回調Activity中實現的這些介面,有些回調介面我們還是熟悉的,dispatchTouchEvent,onAttachedToWindow,onDetachedFromWindow等。

下面分析view是如何附屬到window上的,通過上面可以看到,在attach之後就要執行callActivityOnCreate,在onCreate中我們會調用setContentView方法。

getWindow獲取了Window對象,Window的具體實現類是PhoneWindow,所以要看PhoneWindow的setContentView方法。

這里涉及到一個mContentParent變數,他是一個DecorView的一部分,DecorView是PhoneWindow的一個內部類,我先介紹一下關於DecorView的知識。

DecorView是Activity的頂級VIew,DecorView繼承自FrameLayout,在DecorView中有上下兩個部分,上面是標題欄,下面是內容欄,我們通過PhoneWindow的setContentView所設置的布局文件是加到內容欄(mContentParent)裡面的,View層的事件都是先經過DecorView在傳遞給我們的View的。

OK在回到setContentView的源碼分析,我們可以得到Activity的Window創建需要三步。

- 1、 如果沒有DecorView,在installDecor中創建DecorView。

- 2、將View添加到decorview中的mContentParent中。

- 3、回調Activity的onContentChanged介面。

先看看第一步,installDecor的源碼

installDecor中調用了generateDecor,繼續看

直接給new一個DecorView,有了DecorView之後,就可以載入具體的布局文件到DecorView中了,具體的布局文件和系統和主題有關系。

在看第二步,將View添加到decorview中的mContentParent中。

直接將Activity視圖加到DecorView的mContentParent中,最後一步,回調Activity的onContentChanged介面。在Activity中尋找onContentChanged方法,它是個空實現,我們可以在子Activity中處理。

到此DecorView被創建完畢,我們一開始從Thread中的handleLaunchActivity方法開始分析,首先載入Activity的位元組碼文件,利用反射的方式創建一個Activity對象,調用Activity對象的attach方法,在attach方法中,創建系統需要的Window並為設置回調,這個回調定義在Window之中,由Activity實現,當Window的狀態發生變化的時候,就會回調Activity實現的這些回調方法。調用attach方法之後,Window被創建完成,這時候需要關聯我們的視圖,在handleLaunchActivity中的attach執行之後就要執行handleLaunchActivity中的callActivityOnCreate,在onCreate中我們會調用setContentView方法。通過setContentView,創建了Activity的頂級View---DecorView,DecorView的內容欄(mContentParent)用來顯示我們的布局。 這個是我們上面分析得到了一個大致流程,走到這里,這只是添加的過程,還要有一個顯示的過程,顯示的過程就要調用handleLaunchActivity中的handleResumeActivity方法了。最後會調用makeVisible方法。

這裡面首先拿到WindowManager對象,用tWindowManager 的父介面ViewManager接收,ViewManager可以
最後調用 mDecor.setVisibility(View.VISIBLE)設置mDecor可見。到此,我們終於明白一個Activity是怎麼顯示在我們的面前了。
參考鏈接:
http://blog.csdn.net/feiclear_up/article/details/49201357

Ⅵ android的自定義View的實現原理哪位能給我個思路呢。謝謝。

如果說要按類型來劃分的話,自定義View的實現方式大概可以分為三種,自繪控制項、組合控制項、以及繼承控制項。那麼下面我們就來依次學習一下,每種方式分別是如何自定義View的。

一、自繪控制項

自繪控制項的意思就是,這個View上所展現的內容全部都是我們自己繪制出來的。繪制的代碼是寫在onDraw()方法中的,而這部分內容我們已經在Android視圖繪制流程完全解析,帶你一步步深入了解View(二)中學習過了。

下面我們准備來自定義一個計數器View,這個View可以響應用戶的點擊事件,並自動記錄一共點擊了多少次。新建一個CounterView繼承自View,代碼如下所示:

<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ffcb05">

<Button
android:id="@+id/button_left"
android:layout_width="60dp"
android:layout_height="40dp"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:background="@drawable/back_button"
android:text="Back"
android:textColor="#fff"/>

<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="ThisisTitle"
android:textColor="#fff"
android:textSize="20sp"/>

</RelativeLayout>

在這個布局文件中,我們首先定義了一個RelativeLayout作為背景布局,然後在這個布局裡定義了一個Button和一個TextView,Button就是標題欄中的返回按鈕,TextView就是標題欄中的顯示的文字。

接下來創建一個TitleView繼承自FrameLayout,代碼如下所示:

{

privateButtonleftButton;

privateTextViewtitleText;

publicTitleView(Contextcontext,AttributeSetattrs){
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.title,this);
titleText=(TextView)findViewById(R.id.title_text);
leftButton=(Button)findViewById(R.id.button_left);
leftButton.setOnClickListener(newOnClickListener(){
@Override
publicvoidonClick(Viewv){
((Activity)getContext()).finish();
}
});
}

publicvoidsetTitleText(Stringtext){
titleText.setText(text);
}

publicvoidsetLeftButtonText(Stringtext){
leftButton.setText(text);
}

(OnClickListenerl){
leftButton.setOnClickListener(l);
}

}

TitleView中的代碼非常簡單,在TitleView的構建方法中,我們調用了LayoutInflater的inflate()方法來載入剛剛定義的title.xml布局,這部分內容我們已經在Android LayoutInflater原理分析,帶你一步步深入了解View(一)這篇文章中學習過了。

接下來調用findViewById()方法獲取到了返回按鈕的實例,然後在它的onClick事件中調用finish()方法來關閉當前的Activity,也就相當於實現返回功能了。

另外,為了讓TitleView有更強地擴展性,我們還提供了setTitleText()、setLeftButtonText()、setLeftButtonListener()等方法,分別用於設置標題欄上的文字、返回按鈕上的文字、以及返回按鈕的點擊事件。

到了這里,一個自定義的標題欄就完成了,那麼下面又到了如何引用這個自定義View的部分,其實方法基本都是相同的,在布局文件中添加如下代碼:

<RelativeLayoutxmlns: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">

<com.example.customview.TitleView
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.customview.TitleView>

</RelativeLayout>

這樣就成功將一個標題欄控制項引入到布局文件中了,運行一下程序。

現在點擊一下Back按鈕,就可以關閉當前的Activity了。如果你想要修改標題欄上顯示的內容,或者返回按鈕的默認事件,只需要在Activity中通過findViewById()方法得到TitleView的實例,然後調用setTitleText()、setLeftButtonText()、setLeftButtonListener()等方法進行設置就OK了。
閱讀全文

與androidview類源碼解析相關的資料

熱點內容
程序員可以干什麼 瀏覽:70
績效考核權重分配演算法 瀏覽:524
android應用logo 瀏覽:898
光遇安卓服墓土商店什麼時候開 瀏覽:566
月收益翻倍的源碼 瀏覽:636
asop源碼放在哪裡 瀏覽:989
電腦伺服器密碼怎麼找 瀏覽:574
jdp轉換pdf 瀏覽:749
把pdf導入iphone 瀏覽:508
米哈游租賃的雲伺服器是哪個 瀏覽:524
android直接打電話 瀏覽:1016
ubuntu停止命令 瀏覽:283
cnc攻絲編程 瀏覽:869
換個手機號碼app怎麼注冊 瀏覽:320
怎麼下載小猴口算app 瀏覽:115
輕鏈app的貨怎麼樣 瀏覽:625
電腦里的u盤如何加密 瀏覽:371
我的世界全部版本伺服器下載地址 瀏覽:50
交換原理pdf 瀏覽:230
菜鳥驛站app怎麼邀請新人 瀏覽:449