Window和WindowManager

  • Window和WindowManager
    Window表示一个窗口,可以实现在桌面上显示悬浮窗,Window是一个抽象类,具体实现是PhoneWindow。通过WindowManager创建Window,WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService,WindowManager和WindowManagerService交互是一个IPC过程,Window实际是View的直接管理者

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    val btn = Button(baseContext)
    btn.text = "button"
    btn.setOnClickListener { toast("Window Demo") }
    val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,
    LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSLUCENT)
    layoutParams.type = LayoutParams.TYPE_SYSTEM_OVERLAY/TYPE_SYSTEM_ERROR/TYPE_SYSTEM_OVERLAY/TYPE_APPLICATION_PANEL
    //不需要获取焦点,不需要接收各种输入事件
    layoutParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE
    //window 区域以为的单击事件传递给底层window,当前window区域以内的自己处理,否则其他window无法收到单击事件
    .or(LayoutParams.FLAG_NOT_TOUCH_MODAL)
    //window 显示在锁屏上
    .or(LayoutParams.FLAG_SHOW_WHEN_LOCKED)
    layoutParams.gravity = Gravity.LEFT.or(Gravity.TOP)
    layoutParams.x = 100
    layoutParams.y = 300
    windowManager.addView(btn, layoutParams)
  • public interface WindowManager extends ViewManager

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public interface ViewManager
    {
    /**
    * Assign the passed LayoutParams to the passed View and add the view to the window.
    * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
    * errors, such as adding a second view to a window without removing the first view.
    * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
    * secondary {@link Display} and the specified display can't be found
    * (see {@link android.app.Presentation}).
    * @param view The view to be added to this window.
    * @param params The LayoutParams to assign to view.
    */
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
    }
  • 添加触摸事件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    val btn = Button(baseContext)
    btn.isClickable = true
    btn.text = "button"

    // btn.setOnClickListener { toast("Window Demo") }
    val layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT,
    LayoutParams.WRAP_CONTENT, 0, 0, PixelFormat.TRANSLUCENT)
    // layoutParams.type = LayoutParams.TYPE_SYSTEM_OVERLAY
    layoutParams.type =LayoutParams.TYPE_APPLICATION_PANEL
    layoutParams.flags =
    //不需要获取焦点,不需要接收各种输入事件
    LayoutParams.FLAG_NOT_FOCUSABLE
    //window 区域以为的单击事件传递给底层window,当前window区域以内的自己处理,否则其他window无法收到单击事件
    .or(LayoutParams.FLAG_NOT_TOUCH_MODAL)
    //window 显示在锁屏上
    .or(LayoutParams.FLAG_SHOW_WHEN_LOCKED)
    layoutParams.gravity = Gravity.LEFT.or(Gravity.TOP)
    layoutParams.x = 100
    layoutParams.y = 300
    btn.setOnTouchListener { v, event ->
    when (event.action) {
    MotionEvent.ACTION_MOVE -> {
    layoutParams.x = event.rawX.toInt()
    layoutParams.y = event.rawY.toInt()
    windowManager.updateViewLayout(btn, layoutParams)
    }
    else -> {
    Log.i("touch", "else")
    }
    }
    return@setOnTouchListener false
    }
    windowManager.addView(btn, layoutParams)

Window内部机制

Window是抽象概念,每一个Window对应一个View和ViewRootImpl,以View的形式存在

  • 接口WindowManager实现类WindowManagerImpl
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @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);
    }

    @Override
    public void removeView(View view) {
    mGlobal.removeView(view, false);
    }
willkernel wechat
关注微信公众号
帅哥美女们,请赐予我力量吧!