Android的檢視繪製與事件分發流程(底層)
- 呼叫對應Activity的呼叫了onCreate方法
- 隨後執行setContentView;其實就是呼叫的PhoneWindow.setContentView方法
- 建立DecorView、View或ViewGroup物件
- 呼叫了onResume方法,裡面有呼叫Activity的makeVisible方法,該方法內部
- 獲取一個WindowManager,並呼叫其addView方法,將檢視交給WindowManagerService進行管理,後者負責顯示檢視和傳遞使用者事件
- 建立ViewRoot和W類
- WinowManager呼叫WmS的遠端介面完成新增一個視窗到螢幕
- 呼叫前面view的setVisibility方法;該方法最終會跳轉到ViewRoot的performTravels方法去;參考筆記《View—重繪》
- 獲取一個WindowManager,並呼叫其addView方法,將檢視交給WindowManagerService進行管理,後者負責顯示檢視和傳遞使用者事件
如果要分析繪製的底層細節從下面的幾個方法開始。 3、ViewRoot的建立、顯示、事件分發[email protected] public void setContentView(@LayoutRes int layoutResID) { mWindow.setContentView(layoutResID); initWindowDecorActionBar(); //對ActionBar進行一些初始化 } private Window mWindow = new PhoneWindow(this);
檢視繪製
PhoneWindow.class (com.android.internal.policy.impl.PhoneWindow)
public class PhoneWindow extends Window private DecorView mDecor; //DecorView是PhoneWindow中的內部類private final class DecorView extends FrameLayout private ViewGroup mContentParent; private LayoutInflater mLayoutInflater; public PhoneWindow(Context context) { super(context); mLayoutInflater = LayoutInflater.from(context); }
setContentView(int )@PhoneWindow.class
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent); //note1
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
1、解析xml檔案同時制定parent為mContentParent
setContentView(View )@PhoneWindow.class@Override
public void setContentView(View view) {
setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor(); //note1
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
mContentParent.addView(view, params); //noe1
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
1、建立DecorWindow 和 ContentParent
2、contentView新增到mContentParent中
installDecor()@PhoneWindow.class
private void installDecor() {
if (mDecor == null) { //建立DecorWindow
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) { //如果mContentParent不為空則直接返回該方法下面程式碼就不會被執行
mContentParent = generateLayout(mDecor); //獲取DecorWindow下的使用者自定義檢視佈局應該所屬的parent ViewGroup
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
.........
}
}
generateDecor()@PhoneWindow.class
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
generateLayout()@PhoneWindow.class
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
....//這裡會判斷DecorWindow是全屏顯示還是WrapContent
int layoutResource;
int features = getLocalFeatures();
....//根據feature採用不同的佈局
View in = mLayoutInflater.inflate(layoutResource, null); //這裡載入的是整個手機螢幕即將顯示的View,包括狀態列、ActionBar、使用者自定義View等
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //note1
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
......
return contentParent;
}
1、
等價於ViewGroup contentParent = (ViewGroup)decor.findViewById(ID_ANDROID_CONTENT);public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content。等價於mDecor.findViewById(com.android.internal.R.id.content)。獲取的是使用者自定義View的佈局檔案所屬的父佈局。
getDecorView()@PhoneWindow.class@Override
public final View getDecorView() {
if (mDecor == null) {
installDecor();
}
return mDecor;
}
WindowManager.class
WindowManager常用的自有三個方法addView、updateView、removeView。 WindowManager的addView方法底層- 就是根據View建立ViewRoot,ViewRoot內部包含一個W類。
- W是一個實現了IWindow.Stub介面的ViewRoot的內部類,作用就是呼叫ViewRoot的同名方法,WMS與ViewRoot之間的通訊明顯就是通過W;
- 而ViewRoot向WMS之間的通訊則是通過IWindowSession sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")).openSession(imm.getClient(), imm.getInputContext());
- 不管W還是IWindowSession,他們底層都是通過Binder通訊機制實現的。
WindowManager.LayoutParams extends ViewGroup.LayoutParams implements Parcelable{
public int x;
//視窗的X座標,但是並不一定是絕對座標;如果對應的gravity是LEFT、RIGHT等則表示x偏移值
public int y;
//視窗的Y座標,但是並不一定是絕對座標;如果對應的gravity是BOTTOM、TOP等則表示y偏移值
public int flags;
public int type;
.....
}
Flags引數表示Window的屬性,通過修改Flags引數的值控制Window的顯示特性,比如下面幾個常用的選項
- FLAG_NOT_FOCUSABLE:表明該Window不接收輸入事件,此標記會同時啟動FLAG_NOT_TOUCH_MODAL。輸入事件傳遞給下層的具有焦點的Window
- FLAG_NOT_TOUCH_MODAL:當前Window區域以外的單擊事件傳遞給底層的Window,當前Window區域內的單擊事件則自己處理。一般都需要開啟這個標記,否則下面的Window將無法獲取到事件。
- FLAG_SHOW_WHEN_LOCKED:讓該Window可以顯示在鎖屏的介面上面
- 應用Window:即我們普通的Activity對應的View
- 子Window:需要依賴於一個父Window中,不能單獨存在,如dialog
- 系統Window:需要宣告許可權才能建立的Window,比如Toast和系統狀態列
//應用Window 1~99
FIRST_APPLICATION_WINDOW = 1;
TYPE_BASE_APPLICATION = 1;
TYPE_APPLICATION = 2;
TYPE_APPLICATION_STARTING = 3;
LAST_APPLICATION_WINDOW = 99;
//子Window 1000~1999
FIRST_SUB_WINDOW = 1000;
TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
LAST_SUB_WINDOW = 1999;
//系統Window 2000~2999
FIRST_SYSTEM_WINDOW = 2000;
TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;
TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;
TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;
TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;
TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;
TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;
TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;
TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;
TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;
TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;
TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10; //對應AndroidManifest要宣告<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;
TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;
TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;
TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;
TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
TYPE_KEYGUARD_SCRIM = FIRST_SYSTEM_WINDOW+29;
TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
LAST_SYSTEM_WINDOW = 2999;
事件分發
WindowManager的addView方法底層就是建立ViewRoot和W類。1、W是一個實現了IWindow.Stub介面的ViewRoot的內部類,作用就是呼叫ViewRoot的同名方法,WMS與ViewRoot之間的通訊明顯就是通過W; 2、而ViewRoot向WMS之間的通訊則是通過IWindowSession sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")).openSession(imm.getClient(), imm.getInputContext()); 3、不管W還是IWindowSession,他們底層都是通過Binder通訊機制實現的。addView的引數是mDecorWindow,所以ViewRoot的建立是基於這個mDecorView,對應ViewRoot的mView域(ViewRoot只有一個子View)。因此mDecorWindow是第一個接收到使用者點選事件的View。下面我們對整個流程進行一下分析。 首先從ViewRoot的handlerMessage方法看起。 handlerMessage()@ViewRoot.class
public void handleMessage(Message msg) {
case DISPATCH_POINTER: {
MotionEvent event = (MotionEvent) msg.obj;
try {
deliverPointerEvent(event);
} finally {
event.recycle();
if (msg.arg1 != 0) {
finishInputEvent();
}
if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
}
} break;
}
DISPATCH_POINTER事件對應使用者的點選事件。呼叫ViewRoot的deliverPointerEvent方法。
deliverPointerEvent()@ViewRoot.class
private void deliverPointerEvent(MotionEvent event){
....
handled = mView.dispatchTouchEvent(event);
....
}
ViewRoot的deliverPointerEvent方法會呼叫mView的dispatchTouchEvent方法
dispatchTouchEvent()@DecorWindow.class
public boolean dispatchTouchEvent(MotionEvent ev) {
final Callback cb = getCallback(); //note1
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) //note2
: super.dispatchTouchEvent(ev);
}
1、呼叫PhoneWindow的getCallback方法獲取Activity在Attach方法中傳入的Callback物件,Activity實現了這個介面2、cb.dispatchTouchEvent(ev) 就是呼叫Activity的dispatchTouchEvent方法
dispatchTouchEvent()@Activity.Class@Window.class public void setCallback(Callback callback) { mCallback = callback; } @Window.class public final Callback More ...getCallback() { return mCallback; }
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) { //note1
return true;
}
return onTouchEvent(ev); //note2
}
1、呼叫PhoneWindow的superDispatchTouchEvent()方法
2、呼叫自己的onTouchEvent方法
uperDispatchTouchEvent()@PhoneWindow.class
public boolean uperDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
//呼叫mDecorDispatchTouchEvent方法
uperDispatchTouchEvent()@DecorWindow.class
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
//呼叫父類FrameLayout的dispatchTouchEvent方法
通過上面的簡單分析可以知道如下結論:首先ViewRoot接收到了來自WMS的事件(通過W傳送,W轉換事件為非同步事件),隨後ViewRoot在的Handler中處理事件。對於使用者的觸屏資訊等事件,往往大多數是交給其直接子view,即DecorWindow物件。以DecorWindow的dispatchTouchEvent方法為例,該方法內部會呼叫getCallback的dispatchTouchEvent方法,即對應DecorWindow所繫結的Activity的dispatchTouchEvent方法。Activity的dispatchTouchEvent方法內部先呼叫PhoneWindow的superDispatchTouchEvent方法(方法內部呼叫DecorWindow的superDispatchTouchEvent方法,進而呼叫DecorWindow的父類FrameLayout的dispatchTouchEvent方法,往下就是一個普通的View的dispatchTouchEvent事件分發過程),如果事件沒有被DecorWindow下面的View處理則最後呼叫自身的onTouchEvent方法。
相關推薦
Android的檢視繪製與事件分發流程(底層)
本文我們來簡單的分析下Activity的SetContentView方法底層是如何對我們的layout.xml檔案進行處理,然後分析一下事件是如何從WindowManagerService中傳遞到View的dispatchTouchEvent方法中的,最後會
Android之Input子系統事件分發流程
Android建立視窗機制,請看如下轉載: 一、Android4.2系統服務側——與View關係 1.服務端channel註冊過程 frameworks/base/core/java/android/view/ViewRootImpl.java public void s
SimpleTouch:以稍微(zui)簡單的方式解決Android事件分發流程
該庫已經開源到github,地址github.com/AlexMahao/S… 目標 一個用於監聽android事件分發流程的庫,兩行程式碼即可在執行時期監聽事件的分發流程。 在編寫一些複雜的佈局時,常常由於事件分發到底是哪個view處理產生困擾,做法通常需要經過以
Android檢視繪製流程完全解析,帶你一步步深入瞭解View(二)
在上一篇文章中,我帶著大家一起剖析了一下LayoutInflater的工作原理,可以算是對View進行深入瞭解的第一步吧。那麼本篇文章中,我們將繼續對View進行深入探究,看一看它的繪製流程到底是什麼樣的。如果你還沒有看過我的上一篇文章,可以先去閱讀 Android Layo
Android原始碼解析(三十)-->觸控事件分發流程
前面一篇文章中我們分析了App返回按鍵的分發流程,從Native層到ViewRootImpl層到DocorView層到Activity層,以及在Activity中的dispatchKeyEvent方法中分發事件,最終呼叫了Activity的finish方法,
觸控事件/事件分發流程/滑動衝突處理
目錄 1.觸控事件MotionEvent: 1)主要對應3操作 2)MotionEvent內部主要方法: 2.事件傳遞 1)預設情況demo:
Android熱插拔事件處理流程--Vold
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Nginx 多程序連線請求/事件分發流程分析
https://www.cnblogs.com/NerdWill/p/4992345.html Nginx使用多程序的方法進行任務處理,每個worker程序只有一個執行緒,單執行緒迴圈處理全部監聽的事件。本文重點分析一下多程序間的負載均衡問題以及Nginx多程序事件處理流程,方便大家自己寫程式的
帶你從原始碼角度分析ViewGroup中事件分發流程
序言 這篇博文不是對事件分發機制全面的介紹,只是從原始碼的角度分析ACTION_DOWN、ACTION_MOVE、ACTION_UP事件在ViewGroup中的分發邏輯,瞭解各個事件在ViewGroup的分發邏輯對理解、解決滑動衝突問題很有幫助。 ViewGroup中事件分發流
Android檢視狀態及重繪流程分析,帶你一步步深入瞭解View(三)
在前面一篇文章中,我帶著大家一起從原始碼的層面上分析了檢視的繪製流程,瞭解了檢視繪製流程中onMeasure、onLayout、onDraw這三個最重要步驟的工作原理,那麼今天我們將繼續對View進行深入探究,學習一下檢視狀態以及重繪方面的知識。如果你還沒有看過我前面一篇文章
Android面試準備:事件分發機制
View的事件分發機制舉例 為按鈕設定onClick點選事件和onTouch觸控事件的執行順序為: 1、onClick事件: button.setOnClickListener(new OnClickListener() { @Over
Android開發中的事件分發和消費機制的一些理解
說明事件分發被用作解決事件衝突,還被用作自定義View事件包含的動作 1.ACTION_DOWN(按下):手指只要一觸控式螢幕幕就立即觸發這個動作 2.ACTION_MOVE(移動):手指觸控式螢幕幕
Android Activity的按鍵事件處理流程
Android裡,Activity按鍵事件相關的分發/處理函式有如下幾個: 1) public boolean dispatchKeyEvent(KeyEvent event); 2)public boolean onKeyDown
非同步通訊與事件分發框架
本文的潛在讀者是五年以下經驗的程式設計師,五年以上的資深工程師請繞道。 如果讀者有需求,本文回覆超過十個類似需求,我會整理一套開源的開發框架,含python/c++的socket伺服器端/客戶端程式碼,Java/oc的socket客戶端外加事件分發以及sample。這樣的框
Android軟解碼與硬解碼流程概述
最近整理了下Android端專案裡硬解碼與軟解碼的大致流程,做個筆記,方便以後review。如有錯誤,歡迎指正。硬解碼:1.建立MediaCodec:如 MediaCodec.createDecoderByType(keyMime);2.configure codec:配置format,surface等引數3
Android --- 檢視繪製監聽
// 從檢視上獲取檢視樹觀察者 ViewTreeObserver vo = mainView.getViewTreeObserver(); // 對檢視監聽即將繪製 vo.addOnPreDrawListener(new ViewTreeObserver.OnP
Android中ViewGroup的事件分發結論記錄
開發十年,就只剩下這套架構體系了! >>>
Android的事件分發(dispatchTouchEvent),攔截(onInterceptTouchEvent)與處理(onTouchEvent)
在Android中,View的結構是樹狀的,所以,當觸發觸控事件的時候,其事件傳遞也是從上之下一層層的傳遞。下面我們結合例子來一點點進行分析。 首先,我們需要了解事件處理中的幾個方法: 1、在ViewGroup中,事件分為dispatchTouchEvent(事件的分發)
android面試題之觸控事件分發與處理簡述
android觸控事件分發與處理 android的分發機制:由父控制元件判斷是否攔截,如果不攔截事件,則繼續分發到子控制元件,然後一直分發下去。 處理:與分發相反,由子控制元件先處理事件,如果子控制元件不處理,則交給父控制元件處理,一直向上傳遞,直到那個控制
Android事件分發底層原理
1.簡介 1.1事件構成 在Android中,事件(TouchEvent)主要包括點按、長按、拖拽、滑動等,所有的事件都由如下三個部分組成 按下(ACTION_DOWN) 移動(ACTION_MOVE) 擡起(ACTION_UP) 一般來說,一次