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

版权声明:本文为博主原创文章,请尊重个人劳动成果,转载注明地址:http://blog.csdn.net/evan_man 谢谢! https://blog.csdn.net/evan_man/article/details/52199517
请尊重他人劳动成果,请勿随意剽窃,转载请注明,谢谢! 转载请注明出处:http://blog.csdn.net/evan_man/article/details/52199517

    本文我们来简单的分析下Activity的SetContentView方法底层是如何对我们的layout.xml文件进行处理,然后分析一下事件是如何从WindowManagerService中传递到View的dispatchTouchEvent方法中的,最后会简单了解下PhoneWindow、DecorWindow、WindowManager等概念。
Activity中创建View的过程大体如下
  1. 调用对应Activity的调用了onCreate方法
    • 随后执行setContentView;其实就是调用的PhoneWindow.setContentView方法
    • 创建DecorView、View或ViewGroup对象
  2. 调用了onResume方法,里面有调用Activity的makeVisible方法,该方法内部
    • 获取一个WindowManager,并调用其addView方法,将视图交给WindowManagerService进行管理,后者负责显示视图和传递用户事件
      • 创建ViewRoot和W类
      • WinowManager调用WmS的远程接口完成添加一个窗口到屏幕
    • 调用前面view的setVisibility方法;该方法最终会跳转到ViewRoot的performTravels方法去;参考笔记《View—重绘》
setContentView方法内容如下
[email protected]
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方法底层
  1. 就是根据View创建ViewRoot,ViewRoot内部包含一个W类。
  2. W是一个实现了IWindow.Stub接口的ViewRoot的内部类,作用就是调用ViewRoot的同名方法,WMS与ViewRoot之间的通信明显就是通过W;
  3. 而ViewRoot向WMS之间的通信则是通过IWindowSession sWindowSession = IWindowManager.Stub.asInterface( ServiceManager.getService("window")).openSession(imm.getClient(), imm.getInputContext()); 
  4. 不管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。输入事件传递给下层的具有焦点的Window
  • FLAG_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方法。

猜你喜欢

转载自blog.csdn.net/evan_man/article/details/52199517
今日推荐