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里面实际上只有三个方法:addViewupdateViewLayoutremoveView,分别对应着添加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里面有addFlagsclearFlagssetColorMode等来控制这些属性的设置。
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,
打赏 赞(0)
微信
支付宝
微信二维码图片

微信扫描二维码打赏

支付宝二维码图片

支付宝扫描二维码打赏

分类: Android

0 条评论

发表评论

电子邮件地址不会被公开。