Android的视图绘制与事件分发流程(底层)

    xiaoxiao2024-12-03  2

    请尊重他人劳动成果,请勿随意剽窃,转载请注明,谢谢! 转载请注明出处:http://blog.csdn.net/evan_man/article/details/52199517     本文我们来简单的分析下Activity的SetContentView方法底层是如何对我们的layout.xml文件进行处理,然后分析一下事件是如何从WindowManagerService中传递到View的dispatchTouchEvent方法中的,最后会简单了解下PhoneWindow、DecorWindow、WindowManager等概念。 Activity中创建View的过程大体如下 调用对应Activity的调用了onCreate方法 随后执行setContentView;其实就是调用的PhoneWindow.setContentView方法创建DecorView、View或ViewGroup对象 调用了onResume方法,里面有调用Activity的makeVisible方法,该方法内部 获取一个WindowManager,并调用其addView方法,将视图交给WindowManagerService进行管理,后者负责显示视图和传递用户事件 创建ViewRoot和W类WinowManager调用WmS的远程接口完成添加一个窗口到屏幕 调用前面view的setVisibility方法;该方法最终会跳转到ViewRoot的performTravels方法去;参考笔记《View—重绘》 setContentView方法内容如下 setContentView@Activity.class public void setContentView(@LayoutRes int layoutResID) { mWindow.setContentView(layoutResID); initWindowDecorActionBar(); //对ActionBar进行一些初始化 } private Window mWindow = new PhoneWindow(this); 如果要分析绘制的底层细节从下面的几个方法开始。     1、com.android.internal.policy.impl.PhoneWindow 的setContentView以及getDecorView两个方法,     2、 android.view.WindowManager 的addView方法进行探究。     3、 ViewRoot的创建、显示、事件分发

    视图绘制

    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通信机制实现的。 View是Android视图的呈现方式,但是View不能单独存在,它必须依附于Window这个抽象的概念上。一个PhoneWindow对应一个View(decorView),;一个view对应一个ViewRoot和一个WindowManager.LayoutParam。 ViewRoot用于对view进行视图绘制的控制和事件的处理,WindowManager.LayoutParams用于告诉WMS当前Window在屏幕什么位置进行显示,以何种方式显示!其中WindowManager.LayoutParams的flags和type参数比较重要,下面我们就对其进行解释说明。 WindowManager.LayoutParams.class 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。输入事件传递给下层的具有焦点的WindowFLAG_NOT_TOUCH_MODAL:当前Window区域以外的单击事件传递给底层的Window,当前Window区域内的单击事件则自己处理。一般都需要开启这个标记,否则下面的Window将无法获取到事件。FLAG_SHOW_WHEN_LOCKED:让该Window可以显示在锁屏的界面上面 Type参数表示Window的类型,Window有三种类型: 应用Window:即我们普通的Activity对应的View子Window:需要依赖于一个父Window中,不能单独存在,如dialog系统Window:需要声明权限才能创建的Window,比如Toast和系统状态栏 Window是分层的,对应的z-ordered越大越靠前显示,层级大的会覆盖层级小的Window。  具体如下  //应用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方法 @Window.class public void setCallback(Callback callback) { mCallback = callback; } @Window.class public final Callback More ...getCallback() { return mCallback; } disp atchTouchEvent()@Activity.Class 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方法 uperDispatchTouch Event()@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方法。
    转载请注明原文地址: https://ju.6miu.com/read-1294214.html
    最新回复(0)