5分钟搞定PopUpWindow

本文介绍如何解决PopUpWindow在华为等手机横屏时显示位置偏移的问题,包括PopUpWindow的基本使用方法及针对虚拟按键机型的解决方案。

1e97da0737325a6483ccfebbe0ed9c36da93a5137961c-1j0DuG_fw658.png

前言

最近在项目中用到了 PopUpWindow,并且在机型适配时发现华为等具有虚拟按键的手机在横屏状态时会造成 PopUpWindow 显示位置偏移的情况存在,最后完美解决了这个问题,所以把经验分享出来,看能否对你有用。

弹窗有很多种实现方式,例如:
1. Dialog
2. DialogFragment
3. Fragment
4. PopUpWindow
5. ListPopUpWindow

在这些方式中,我们主要讲 PopUpWindow 的使用方式,因为它能在任意位置弹出, 这是其他方式很难做到的。

从 Google 爸爸的开发文档中我们不难看出,首先它是一个 Window,弹出时位于其他控件的上层。

image.png

怎么使用 PopUpWindow

  • 创建布局文件
  • 创建 ContentView
  • 设置 PopUpWindow
  • Show

创建布局文件

PopUpWindow 就是一个容器,是需要编写对应的布局文件,在项目需求中我们要做成这样的效果

image.png

<com.tutu.app.view.TutuRegisterTitleView xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:background="@color/sdk_background"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tutu_game_register_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:layout_marginTop="4dp"
        android:textAlignment="center"
        android:layout_marginBottom="6dp"
        android:textColor="#333333"
        android:textSize="17dp" />

    <View
        style="@style/TutuGameFullLandscapeLine"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"/>
</com.tutu.app.view.TutuRegisterTitleView>

创建 ContentView

