android 窗口类型分析
来源:互联网 发布:淘宝免费申请试用理由 编辑:程序博客网 时间:2024/05/22 12:32
1, 概述
Android窗口类型主要分成了三大类:
1,应用程序窗口。一般应用程序的窗口,比如我们应用程序的Activity的窗口。
2,子窗口。一般在Activity里面的窗口,比如各种菜单等。
3,系统窗口。系统的窗口,比如输入法,Toast,墙纸等。
WindowManager里面窗口的type类型值定义是一个递增保留的连续增大数值。其实就是窗口的Z-ORDER序列(值越大显示的位置越在上面,需要将屏幕想成三维坐标模式)。
2 WindowManager/LayoutParams
2.1 窗口类型
WindowManager(窗口管理)是如何管理Window呢?
WindowManager是一个接口,首先看看里面的关键定义,
窗口类型,
public int type;
应用程序窗口
FIRST_APPLICATION_WINDOW = 1
第一个应用窗口
TYPE_APPLICATION = 2
应用的默认窗口
LAST_APPLICATION_WINDOW = 99
最后的应用窗口
所有acitivty的窗口的值都在[1,99],默认值是TYPE_APPLICATION,WMS在进行窗口叠加时,会动态的改变activity的值。
子窗口
子窗口的Z序和坐标空间都依赖于Activity窗口
所有子窗口的值都在[1000,1999], WMS在进行窗口叠加时,会动态调整子窗口的值。
系统窗口
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
Toast
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
系统错误窗口
TYPE_INPUT_METHOD= FIRST_SYSTEM_WINDOW+11
输入法窗口
系统窗口远不止上面的表格中所展现的,一共定义了33种。
系统窗口的创建一般不依赖于Activity窗口
所有系统窗口的值都在[2000,2999], WMS在进行窗口叠加时,会动态调整子窗口的值。有些系统窗口只能出现一个,不能添加多个,否则用户会觉得很乱(体验差)。比如输入法窗口,系统状态条窗口等。
2.2 窗口内存缓存类型
public int memoryType; // 保存窗口内存缓存类型
窗口内存缓存一共有4种,
public static final int MEMORY_TYPE_NORMAL = 0; //窗口缓冲位于主内存public static final int MEMORY_TYPE_HARDWARE=1;//窗口缓冲位于可以被DMA访问,或者硬件加速的内存区域public static final int MEMORY_TYPE_GPU = 2; //窗口缓冲位于可被图形加速器访问的区域public static final int MEMORY_TYPE_PUSH_BUFFERS = 3; //窗口缓冲不拥有自己的缓冲区,不能被锁定,缓冲区由本地方法提供
2.3 窗口行为类型
public int flags; //保存窗口的行为
窗口行为类型的行为一共32种,数值都是2的n次幂,所以各种flags可以混合使用,直接按位或是最好的方法。
比如最开始四种行为定义如下,
//Flag:当该window对用户可见的时候,允许锁屏 public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001; //Flag:让该window后所有的东西都成暗淡 public static final int FLAG_DIM_BEHIND = 0x00000002; //Flag:让该window后所有东西都模糊(4.0以上已经放弃这种毛玻璃效果) public static final int FLAG_BLUR_BEHIND = 0x00000004; //Flag:让window不能获得焦点,这样用户快就不能向该window发送按键事 public static final int FLAG_NOT_FOCUSABLE = 0x00000008;2.4 硬件加速类型
public int privateFlags; // 保存硬件加速行为一共有11种。定义如下,
public static final int PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED = 0x00000001;public static final int PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED = 0x00000002;public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004;2.5 窗口输入键盘类型
public int softInputMode;一共定义了13种键盘在窗口中的显示方法。
public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;public static final int SOFT_INPUT_STATE_UNCHANGED = 1;当然, WindowManager的一些其他变量也控制着窗口的显示形式。
3 应用窗口
3.1 一般应用窗口
ActivityThread的主线程ApplicationThread的performLaunchActivity方法会调用Activity的onCreate方法,在onCreate中会调用setContentView等方法加载解析xml资源。从Toast的最简单调用开始,它的调用代码是:void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); // 获取的是WindowManagerImpl对象 wm.addView(mDecor, getWindow().getAttributes()); // PhoneWindow mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }getAttributes最后获取的默认type类型是TYPE_APPLICATION。
3.2 Dialog
Dialog是一系列XXXDialog的基类,我们可以new任意Dialog或者通过Activity提供的 onCreateDialog(……)、onPrepareDialog(……)和showDialog(……)等方法来管理我们的Dialog,但是究 其实质都是来源于Dialog基类,所以我们对于各种XXXDialog来说只用分析Dialog的窗口加载就可以了。
Dialog构造方法如下,
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; //mContext已经从外部传入的context对象获得值(一般是个Activity)!!!非常重要,先记住!!! } mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final Window w = new PhoneWindow(mContext); // 创建新的Window mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this); }看看show方法,
public void show() { if (mShowing) { if (mDecor != null) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return; } mCanceled = false; if (!mCreated) { dispatchOnCreate(null); } onStart(); mDecor = mWindow.getDecorView(); if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { final ApplicationInfo info = mContext.getApplicationInfo(); mWindow.setDefaultIcon(info.icon); mWindow.setDefaultLogo(info.logo); mActionBar = new WindowDecorActionBar(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); //和上面分析的Activity一样type为TYPE_APPLICATION if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); nl.copyFrom(l); nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; l = nl; } try { mWindowManager.addView(mDecor, l);//把一个View添加到Activity共用的windowManager里面去 mShowing = true; sendShowMessage(); } finally { } }可以看见Dialog的新Window与Activity的Window的type同样都为TYPE_APPLICATION,上面介绍 WindowManager.LayoutParams时TYPE_APPLICATION的注释明确说过,普通应用程序窗口 TYPE_APPLICATION的token必须设置为Activity的token来指定窗口属于谁。所以可以看见,既然Dialog和 Activity共享同一个WindowManager(也就是上面分析的WindowManagerImpl),而WindowManagerImpl 里面有个Window类型的mParentWindow变量,这个变量在Activity的attach中创建WindowManagerImpl时传入 的为当前Activity的Window,而当前Activity的Window里面的mAppToken值又为当前Activity的token,所以 Activity与Dialog共享了同一个mAppToken值,只是Dialog和Activity的Window(PhoneWindow)对象不同。
为什么一定需要Activity的token 呢,禁止在当前activity中弹出另外一个activity的dialog,因为dialog有时候需要交互的,不能像toast一样(toast无交互).
所以传进去的context对象一定是Activity,如果是其他组件(Service)的context会报错。
4 子窗口
PopWindow是android系统中一个典型的子窗口,它的创建也依赖于Activity。
例如各种菜单(PopupMenu)都是利用PopWindow来实现的。
PopupWindow与Dialog一个不同点是PopupWindow是一个阻塞的对话框,如果你直接在Activity的onCreate等方法中显示它则会报错,所以PopupWindow最好另开启一个新线程去调用。
PopupWindow一个构造函数如下,
public PopupWindow(View contentView, int width, int height, boolean focusable) { if (contentView != null) { mContext = contentView.getContext();//最终这个mContext实质是Activity mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } // WindowManager setContentView(contentView); // 初始化赋值 setWidth(width); setHeight(height); setFocusable(focusable); }显示时,最后都会调用showAsDropDown方法,
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) { if (isShowing() || mContentView == null) { return; } TransitionManager.endTransitions(mDecorView); registerForScrollChanged(anchor, xoff, yoff, gravity); mIsShowing = true; mIsDropdown = true; final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken()); // preparePopup(p); final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, gravity); updateAboveAnchor(aboveAnchor); invokePopup(p); }createPopupLayoutParams方法会新建一个WindowManager.LayoutParams对象,然后变量type赋值为mWindowLayoutType,
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;private void invokePopup(WindowManager.LayoutParams p) { if (mContext != null) { p.packageName = mContext.getPackageName(); } final PopupDecorView decorView = mDecorView; decorView.setFitsSystemWindows(mLayoutInsetDecor); setLayoutDirectionFromAnchor(); mWindowManager.addView(decorView, p); if (mEnterTransition != null) { decorView.requestEnterTransition(mEnterTransition); } }PopWindow中没有像Activity及Dialog一样new新的Window, 完全使用了Activity的Window与WindowManager。
5 系统窗口
系统窗口的种类很多,以Toast为例来论述,
首先看Toast的构造方法,
public Toast(Context context) { mContext = context; mTN = new TN(); mTN.mY = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.toast_y_offset); mTN.mGravity = context.getResources().getInteger( com.android.internal.R.integer.config_toastDefaultGravity); }内部类TN构造方法如下,
TN() { // XXX This should be changed to use a Dialog, with a Theme.Toast // defined that sets up the layout params appropriately. final WindowManager.LayoutParams params = mParams; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.format = PixelFormat.TRANSLUCENT; params.windowAnimations = com.android.internal.R.style.Animation_Toast; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.setTitle("Toast"); params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; }主要是为WindowManager.LayoutParams中的变量赋值, type赋值为
params.type = WindowManager.LayoutParams.TYPE_TOAST;显示时,调用TN的handleShow方法,如下,
public void handleShow() { if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView + " mNextView=" + mNextView); if (mView != mNextView) { // remove the old view if necessary handleHide(); mView = mNextView; Context context = mView.getContext().getApplicationContext(); String packageName = mView.getContext().getOpPackageName(); if (context == null) { context = mView.getContext(); } mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); // We can resolve the Gravity here by using the Locale for getting // the layout direction final Configuration config = mView.getContext().getResources().getConfiguration(); final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDirection()); mParams.gravity = gravity; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; } if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { mParams.verticalWeight = 1.0f; } mParams.x = mX; mParams.y = mY; mParams.verticalMargin = mVerticalMargin; mParams.horizontalMargin = mHorizontalMargin; mParams.packageName = packageName; if (mView.getParent() != null) { if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this); mWM.addView(mView, mParams); trySendAccessibilityEvent(); } }和前2种窗口内型不同的是,系统窗口不依赖于Activity而存在,并且由系统服务统一管理,只是一个通知提示作用,并不会和Activity进行交互。
在使用Toast时context参数尽量使用getApplicationContext(),可以有效的防止静态引用导致的内存泄漏。
6 addView方法
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);通过这种方法获取的服务都是WindowManagerImpl对象,其addView方法如下,
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { android.util.SeempLog.record_vg_layout(383,params); applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }直接调用WindowManagerGlobal的
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { if (view == null) { throw new IllegalArgumentException("view must not be null"); } if (display == null) { throw new IllegalArgumentException("display must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { throw new IllegalArgumentException("Params must be WindowManager.LayoutParams"); } final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params; if (parentWindow != null) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } else { // If there's no parent, then hardware acceleration for this view is // set from the application's hardware acceleration setting. final Context context = view.getContext(); if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } ViewRootImpl root; View panelParentView = null; synchronized (mLock) { // Start watching for system property changes. if (mSystemPropertyUpdater == null) { mSystemPropertyUpdater = new Runnable() { @Override public void run() { synchronized (mLock) { for (int i = mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); } } } }; SystemProperties.addChangeCallback(mSystemPropertyUpdater); } int index = findViewLocked(view, false); if (index >= 0) { if (mDyingViews.contains(view)) { // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { throw new IllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. } // If this is a panel window, then find the window it is being // attached to for future reference. if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } } root = new ViewRootImpl(view.getContext(), display); // 初始化 view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); } // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } }最后的setView方法会调用performTraversals方法完成View的测量,确定位置以及显示,这样一个窗口就显示出来了。
- android 窗口类型分析
- Android窗口类型
- Android窗口类型有关
- android 窗口类型介绍
- Android WindowManager窗口类型
- Android 窗口类型
- Android入门之窗口类型
- 【Android】浮动窗口层级分析
- android的窗口机制分析------ViewRoot类
- android的窗口机制分析------事件处理
- Android的窗口机制分析-事件处理
- android的窗口时机制分析
- android的窗口机制分析------事件处理
- android的窗口机制分析------事件处理
- android的窗口机制分析------事件处理
- Android的窗口机制分析-事件处理
- android的窗口机制分析------事件处理
- android的窗口机制分析------事件处理
- Android之---Activity的四种启动模式
- IBM V3500存储更换控制器一例
- 重拾java基础(二十二):集合、IO流总结附带Excel资料
- 求第n个斐波那契数(非递归与递归方法实现)
- 关于SSH中异常处理的冲突引起的无限循环-Infinite recursion detected: [……]-问题处理
- android 窗口类型分析
- ROS实战_1.1 Pioneer-3DX 移动机器人平台介绍与学习指南
- SVG(可缩放矢量图形)虚线相关属性与线条动画原理:一条会动的线
- 浅谈MDN CSS
- 整体二分 【Poi2011】 Meteors bzoj2527
- 看 刻意练习 有感
- 默认样式重置标准版
- Tinker接入及原理分析
- 语义网概念