请尊重他人劳动成果,请勿随意剽窃,转载请注明,谢谢!
转载请注明出处: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