WindowManager的简单使用介绍,以及removeView与removeViewImmediate的区别

本文详细介绍了Android中WindowManager如何添加、更新和移除全局视图,包括addView的权限要求、updateViewLayout的同步操作以及removeView与removeViewImmediate的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android中view的更新全部由WindowManager负责。

(1)WindowManager.addView的简单使用:
当我们需要弹出一个全局的Telop是就需要用到WindowManager的addView方法。
mWindowManager =
            getSystemService(Context.WINDOW_SERVICE) as WindowManager
        mView = View.inflate(this, R.layout.full_layout, null)
        mParams.format = PixelFormat.RGBA_8888
        mParams.type = Config.WINDOW_TYPE
        mParams.flags = (WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                or WindowManager.LayoutParams.FLAG_FULLSCREEN
                or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
        mParams.gravity = Gravity.TOP or Gravity.START
        mParams.width = 100
        mParams.height = 100
        mWindowManager.addView(mView, mParams)

使用此方法需要配合权限

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

此部分需要用户自动同意允许弹出的Telop浮在其他应用上方。 

 if (!Settings.canDrawOverlays(this)) {
            startActivityForResult(new Intent(
                    Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName())
            ), 1);
}

并且非系统级的应用type只能指定。 

WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY

(2)WindowManager.updateViewLayout的简单使用:


根据名称其实现原理就是针对当前View的ViewGroup.LayoutParams进行更新操作。
此部分为同步操作,不建议频繁调用此方法。
aosp源码实现如下:

@Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyTokens(params);
        mGlobal.updateViewLayout(view, params);
    }

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);
        }
    }
(3)removeView与removeViewImmediate的区别: 

当我们执行了addView之后Telop执行完毕其功能之后需要消去,此时需要调用WindowManager的消去方法
aosp中提供了removeView与removeViewImmediate两种方法。
其中removeView为异步方法,

 @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

 removeViewImmediate为同步方法。

  @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

 ViewImpl的具体实现:
当调用removeView方法时是调用handler的队列进行移除view操作。
当调用removeViewImmediate方法时是直接做移除View操作。

  /**
     * @param immediate True, do now if not in traversal. False, put on queue and do later.
     * @return True, request has been queued. False, request has been completed.
     */
    boolean die(boolean immediate) {
        // Make sure we do execute immediately if we are in the middle of a traversal or the damage
        // done by dispatchDetachedFromWindow will cause havoc on return.
        if (immediate && !mIsInTraversal) {
            doDie();
            return false;
        }

        if (!mIsDrawing) {
            destroyHardwareRenderer();
        } else {
            Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
                    "  window=" + this + ", title=" + mWindowAttributes.getTitle());
        }
        mHandler.sendEmptyMessage(MSG_DIE);
        return true;
    }

PS:针对removeViewImmediate API官方提醒:
/**
     * Special variation of {@link #removeView} that immediately invokes
     * the given view hierarchy's {@link View#onDetachedFromWindow()
     * View.onDetachedFromWindow()} methods before returning.  This is not
     * for normal applications; using it correctly requires great care.
     *
     * @param view The view to be removed.
     */
    public void removeViewImmediate(View view);

