实现弹窗随键盘上移居中

news2025/6/12 18:19:16

实现弹窗随键盘上移的核心思路

在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。

// 在Activity或Fragment中设置键盘监听
val rootView = findViewById<View>(android.R.id.content)
rootView.viewTreeObserver.addOnGlobalLayoutListener {
    val rect = Rect()
    rootView.getWindowVisibleDisplayFrame(rect)
    val screenHeight = rootView.rootView.height
    val keyboardHeight = screenHeight - rect.bottom
    if (keyboardHeight > screenHeight * 0.15) {
        // 键盘显示,调整弹窗位置
        adjustDialogPosition(keyboardHeight)
    } else {
        // 键盘隐藏,恢复默认位置
        resetDialogPosition()
    }
}

创建自定义弹窗布局

使用Dialog或DialogFragment时,需要确保布局可以动态调整位置。示例布局文件:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/dialog_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/dialog_content"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:padding="16dp">
        <!-- 弹窗内容 -->
    </LinearLayout>
</FrameLayout>

动态调整弹窗位置代码

通过修改布局参数实现位置调整:

private fun adjustDialogPosition(keyboardHeight: Int) {
    val dialogContent = dialog.findViewById<View>(R.id.dialog_content)
    val params = dialogContent.layoutParams as FrameLayout.LayoutParams
    val screenHeight = resources.displayMetrics.heightPixels
    val targetY = (screenHeight - keyboardHeight) / 2 - dialogContent.height / 2
    params.topMargin = targetY
    dialogContent.layoutParams = params
}

private fun resetDialogPosition() {
    val dialogContent = dialog.findViewById<View>(R.id.dialog_content)
    val params = dialogContent.layoutParams as FrameLayout.LayoutParams
    params.topMargin = 0
    params.gravity = Gravity.CENTER
    dialogContent.layoutParams = params
}

处理WindowSoftInputMode

在AndroidManifest.xml中为Activity设置正确的软键盘模式:

<activity
    android:name=".YourActivity"
    android:windowSoftInputMode="adjustResize|stateHidden" />

注意事项

  1. 键盘高度计算需要排除系统状态栏和导航栏的影响
  2. 在横屏模式下需要特殊处理布局逻辑
  3. 不同Android版本可能存在行为差异,需要充分测试
  4. 对于DialogFragment,需要在onCreateView中获取根视图进行监听

是的,这是我直接使用AI生成的文章,看了下,大致都实现了,感觉现在博客这条下坡路确实要走到底了啊。

下面是我的代码:

 @Override
    public void onStart() {
        super.onStart();
        // 在对话框显示后设置键盘监听
        setupKeyboardListener();
    }

    /**
     * 设置键盘监听
     */
    private void setupKeyboardListener() { 
        // 设置全局布局监听,检测键盘状态变化
        if (mContext instanceof android.app.Activity) {
            android.app.Activity activity = (android.app.Activity) mContext;
            View rootView = activity.findViewById(android.R.id.content);
            if (rootView != null) {
                mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        checkKeyboardStatus();
                    }
                };
                rootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
            }
        }
    }
    
    /**
     * 检查键盘状态
     */
    private void checkKeyboardStatus() {
        if (mContext instanceof android.app.Activity) {
            android.app.Activity activity = (android.app.Activity) mContext;
            View rootView = activity.findViewById(android.R.id.content);
            if (rootView != null) {
                android.graphics.Rect rect = new android.graphics.Rect();
                rootView.getWindowVisibleDisplayFrame(rect);
                
                int screenHeight = rootView.getHeight();
                int visibleHeight = rect.bottom - rect.top;
                
                // 判断键盘是否弹起(可视区域高度小于屏幕高度的75%)
                boolean keyboardVisible = visibleHeight < screenHeight * 0.75;
                
                if (keyboardVisible && !isKeyboardShown) {
                    // 键盘弹起,计算对话框在剩余可见区域的居中位置
                    isKeyboardShown = true;
                    adjustDialogPosition(true, visibleHeight);
                } else if (!keyboardVisible && isKeyboardShown) {
                    // 键盘收起
                    isKeyboardShown = false;
                    adjustDialogPosition(false, screenHeight);
                }
            }
        }
    }
    
    /**
     * 调整对话框位置
     * @param keyboardShown 键盘是否显示
     * @param visibleHeight 可见区域高度
     */
    private void adjustDialogPosition(boolean keyboardShown, int visibleHeight) {
        if (getDialogHelper() == null) {
            return;
        }
        
        try {
            View contentView = getDialogHelper().getContentView();
            if (keyboardShown) {
                // 键盘弹起时,让对话框在剩余可见区域中居中显示
                getDialogHelper().setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
                
                // 对话框高度(根据布局文件dialog_quick_greet.xml约246dp)
                int dialogHeight = (int) (246 * mContext.getResources().getDisplayMetrics().density);
                
                // 计算让对话框在可见区域居中的上边距
                // 公式:(可见区域高度 - 对话框高度) / 2
                int centeredTopMargin = (visibleHeight - dialogHeight) / 2;
                
                // 设置最小边距,避免对话框贴着屏幕顶部
                int minTopMargin = (int) (50 * mContext.getResources().getDisplayMetrics().density);
                int topMargin = Math.max(centeredTopMargin, minTopMargin);
                
                setContentViewMargin(contentView, topMargin);
            } else {
                // 键盘收起时,恢复默认的居中位置 自己的方法
                getDialogHelper().setGravity(Gravity.CENTER);
                setContentViewMargin(contentView, 0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 设置内容视图的上边距
     */
    private void setContentViewMargin(View contentView, int topMargin) {
        if (contentView != null && contentView.getLayoutParams() instanceof android.view.ViewGroup.MarginLayoutParams) {
            android.view.ViewGroup.MarginLayoutParams params = 
                (android.view.ViewGroup.MarginLayoutParams) contentView.getLayoutParams();
            params.topMargin = topMargin;
            contentView.setLayoutParams(params);
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2407508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建

华为云FlexusDeepSeek征文&#xff5c;DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色&#xff0c;华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型&#xff0c;能助力我们轻松驾驭 DeepSeek-V3/R1&#xff0c;本文中将分享如何…

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…

【论文阅读28】-CNN-BiLSTM-Attention-(2024)

本文把滑坡位移序列拆开、筛优质因子&#xff0c;再用 CNN-BiLSTM-Attention 来动态预测每个子序列&#xff0c;最后重构出总位移&#xff0c;预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵&#xff08;S…

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…