ContentView 是我们将布局文件生成的View,并且将其放入 PopUpWindow 中。


        // 初始化popUpWindow
        linearLayout = new LinearLayout(getContext());
        // 生成 View 对象
        popRootView =  View.inflate(getContext(), 
        // PopUpWindow 传入 ContentView
        popupWindow = new PopupWindow(popRootView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

设置 PopUpWindow

看到这里,有人可能问:我们在布局文件中已经设置了背景颜色,为什么要在Java代码中重复设置呢?

因为在某些 Andorid 系统版本中会出现点击外部和返回键弹窗却无法消失的 Bug,所以你懂的。

附上某大牛对该 Bug 的分析 PopupWindow 点击外部和返回键无法消失背后的真相

        // 设置背景
        popupWindow.setBackgroundDrawable(new ColorDrawable());
        // 外部点击事件
        popupWindow.setOutsideTouchable(true);

Show

这个就很简单,短短一行代码,控制弹窗的显示实际位置,难点在于 x,y 值的确定

            // 传入 AnchorView ,锚点实际为 Window
            // Gravity.TOP 在该锚点的正上方
            // Gravity.LEFT 在屏幕左侧
            // Gravity.NO_GRAVITY,在屏幕左上角
            // x 为坐标系 x轴方向的偏移量,左负右正
            // y 为坐标系 y轴方向的偏移量,上负下正

            popupWindow.showAtLocation(view, Gravity.TOP, 0, y);


            popupWindow.showAtLocation(view, Gravity.NO_GRAVITY,x, y);


            popupWindow.showAtLocation(view, Gravity.TOP, 0, y);

怎样使用 ListPopUpWindow

GS20180625140257.gif

和PopUpWindow 相比,它更适合展示多条数据,内部包含了一个 ListView ,那就意味着需要 Adapter 进行数据的绑定。

 listPopupWindow = new ListPopupWindow(getContext());
            // 适配器添加数据
            listPopupWindowAdapter.addAdapterList(list);
            // 添加适配器
            listPopupWindow.setAdapter(listPopupWindowAdapter);
            // 设置弹窗的大小和位置
            listPopupWindow.setWidth(width+horizontalOffset*2);
            listPopupWindow.setHeight(height);
            listPopupWindow.setAnchorView(view);
            listPopupWindow.setModal(true);
            listPopupWindow.setVerticalOffset(-VerticalOffset);
            listPopupWindow.setHorizontalOffset(-horizontalOffset*4);
            // 设置背景
            listPopupWindow.setBackgroundDrawable(
                    BitmapUtils.getDrawableFromResource(getContext(), RUtils.drawable(getContext(),"tutu_area_code_background")));

虚拟按键对 PopUpWindow 的影响

虚拟按键的机型在横屏状态下,会造成一个 x 轴方向的偏移(根据具体代码确定),所以我们使用神器 getLocationInWindow,获取锚点 View 在当前 Window 的坐标,然后通过计算确定弹窗出现位置。

具体决定方案,请看 Fucking Code

  // 获取锚点 View 在屏幕中的坐标
  int[] location = new int[2];
                    back.getLocationInWindow(location);

                    int x = location[0];//获取当前位置的横坐标
                    int y = location[1];//获取当前位置的纵坐标
 // 竖屏不做处理
        if (VERTICAL_SCREEN == getContext().getResources().getConfiguration().orientation){
            popupWindow.showAtLocation(view, Gravity.TOP, 0, y);            
        }
        // 横屏状态
        else if (HORIZONTALL_SCREEN == getContext().getResources().getConfiguration().orientation) {
            // 检测是否有虚拟按键
            if (checkDeviceHasNavigationBar(getContext())){

                  popupWindow.showAtLocation(view, Gravity.NO_GRAVITY,x, y);

            }else {
                popupWindow.showAtLocation(view, Gravity.TOP, 0, y);
            }
        }
<think>我们正在回答关于Android PopupWindow的使用教程及常见问题。根据用户要求,我们需要参考给定的引用内容,并按照系统级指令进行回答。 引用内容总结: [^1] 提到了创建PopupWindow的三个基本参数,这是构建复杂PopupWindow的基础。 [^2] 介绍了PopupWindow的概述,包括其定义、创建和操作步骤,以及一个示例项目PopupWindowDemo。 [^3] 提到了PopupWindow无法直接重写事件处理方法,但提供了两种方法处理返回键事件,其中一种是在构造时设置属性。 回答结构: 1. 介绍PopupWindow的基本概念和用途。 2. 详细说明创建PopupWindow的步骤,包括三个基本参数(布局、宽度、高度)。 3. 展示一个简单的PopupWindow创建示例代码(Java)。 4. 讨论常见问题,如处理返回键关闭、动画效果、位置设置等。 5. 生成相关问题。 注意:由于引用内容有限,我们将结合引用和Android开发的一般知识进行回答。</think>### Android PopupWindow 使用教程及常见问题 #### 一、PopupWindow 基础使用教程 1. **创建三个核心参数**(引用[^1]) - **布局文件**:定义弹出窗口的UI界面(XML布局) - **宽度/高度**:设置窗口尺寸(`WRAP_CONTENT` 或固定值) - **焦点设置**:决定是否拦截外部点击事件 2. **基础实现步骤**(引用[^2]): ```java // 1. 加载布局 View popupView = LayoutInflater.from(context).inflate(R.layout.popup_layout, null); // 2. 创建PopupWindow(三个核心参数) PopupWindow popupWindow = new PopupWindow( popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ); // 3. 设置特性 popupWindow.setFocusable(true); // 允许获取焦点 popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); // 透明背景 // 4. 显示位置(以锚定按钮为例) Button anchor = findViewById(R.id.btn_anchor); popupWindow.showAsDropDown(anchor, 0, 0, Gravity.CENTER); ``` #### 二、关键特性配置 1. **动画效果**(引用[^2]): ```java // 在res/anim中定义动画 popupWindow.setAnimationStyle(R.style.PopupAnimation); ``` 2. **关闭行为控制**: ```java // 点击外部关闭(需透明背景) popupWindow.setOutsideTouchable(true); // 拦截返回键(引用[^3]) popupWindow.setFocusable(true); popupView.setOnKeyListener((v, keyCode, event) -> { if (keyCode == KeyEvent.KEYCODE_BACK) { popupWindow.dismiss(); return true; } return false; }); ``` #### 三、常见问题解决方案 1. **点击穿透问题** **原因**:未设置透明背景 **修复**:添加 `setBackgroundDrawable(new ColorDrawable(0x00000000))` 2. **位置显示异常** **对策**:使用精确坐标显示 ```java int[] location = new int[2]; anchor.getLocationOnScreen(location); popupWindow.showAtLocation(anchor, Gravity.NO_GRAVITY, location[0], location[1]+anchor.getHeight()); ``` 3. **内存泄漏处理** **方案**:在Activity销毁时关闭弹窗 ```java @Override protected void onDestroy() { if (popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); } super.onDestroy(); } ``` 4. **内容高度自适应** **技巧**:测量布局后重置高度 ```java popupView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); popupWindow.setHeight(popupView.getMeasuredHeight()); ``` #### 四、高级应用场景 1. **带输入法的弹窗** 添加 `SOFT_INPUT_ADJUST_RESIZE`: ```java popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); ``` 2. **嵌套滚动布局** 使用 `NestedScrollView` 包裹内容: ```xml <androidx.core.widget.NestedScrollView android:layout_height="200dp"> <!-- 弹窗内容 --> </androidx.core.widget.NestedScrollView> ``` 3. **仿菜单动画** 组合平移+透明度动画: ```xml <set xmlns:android="http://schemas.android.com/apk/res/android"> <translate android:fromYDelta="20%" android:toYDelta="0" android:duration="300"/> <alpha android:fromAlpha="0" android:toAlpha="1" android:duration="200"/> </set> ``` > **最佳实践提示**:对于复杂交互场景(如动态更新内容),推荐使用 `DialogFragment` 替代 PopupWindow,以获得更好的生命周期管理[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值