<think>嗯,用户现在问的是关于使用`Window.addView`来弹出弹窗的介绍。我需要先理解用户的问题到底是想了解什么。用户可能正在开发Android应用,遇到了需要自定义弹窗的情况,而常见的Dialog或PopupWindow可能无法满足需求,所以转而使用`Window.addView`这种方法。 首先,我需要回顾一下Android中窗口管理的基本知识。`Window`在Android中代表一个顶层窗口,通常Activity关联。通过`getWindow()`方法获取当前Activity的Window对象,然后使用`addView()`方法可以直接添加视图到窗口上。这种方式比较底层,可以更灵活地控制弹窗的位置、样式等,但同时也需要处理更多细节,比如触摸事件、焦点管理、生命周期等。 接下来,用户提到的“弹窗是使用window.addview弹出的”可能指的是直接向Window添加View来实现弹窗效果。这种情况下,弹窗可能是一个悬浮在Activity之上的视图,类似于Toast,但需要更多的自定义控制。常见的应用场景可能是全屏弹窗、自定义位置的悬浮窗,或者需要特殊动画效果的弹窗。 用户可能已经尝试过其他弹窗组件,比如AlertDialog或PopupWindow,但发现它们不够灵活,或者在某些情况下存在限制。例如,PopupWindow在某些系统版本上可能有位置或触摸事件处理的问题,而Dialog可能会占用整个Activity窗口,影响用户体验。因此,用户转向使用`Window.addView`来实现更底层的控制。 接下来,我需要考虑用户可能遇到的问题。使用`Window.addView`需要手动处理很多细节,比如设置正确的窗口类型(Type)、Flag、触摸事件拦截、背景模糊或半透明效果,以及如何正确关闭弹窗。如果处理不当,可能会导致内存泄漏、UI错乱或者触摸事件无法正确传递。 例如,窗口类型通常需要设置为`TYPE_APPLICATION_PANEL`或`TYPE_APPLICATION_OVERLAY`(后者需要权限),以确保弹窗显示在正确的位置。同时,需要设置Flag如`FLAG_NOT_FOCUSABLE`或`FLAG_NOT_TOUCH_MODAL`来控制焦点和触摸行为。用户可能对如何正确设置这些参数不太清楚,导致弹窗无法显示或行为异常。 另外,触摸事件的处理也是一个关键点。用户可能希望点击弹窗外部关闭弹窗,这需要判断触摸事件是否发生在弹窗区域内,如果是外部区域则关闭。这涉及到计算View的位置和判断触摸坐标是否在View的边界内,或者使用遮罩层来捕获触摸事件。 此外,内存管理和生命周期也是需要注意的地方。如果弹窗没有正确移除,可能会导致Window泄漏,进而引发内存问题。用户可能不清楚在何时调用`removeView`或`removeViewImmediate`来正确销毁弹窗。 还有,样式和动画的设置。使用`Window.addView`创建的弹窗可能需要自定义进入和退出动画,或者设置背景透明、圆角等视觉效果。用户可能需要指导如何通过Window的Attributes来设置这些属性。 综合来看,用户的需求不仅仅是了解如何使用`Window.addView`弹出弹窗,而是希望获得一个完整的实现指南,包括正确的窗口参数配置、触摸事件处理、样式设置以及生命周期管理。可能还需要对比其他弹窗实现方式的优缺点,帮助用户理解在什么情况下选择`Window.addView`更合适。 另外,用户可能是在处理一些特殊的UI需求,比如游戏中的HUD、浮动按钮或者需要跨Activity显示的悬浮窗,这时候使用`Window.addView`会更灵活。但需要注意,从Android 8.0(API 26)开始,`TYPE_APPLICATION_OVERLAY`需要请求权限,否则无法显示,这可能也是用户需要注意的地方。 最后,用户可能还关心性能优化,比如如何避免频繁添加和移除View导致的界面卡顿,或者如何正确管理多个弹窗的层级关系。这些都是使用底层Window管理时需要综合考虑的问题。</think>在 Android 开发中,通过 `Window.addView()` 直接向窗口添加视图(View)是一种底层的弹窗实现方式。这种方式常用于**高度自定义的悬浮窗**(如系统级提示、游戏内悬浮按钮等),但需要开发者手动管理窗口参数、触摸事件和生命周期。以下是具体实现方法和关键点: --- ### 一、核心原理 - **`Window` 对象**:每个 Activity 都关联一个 `Window`(通过 `getWindow()` 获取),负责管理窗口的视图层级。 - **`WindowManager`**:通过 `WindowManager`(实际是 `WindowManagerImpl`)控制窗口的添加、更新和移除。 - **层级控制**:通过设置 `WindowManager.LayoutParams` 的 `type` 和 `flags` 属性,控制弹窗的显示位置、行为(如是否可点击、是否拦截焦点)。 --- ### 二、实现步骤(以 Activity 内弹窗为例) #### 1. 创建自定义弹窗布局 ```xml <!-- res/layout/custom_dialog.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/dialog_root" android:layout_width="300dp" android:layout_height="200dp" android:background="#FFFFFF" android:orientation="vertical"> <TextView android:text="这是一个自定义弹窗" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> ``` #### 2. 通过 `WindowManager` 添加弹窗 ```java // 在 Activity 中实现 public void showCustomDialog() { // 1. 加载布局 View dialogView = LayoutInflater.from(this).inflate(R.layout.custom_dialog, null); // 2. 设置窗口参数 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.width = WindowManager.LayoutParams.WRAP_CONTENT; // 宽度 params.height = WindowManager.LayoutParams.WRAP_CONTENT; // 高度 params.format = PixelFormat.TRANSLUCENT; // 透明背景 params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; // 类型(普通应用窗口) params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL // 不阻塞下层触摸事件 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; // 监听外部点击 // 3. 添加视图到窗口 WindowManager windowManager = getWindowManager(); windowManager.addView(dialogView, params); } ``` #### 3. 处理外部点击关闭 ```java dialogView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // 判断是否为外部点击 if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // 移除弹窗 WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); windowManager.removeView(dialogView); return true; } return false; } }); ``` --- ### 三、关键参数详解(`WindowManager.LayoutParams`) | 参数 | 说明 | 常用值 | |------|------|--------| | `type` | 窗口层级类型 | `TYPE_APPLICATION_PANEL`(普通弹窗)<br/>`TYPE_APPLICATION_OVERLAY`(系统级悬浮窗,需权限) | | `flags` | 控制窗口行为 | `FLAG_NOT_FOCUSABLE`(不获取焦点)<br/>`FLAG_NOT_TOUCH_MODAL`(透传触摸事件)<br/>`FLAG_DIM_BEHIND`(背景变暗) | | `gravity` | 弹窗位置 | `Gravity.CENTER`、`Gravity.TOP` 等 | | `dimAmount` | 背景变暗程度 | `0.0f`(透明)~ `1.0f`(全黑) | --- ### 四、注意事项 1. **权限问题**: - `TYPE_APPLICATION_OVERLAY` 需要动态请求 `SYSTEM_ALERT_WINDOW` 权限。 - 从 Android 10 开始,非系统应用无法使用 `TYPE_PHONE` 等类型。 2. **内存泄漏**: - 确保在 `Activity.onDestroy()` 或 `Fragment.onDestroyView()` 中调用 `removeView()`。 3. **触摸事件冲突**: - 若设置 `FLAG_NOT_TOUCH_MODAL`,需自行处理事件传递逻辑。 4. **动画效果**: - 可通过 `WindowManager.LayoutParams.windowAnimations` 设置进入/退出动画。 --- ### 五、对比其他弹窗方案 | 方案 | 优点 | 缺点 | |------|------|------| | **`Window.addView()`** | 完全自定义、可跨 Activity | 需手动管理生命周期 | | **`Dialog`** | 封装完善、自带生命周期 | 样式受系统限制 | | **`PopupWindow`** | 支持锚点定位 | 触摸事件处理复杂 | --- ### 六、扩展问题 1. **动态调整窗口大小** 修改 `LayoutParams` 后调用 `windowManager.updateViewLayout(view, params)`。 2. **跨进程弹窗** 需要权限且类型为 `TYPE_APPLICATION_OVERLAY`,但可能被系统限制。 如果需要进一步探讨某个具体问题(如权限申请、复杂动画),可以继续提问!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值