您的位置:首页 > 其它

Window和WindowManager的分析

2016-05-20 23:27 423 查看
Window表示一个窗口的概念 在日常的开发中我们接触的并不多 但是在我们需要在桌面上显示一个类似于悬浮的东西时 就需要使用Window来实现了 Window类只是一个抽象类 它的具体实现是PhoneWindow 创建一个Window是一件很简单的是 只需要由WindowManager即可完成

WindowManager通过IPC调用WindowManagerService Android中的所有视图都是通过Window来呈现的 不论是Activity Dialog Toast 它们的视图都是附加在Window上的 因此Window实际上是View的直接管理者

我们在研究View的分发机制时也了解到 事件是由Window传递给DecorView中的 然后由DecorView传递给我们的View Activity的setContentView方法 也是通过Window实现的

Window的简单使用介绍

下面我们就来在Window上简单添加一个控件:

Button bt = new Button(this);
bt.setText("哈哈哈哈");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.WRAP_CONTENT,0,0, PixelFormat.TRANSPARENT);
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.gravity = Gravity.CENTER;
params.x = 200;
params.y = 300;
wm.addView(bt,params);


上述代码就在 屏幕的200,300位置放置了一个button控件 而且是Toast类型的

下面来介绍一下Window的一些属性:

详细的介绍可以参考这篇文章 介绍的相当详细:

WindowManager.LayoutParams的各种属性

window的属性都放在WindowManager.LayoutParams中

flags :

用来表示window的显示特性 比如显示的事件的响应情况 焦点的获取情况 是否可以显示在锁屏界面上之类的

type:

用来表示Window的类型 Window有三种类型 应用Window 、子Window 、 还有系统Window

应用Window对应着一个Activity 子Window不能单独存在,必须依附于父容器 例如Dialog就是一个子Window 系统Window需要声明权限才可以 比如Toast和系统状态栏都是系统Window

Window是分层的 每个Window有对应的 z-ordered 属性 层级大的会显示在小的上面 甚至会覆盖

其中应用Window 层级在 1-99 子Window在 1000-1999

系统Window在2000-2999

需要声明权限 :

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"></uses-permission>


format:

表示期望的位图格式。。。

softInputMode:

Window的软键盘的使用模式

如果我们在Window上操控控件只需要调用它的addView removeView updateViewLayout三个方法即可 很简单

Window的内部机制

Window的操作是通过WindowManager进行的 而WindowManager的实现类WindowManagerImpl是通过 WindowManagerGlobal相应的方法执行的 这是典型的桥接模式 将所有的操作都交给WindowManagerGlobal(简称WMG)进行 WMG的addView方法主要分为以下几个步骤:

1.检查参数是否合法 如果是子Window还要调整一些布局参数

2.创建ViewRootImpl 并将View添加到WMG的列表中

//WMG中的一些列表
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();

//向列表中添加的过程
root = new ViewRootImpl(view.getContext(), display);

view.setLayoutParams(wparams);

mViews.add(view);
mRoots.add(root);
mParams.add(wparams);


3.通过ViewRootImp来更新界面完成Window的添加过程

删除的过程也类似 先findViewLocked(view,true)找到要移除的view的位置 找到后调用WMG的removeViewLocked方法 把view移除就可以 当然也是IPC调用 移除的。。 在移除的过程中还会调用我们以前提到的dispatchDetachedFromWindow接口

更新也类似就不详细解析了

Window的创建过程

1.Activity的Window创建过程

要了解这个过程就要先了解Activity的启动过程 比较复杂 最终会由ActivityThread中的performLaunchActivity()来完成整个启动过程 这个方法内部会通过类加载器创建Activity对象 并通过attach为其关联运行过程中所依赖的上下文环境

在attach方法中 系统会创建 Activity所属的Window对象 并为其设置回调接口

mWindow = new PhoneWindow(this); //创建  window
mWindow.setCallback(this);  //设置回调
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);//设置键盘模式
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}


再看Activity的setContentView方法

public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}


是通过window的这个方法进行的

再看phoneWindow的setContentView方法

这个方法大致分为这几步 :

1.如果没有DecorView 创建它

根据android.R.id.content就可以获取contentView的id 然后加载布局即可


2.将View添加到DecorView的mContentParent中

3.回调Activity的onContentChanged方法 通知 改变了 之后就完成了 Activity的Window的创建 。。。

2.Dialog的Window创建过程

和Activity的类似 分为以下几个步骤

1.创建Window


Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}

mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);

mListenersHandler = new ListenersHandler(this);
}


2.初始化DecorView并将Dialog视图添加到DecorView中
主要是调用window的setContentView方法  和Activity的过程差不多 可参考

3.将DecorView添加到WIndow中并显示


mWindowManager.addView(mDecor, l);
mShowing = true;


这样就显示了一个Dialog 所以如果要设置Dialog的布局的信息 只需要设置getWindow的一些属性就可以了。。。

上面两种WIndow的使用其实大致部分都是这个样子的 :

首先要先创建一个WIndow 也就是 new PhoneWindow()

之后给window设置一系列属性 位置 回调之类的

然后就是向window中设置布局了 也就是

mWindow.setContentView方法会执行 这个方法内部会创建并加载一个DecorView(顶级View) 之后会把xml中的布局加载到DecorView中

这时候其实window已经创建好了 它有回调 有 布局 但是还有一点 它并没有加载到屏幕上 所以这时候就需要和WindowManager联系了

使用mWindowManager.addView(mDecor,l) 函数让window显示在屏幕上即可 l参数是window的属性 就是这个参数使WM和window关联起来了 这样就完成了 一个窗口的显示
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: