文章目录
WindowManager(窗口管理服务),它是显示View的最底层,Toast,Activity,Dialog的底层都有用到了这个WindowManager。WindowManager里面主要是addView,removeView,updateViewLayout这几个方法来显示View,以及通过WindowManager.LayoutParams这个API来设置相关的属性。下文讲详细介绍。
WindowsManager
1、WindowsManager使用方法
1
//获取WindowManager对象
WindowManager wManager = getApplicationContext().getSystemService(Context.WINDOW_ SERVICE);
//获得WindowManager.LayoutParams对象,为后续操作作准备
WindowManager.LayoutParams wmParams=new WindowManager.LayoutParams();
//...WindowManager.LayoutParams一顿设置
wManager.addView(textView, wmParams);
2
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button floatingButton = new Button(this);
floatingButton.setText("button");
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
0, 0,
PixelFormat.TRANSPARENT
);
// flag 设置 Window 属性
layoutParams.flags= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
// type 设置 Window 类别(层级)
layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
layoutParams.gravity = Gravity.CENTER;
WindowManager windowManager = getWindowManager();
windowManager.addView(floatingButton, layoutParams);
}
}
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
3全屏设置
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//去除标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
//去除状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);//要放到加载布局文件代码之前
initView();
initData();
initListener();
}
2、WindowManager的关联类
2.1、 WindowManager
先来看看WindowManager里面有什么。
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManager.java
public interface WindowManager extends ViewManager {
int DOCKED_INVALID = -1;
int DOCKED_LEFT = 1;
int DOCKED_TOP = 2;
int DOCKED_RIGHT = 3;
int DOCKED_BOTTOM = 4;
final static String INPUT_CONSUMER_PIP = "pip_input_consumer";
final static String INPUT_CONSUMER_NAVIGATION = "nav_input_consumer";
final static String INPUT_CONSUMER_WALLPAPER = "wallpaper_input_consumer";
public static class BadTokenException extends RuntimeException {
public BadTokenException() {}
public BadTokenException(String name) { super(name);}
}
public static class InvalidDisplayException extends RuntimeException {
public InvalidDisplayException() {}
public InvalidDisplayException(String name) { super(name);}
}
public Display getDefaultDisplay();
public void removeViewImmediate(View view);
public interface KeyboardShortcutsReceiver {
void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result);
}
final int TAKE_SCREENSHOT_FULLSCREEN = 1;
final int TAKE_SCREENSHOT_SELECTED_REGION = 2;
public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array";
public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId);
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
......
}
......
}
WindowManager是一个接口,继承ViewManager。里面有一些实例属性和方法,还有一个LayoutParams占据着很大一部分。LayoutParams是一个序列化类。而且WindowManager里面的大部分都是隐藏的属性和方法,只给系统调用。
2.2、ViewManager
WindowManager继承ViewManager。ViewManager里面实际上只有三个方法:addView
、updateViewLayout
、removeView
,分别对应着添加view、更新viewlayout、移走view。
\frameworks\base\core\java\android\view\ViewManager.java
package android.view;
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
2.3、WindowManagerImpl
那么WindowManager的实现类是哪个?是WindowManagerImpl。
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Context context) {
this(context, null);
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
return new WindowManagerImpl(displayContext, mParentWindow);
}
public void setDefaultToken(IBinder token) {
mDefaultToken = token;
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
// Only use the default token if we don't have a parent window.
if (mDefaultToken != null && mParentWindow == null) {
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
// Only use the default token if we don't already have a token.
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (wparams.token == null) {
wparams.token = mDefaultToken;
}
}
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
@Override
public void requestAppKeyboardShortcuts(
final KeyboardShortcutsReceiver receiver, int deviceId) {
IResultReceiver resultReceiver = new IResultReceiver.Stub() {
@Override
public void send(int resultCode, Bundle resultData) throws RemoteException {
List<KeyboardShortcutGroup> result =
resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY);
receiver.onKeyboardShortcutsReceived(result);
}
};
try {
WindowManagerGlobal.getWindowManagerService()
.requestAppKeyboardShortcuts(resultReceiver, deviceId);
} catch (RemoteException e) {
}
}
@Override
public Display getDefaultDisplay() {
return mContext.getDisplay();
}
@Override
public Region getCurrentImeTouchRegion() {
try {
return WindowManagerGlobal.getWindowManagerService().getCurrentImeTouchRegion();
} catch (RemoteException e) {
}
return null;
}
}
可以看到实现类里面主要的参数是下面这几个。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
里面最主要的是WindowManagerGlobal了,view的增删改都是由它完成的。
WindowManagerGlobal里面主要管理是一下一些参数。view的相关控制用ArrayList装载着。
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java
private static WindowManagerGlobal sDefaultWindowManager;
private static IWindowManager sWindowManagerService;
private static IWindowSession sWindowSession;
private final Object mLock = new Object();
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>();
private Runnable mSystemPropertyUpdater;
addView
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java
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.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
updateViewLayout
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
removeView
Android_8_0_r36_\frameworks\base\core\java\android\view\WindowManagerGlobal.java
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
3、WindowManager 的一些属性
WindowManager.LayoutParams属性里面主要有Type属性和Flags属性。
Type表示Window的类型,Window有三种类型,分别是应用窗口、子窗口和系统窗口。
\frameworks\base\core\java\android\view\ViewManager.java
/**
* Start of window types that represent normal application windows.
*/
public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int TYPE_BASE_APPLICATION = 1;//一个应用程序窗口,作为“基础”窗口
public static final int TYPE_APPLICATION = 2;//正常的应用程序窗口
public static final int TYPE_APPLICATION_STARTING = 3;//特殊应用程序窗口显示
public static final int TYPE_DRAWN_APPLICATION = 4;//显示前绘画
public static final int LAST_APPLICATION_WINDOW = 99;//End of types of application windows.
//子窗口
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;//应用面板
public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;//窗口显示媒体(如视频)
public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;//应用程序窗口的顶部
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;//类似TYPE_APPLICATION_PANEL,布局为顶级窗口
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;//显示媒体层叠窗口
public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;//上面一个sub-panel之上的应用程序的窗口
public static final int LAST_SUB_WINDOW = 1999;//子系统结尾
/**
* Start of system-specific window types.
*/
public static final int FIRST_SYSTEM_WINDOW = 2000;//These are not normally created by applications.
public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//状态栏。只能有一个状态栏
public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//搜索栏。只能有一个搜索栏
public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//电话
public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//系统窗口,如低功率警觉
public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//键盘守卫窗口
public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//临时通知。
public static final int TYPE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+6;//系统覆盖窗口,它需要显示出来
public static final int TYPE_PRIORITY_PHONE = FIRST_SYSTEM_WINDOW+7;//优先电话界面,需要即使显示
public static final int TYPE_SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW+8;//从状态栏面板幻灯片
public static final int TYPE_KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW+9;//键盘守卫显示对话
public static final int TYPE_SYSTEM_ERROR = FIRST_SYSTEM_WINDOW+10;//内部系统错误窗口,出现在上面
public static final int TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11;//内部输入法窗口,上面出现
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;//内部输入方法对话框窗口,上面出现
public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;//壁纸窗口,放在任何想要的窗口
public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14;//面板幻灯片从状态栏
public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;//安全系统覆盖窗口
public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16;//拖动图层,放在最上面
public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;//从状态栏下面板,幻灯片
public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;//(鼠标)指针
public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;//导航栏(有别于状态栏时)
public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;//音量级别覆盖/对话框显示
public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;//引导进度对话框,在所有窗口之上
public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;//当系统UI隐藏时候输入事件
public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;//屏保窗口
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;//导航栏面板
public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;//显示窗口覆盖
public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;//放大窗口覆盖
public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;//私有虚拟显示在顶部的描述
public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;//声音交互
public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;
public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;
public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;
public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;
public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;
public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
public static final int LAST_SYSTEM_WINDOW = 2999;//系统窗口结尾
应用窗口从1开始,到99。子窗口范围为1000 ~ 1999 ,系统窗口范围为2000~2999。子窗口表示依赖于应用窗口的窗口,比如PopupWindow就是依赖于应用的子窗口。Android系统有个X,Y,Z轴的坐标体系。X表示横轴,Y表示竖轴,Z表示垂直于平面的轴。这些窗口数值对应的事Z轴。数字越大表示越靠近用户。从上面的数值可以看出,系统窗口是最靠近用户的,其次是子窗口,最后是应用窗口。
Flags参数表示Window的属性,控制着Window的显示特性。
FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
FLAG_DIM_BEHIND
FLAG_NOT_FOCUSABLE//不获取焦点,不接收各种输入事件,由后面的窗口得到焦点
FLAG_NOT_TOUCHABLE
FLAG_NOT_TOUCH_MODAL//不获取触摸事件,由后面的窗口得到触摸事件
FLAG_TOUCHABLE_WHEN_WAKING
FLAG_KEEP_SCREEN_ON
FLAG_LAYOUT_IN_SCREEN
FLAG_LAYOUT_NO_LIMITS
FLAG_FULLSCREEN
FLAG_FORCE_NOT_FULLSCREEN
FLAG_SECURE
FLAG_SCALED
FLAG_IGNORE_CHEEK_PRESSES
FLAG_LAYOUT_INSET_DECOR
FLAG_ALT_FOCUSABLE_IM
FLAG_WATCH_OUTSIDE_TOUCH
FLAG_SHOW_WHEN_LOCKED //让Window显示在锁屏的界面上
FLAG_SHOW_WALLPAPER
FLAG_TURN_SCREEN_ON
FLAG_DISMISS_KEYGUARD
FLAG_SPLIT_TOUCH
FLAG_HARDWARE_ACCELERATED
FLAG_LOCAL_FOCUS_MODE
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
Flags标识按位来增加,比如下面这这些。当几个功能叠加的时候可以用或运算。
flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;
public static final int FLAG_DIM_BEHIND = 0x00000002;
public static final int FLAG_BLUR_BEHIND = 0x00000004;
public static final int FLAG_NOT_FOCUSABLE = 0x00000008;
public static final int FLAG_NOT_TOUCHABLE = 0x00000010;
public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;
public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;
public static final int FLAG_LAYOUT_IN_SCREEN = 0x00000100;
......
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
......
不止这两个属性设置,还有softInputMode,gravity,horizontalMargin,verticalMargin,screenBrightness,buttonBrightness,rotationAnimation等等的参数设置。WindowManager.LayoutParams里面有addFlags
、clearFlags
、setColorMode
等来控制这些属性的设置。
softInputMode是软键盘设置,可以看下它的几个参数。
SOFT_INPUT_STATE_UNSPECIFIED,
SOFT_INPUT_STATE_UNCHANGED,
SOFT_INPUT_STATE_HIDDEN,
SOFT_INPUT_STATE_ALWAYS_HIDDEN,
SOFT_INPUT_STATE_VISIBLE,
SOFT_INPUT_STATE_ALWAYS_VISIBLE,
SOFT_INPUT_ADJUST_UNSPECIFIED,
SOFT_INPUT_ADJUST_RESIZE,
SOFT_INPUT_ADJUST_PAN,
SOFT_INPUT_ADJUST_NOTHING,
SOFT_INPUT_IS_FORWARD_NAVIGATION,