当前位置: 首页 > news >正文

Android EditText setTranslationY导致输入法覆盖问题

平台

RK3288 + Android 8.1
显示: 1920x1080 @ 160 dpi

概述

碰到一个问题: 弹出的输入法会覆盖文本输入框。
原因:输入框使用了setTranslationY() 位置偏移后, 输入法无法正确获取焦点的位置。

分析

先上图: 初始布局
在这里插入图片描述
调用etTranslationY(700);
在这里插入图片描述
弹出输入法
在这里插入图片描述
最后一张图中, 输入框大概在红框的位置, 也是本文所描述的问题: 输入法遮挡了输入框控件


  • 布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayout android:id="@+id/llEdit"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/tv"android:textSize="28sp"android:gravity="center"android:text="--------------FOOTER---------------------"android:layout_width="match_parent"android:layout_height="wrap_content"/><EditText android:id="@+id/et"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="36sp"/><TextViewandroid:id="@+id/tv2"android:textSize="28sp"android:gravity="center"android:text="--------------HEADER---------------------"android:layout_width="match_parent"android:layout_height="wrap_content"/></LinearLayout><Button android:id="@+id/btTy"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="TranslationY"/>
</RelativeLayout>
  • java
package com.android.apitester.test;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;import com.android.apitester.R;public class EditTextTranslationTest extends Activity {EditText et;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.test_edittext_translation);et = (EditText) findViewById(R.id.et);findViewById(R.id.btTy).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {et.setTranslationY(et.getTranslationY() != 0 ? 0 : 700);}});}
}

稍微改下代码,把输入框放到界面底部
在这里插入图片描述
在这里插入图片描述
输入法正常弹出,并把整体UI往上顶。

后续做了一些数据, getTranslationY不同的大小以作比对

位移大小展示效果备注
-300被覆盖-
-70被覆盖-
-69第一次后正常第一次被覆盖
-50第一次后正常第一次被覆盖
>0被覆盖-

70 是控件的高度!

输入法是怎么把布局顶上去的? 答案在ViewRootImpl中。

frameworks/base/core/java/android/view/ViewRootImpl.java

    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {String innerPrefix = prefix + "  ";//..... 省略 .....writer.print(innerPrefix); writer.print("getCurrY=");writer.print(mScroller != null ? mScroller.getCurrY():0);writer.print("mScrollY=");writer.print(mScrollY);writer.print("mCurScrollY=");writer.print(mCurScrollY);}

dumpsys activity name

//未打开输入法
getCurrY=0,mScrollY=0,mCurScrollY=0//打开输入法
getCurrY=372,mScrollY=372,mCurScrollY=372

准确地说,是上去的

ViewRootImpl doTraversal performTraversals draw scrollToRectOrFocus ViewRootImpl
    boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {final Rect ci = getWindowInsets(false).getSystemWindowInsetsAsRect();final Rect vi = mAttachInfo.mVisibleInsets;int scrollY = 0;boolean handled = false;if (vi.left > ci.left || vi.top > ci.top|| vi.right > ci.right || vi.bottom > ci.bottom) {// We'll assume that we aren't going to change the scroll// offset, since we want to avoid that unless it is actually// going to make the focus visible...  otherwise we scroll// all over the place.scrollY = mScrollY;// We can be called for two different situations: during a draw,// to update the scroll position if the focus has changed (in which// case 'rectangle' is null), or in response to a// requestChildRectangleOnScreen() call (in which case 'rectangle'// is non-null and we just want to scroll to whatever that// rectangle is).final View focus = mView.findFocus();if (focus == null) {return false;}View lastScrolledFocus = (mLastScrolledFocus != null) ? mLastScrolledFocus.get() : null;if (focus != lastScrolledFocus) {// If the focus has changed, then ignore any requests to scroll// to a rectangle; first we want to make sure the entire focus// view is visible.rectangle = null;}if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Eval scroll: focus=" + focus+ " rectangle=" + rectangle + " ci=" + ci+ " vi=" + vi);if (focus == lastScrolledFocus && !mScrollMayChange && rectangle == null) {// Optimization: if the focus hasn't changed since last// time, and no layout has happened, then just leave things// as they are.if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Keeping scroll y="+ mScrollY + " vi=" + vi.toShortString());} else {// We need to determine if the currently focused view is// within the visible part of the window and, if not, apply// a pan so it can be seen.mLastScrolledFocus = new WeakReference<View>(focus);mScrollMayChange = false;if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Need to scroll?");// Try to find the rectangle from the focus view.if (focus.getGlobalVisibleRect(mVisRect, null)) {if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Root w="+ mView.getWidth() + " h=" + mView.getHeight()+ " ci=" + ci.toShortString()+ " vi=" + vi.toShortString());if (rectangle == null) {focus.getFocusedRect(mTempRect);if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Focus " + focus+ ": focusRect=" + mTempRect.toShortString());if (mView instanceof ViewGroup) {((ViewGroup) mView).offsetDescendantRectToMyCoords(focus, mTempRect);}if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Focus in window: focusRect="+ mTempRect.toShortString()+ " visRect=" + mVisRect.toShortString());} else {mTempRect.set(rectangle);if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Request scroll to rect: "+ mTempRect.toShortString()+ " visRect=" + mVisRect.toShortString());}if (mTempRect.intersect(mVisRect)) {if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Focus window visible rect: "+ mTempRect.toShortString());if (mTempRect.height() >(mView.getHeight()-vi.top-vi.bottom)) {// If the focus simply is not going to fit, then// best is probably just to leave things as-is.if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Too tall; leaving scrollY=" + scrollY);}// Next, check whether top or bottom is covered based on the non-scrolled// position, and calculate new scrollY (or set it to 0).// We can't keep using mScrollY here. For example mScrollY is non-zero// due to IME, then IME goes away. The current value of mScrollY leaves top// and bottom both visible, but we still need to scroll it back to 0.else if (mTempRect.top < vi.top) {scrollY = mTempRect.top - vi.top;if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Top covered; scrollY=" + scrollY);} else if (mTempRect.bottom > (mView.getHeight()-vi.bottom)) {scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom);if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Bottom covered; scrollY=" + scrollY);} else {scrollY = 0;}handled = true;}}}}if (scrollY != mScrollY) {if (DEBUG_INPUT_RESIZE) Log.v(mTag, "Pan scroll changed: old="+ mScrollY + " , new=" + scrollY);if (!immediate) {if (mScroller == null) {mScroller = new Scroller(mView.getContext());}mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);} else if (mScroller != null) {mScroller.abortAnimation();}mScrollY = scrollY;}return handled;}        

frameworks/base/core/java/android/view/View.java

    public void getDrawingRect(Rect outRect) {outRect.left = mScrollX;outRect.top = mScrollY;outRect.right = mScrollX + (mRight - mLeft);outRect.bottom = mScrollY + (mBottom - mTop);}public void getFocusedRect(Rect r) {getDrawingRect(r);}

获取当前聚焦的控件的位置信息与当前ViewRootImpl的可见区域进行比对计算出滚动距离。
在绘制的过程中不断更新并计算滚动位置

通过修改mScroller的动画时长,可以清晰看到滚动的过程效果

mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
//改为
mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY, 1000);

在这里插入图片描述
为什么刚好位移 setTranslationY(70) 无法滚动主窗口

		if (mTempRect.intersect(mVisRect)) {if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Focus window visible rect: "+ mTempRect.toShortString());if (mTempRect.height() >(mView.getHeight()-vi.top-vi.bottom)) {// If the focus simply is not going to fit, then// best is probably just to leave things as-is.if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Too tall; leaving scrollY=" + scrollY);}// Next, check whether top or bottom is covered based on the non-scrolled// position, and calculate new scrollY (or set it to 0).// We can't keep using mScrollY here. For example mScrollY is non-zero// due to IME, then IME goes away. The current value of mScrollY leaves top// and bottom both visible, but we still need to scroll it back to 0.else if (mTempRect.top < vi.top) {scrollY = mTempRect.top - vi.top;if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Top covered; scrollY=" + scrollY);} else if (mTempRect.bottom > (mView.getHeight()-vi.bottom)) {scrollY = mTempRect.bottom - (mView.getHeight()-vi.bottom);if (DEBUG_INPUT_RESIZE) Log.v(mTag,"Bottom covered; scrollY=" + scrollY);} else {scrollY = 0;}handled = true;}

获取的控件的焦点区域和可视区域不存在交集, 导致后续的mScroller部分的代码没有执行。
在TextView中重写了 getFocusedRect,返回的是 光标的坐标,在测试的DEMO中输出如下 [2,10][6,70] 的坐标。

/**
//弹
Need to scroll?
Root w=1920 h=1080 ci=[0,24][0,56] vi=[0,24][0,466]
Focus android.widget.EditText{4146188 VFED..CL. .F..H.I. 0,828-1920,898 #7f03000a app:id/et aid=1073741824}: focusRect=[2,10][6,70]
Focus in window: focusRect=[2,926][6,986] visRect=[0,916][1920,986]
Focus window visible rect: [2,926][6,986]
Bottom covered; scrollY=372
Pan scroll changed: old=0 , new=372//不弹
Eval scroll: focus=android.widget.EditText{40fcecd VFED..CL. .F..H.I. 0,828-1920,898 #7f03000a app:id/et aid=1073741824} rectangle=null ci=Rect(0, 24 - 
Need to scroll?
Root w=1920 h=1080 ci=[0,24][0,56] vi=[0,24][0,466]
Focus android.widget.EditText{40fcecd VFED..CL. .F..H.I. 0,828-1920,898 #7f03000a app:id/et aid=1073741824}: focusRect=[2,10][6,70]
Focus in window: focusRect=[2,926][6,986] visRect=[0,846][1920,916]

if (mTempRect.intersect(mVisRect)) 对应的两个矩形:

  1. focusRect=[2,926][6,986] visRect=[0,916][1920,986] <-
  2. focusRect=[2,926][6,986] visRect=[0,846][1920,916] <- 不弹,无交集

参考

Android软键盘弹出时把布局顶上去的解决方法
Android EditText默认不弹出输入法的实现方法
5种方法完美解决android软键盘挡住输入框方法详解
Android输入法弹出流程

相关文章:

Android EditText setTranslationY导致输入法覆盖问题

平台 RK3288 Android 8.1 显示: 1920x1080 160 dpi 概述 碰到一个问题&#xff1a; 弹出的输入法会覆盖文本输入框。 原因&#xff1a;输入框使用了setTranslationY() 位置偏移后&#xff0c; 输入法无法正确获取焦点的位置。 分析 先上图: 初始布局 调用etTranslation…...

MySQL 导出和导入数据

文章目录 一&#xff0c;导出数据&#xff08;一&#xff09;使用SELECT ... INTO OUTFILE语句导出数据&#xff08;二&#xff09;使用mysqldump工具导出数据&#xff08;三&#xff09;使用SELECT ... INTO DUMPFILE语句导出数据 二&#xff0c;导入数据&#xff08;一&#…...

ubuntu22.04 设置网卡开机自启

配置文件路径 在Ubuntu中&#xff0c;网络配置文件通常位于/etc/netplan/目录下&#xff0c;其文件名以.yaml为后缀。Netplan是Ubuntu 17.10及更高版本中默认的网络配置工具&#xff0c;用于配置网络接口、IP地址、网关、DNS服务器等。 我们可以看到配置文件为 01-network-ma…...

持续部署:提高敏捷加速软件交付(内含教程)

在当今快节奏的数字化环境中&#xff0c;企业不断寻求更快地交付软件、增强客户体验并在竞争中保持领先的方法。持续部署&#xff08;Continuous Deployment, CD&#xff09;已成为一种改变游戏规则的方法&#xff0c;使企业能够简化软件交付、提高敏捷性并缩短上市时间。持续部…...

Spark_Spark内存模型管理

工作中经常用到Spark内存调参&#xff0c;之前还没对这块记录&#xff0c;这次记录一下。 环境参数 spark 内存模型中会涉及到多个配置&#xff0c;这些配置由一些环境参数及其配置值有关&#xff0c;为防止后面理解混乱&#xff0c;现在这里列举出来&#xff0c;如果忘记了&a…...

C++之operator=与operator==用法区别(二百一十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…...

【漏洞复现】WordPress插件wp-file-manager任意文件上传漏洞(CVE-2020-25213)

文章目录 前言声明一、简介二、插件介绍三、漏洞概述四、影响范围五、漏洞分析六、环境搭建七、漏洞复现手工验证file_Manager_Rce.pyfile_manager_upload.py八、修复建议前言 WordPress插件WPFileManager中存在一个严重的安全漏洞,攻击者可以在安装了此插件的任何WordPress网…...

基于安卓Java试题库在线考试系统uniapp 微信小程序

本文首先分析了题库app应用程序的需求&#xff0c;从系统开发环境、系统目标、设计流程、功能设计等几个方面对系统进行了系统设计。开发出本题库app&#xff0c;主要实现了学生、教师、测试卷、试题、考试等。总体设计主要包括系统功能设计、该系统里充分综合应用Mysql数据库、…...

Java入坑之语法糖

一、for和for-each 1.1for和for-each概念 for 循环是一种常用的循环结构&#xff0c;它可以通过一个变量&#xff08;通常是 i&#xff09;来控制循环的次数和范围。for 循环的语法格式如下&#xff1a; for (初始化; 布尔表达式; 更新) {//代码语句 }for-each 循环是 Java …...

VUE响应式

响应式 :::tip 提示 我们了解过响应式可以同步更新数据和视图&#xff0c;但是其工作原理我们最好也要了解一下。这样当你使用时遇到一些常见的错误&#xff0c;也能够快速定位是什么问题导致的。 了解响应式原理之前&#xff0c;你必须要先去了解 ES5 的 Object.defineProper…...

Godot 和 VScode配置C#环境注意事项

前言 尽管有些博主会建议如果我们熟悉C#的话&#xff0c;最好还是使用GDscript&#xff0c;而且对于小白上手也相对简单&#xff0c;但是C#的性能终究还是比动态语言好&#xff0c;也相比CPP简单些&#xff0c;尽管现在Godot还是有些问题&#xff0c;比如不像unity那样适配swit…...

三、Mediasoup进程通信实现的原理

Mediasoup 创建父子进程&#xff0c;js与c进程交互的通道 worker.js构造函数中创建父子进程&#xff0c;c通过libuv的socket可以实现 JavaScript 与 C 之间的相互收发消息 一、 父子进程通信 这是一个简单的示例&#xff0c;演示了如何使用 libuv 在父子进程之间进行通信。以…...

【计算机网络】 TCP——四次挥手

文章目录 流程考点 流程 主动方打算关闭连接&#xff0c;此时会发送一个TCP首部FIN标志位被置为1的报文&#xff0c;也即FIN报文&#xff0c;之后主动方进入FIN_WAIT_1状态。被动方收到该报文后&#xff0c;就向主动方发送ACK应答报文&#xff0c;接着被动方进入CLOSE_WAIT状态…...

「Java开发指南」在MyEclipse中的Spring开发(二)

在上文中&#xff08;点击这里回顾>>&#xff09;&#xff0c;我们主要介绍了一些Spring的基本概念、Spring项目配置及向导&#xff0c;本章节将继续介绍如何管理多个项目&#xff0c;Spring配置编辑器等&#xff0c;欢迎持续关注~ MyEclipse v2023.1.2离线版下载(Q技术…...

策略模式,一种广泛应用于各种情况的设计模式(设计模式与开发实践 P5)

文章目录 策略模式实现思想实战 - 表单 策略模式 定义&#xff1a;定义一系列算法&#xff0c;把它们一个个封装起来&#xff0c;并且可以互相替换 例如&#xff0c;我们要计算年终奖&#xff0c;年终奖根据绩效 A、B、C 来计算最终数值 实现 最初我们很容易想到用 分支 if…...

90. 子集 II

给你一个整数数组 nums &#xff0c;其中可能包含重复元素&#xff0c;请你返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。返回的解集中&#xff0c;子集可以按 任意顺序 排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,2] 输出…...

Intel汇编语言程序设计(第7版)第四章编程练习题答案

1. 大端序转成小端序 .386 .model flat, stdcall option casemap:none include windows.inc include kernel32.inc includelib kernel32.lib include user32.inc includelib user32.lib.stack 4096.data bigEndian BYTE 12h, 34h, 56h, 78h littleEndian DWORD ?Fmt BYTE &…...

EDA(Exploratory Data Analysis)探索性数据分析

EDA(Exploratory Data Analysis)中文名称为探索性数据分析&#xff0c;是为了在特征工程或模型开发之前对数据有个基本的了解。数据类型通常分为两类&#xff1a;连续类型和离散类型&#xff0c;特征类型不同&#xff0c;我们探索的内容也不同。 1. 特征类型 1.1 连续型特征 …...

Python中的多媒体处理库有哪些?

在Python中&#xff0c;有几个常用的多媒体处理库&#xff0c;包括&#xff1a; Pillow - 一个强大的图像处理库&#xff0c;可以进行图像的读取、保存、剪裁、调整大小、滤镜处理等操作。 OpenCV - 一个用于图像和视频处理的开源计算机视觉库&#xff0c;提供了许多图像处理和…...

LeetCode【28. 找出字符串中第一个匹配项的下标】

不要用珍宝装饰自己&#xff0c;而要用健康武装身体 给你两个字符串 haystack 和 needle &#xff0c;请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标&#xff08;下标从 0 开始&#xff09;。如果 needle 不是 haystack 的一部分&#xff0c;则返回 -1 。 …...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

WEB3全栈开发——面试专业技能点P4数据库

一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库&#xff0c;基于 mysql 库改进而来&#xff0c;具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点&#xff1a; 支持 Promise / async-await&#xf…...

qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001

qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类&#xff0c;直接把源文件拖进VS的项目里&#xff0c;然后VS卡住十秒&#xff0c;然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分&#xff0c;导致编译的时候找不到了。因…...

SQL注入篇-sqlmap的配置和使用

在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap&#xff0c;但是由于很多朋友看不了解命令行格式&#xff0c;所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习&#xff0c;链接&#xff1a;https://wwhc.lanzoue.com/ifJY32ybh6vc…...

性能优化中,多面体模型基本原理

1&#xff09;多面体编译技术是一种基于多面体模型的程序分析和优化技术&#xff0c;它将程序 中的语句实例、访问关系、依赖关系和调度等信息映射到多维空间中的几何对 象&#xff0c;通过对这些几何对象进行几何操作和线性代数计算来进行程序的分析和优 化。 其中&#xff0…...

华为云Flexus+DeepSeek征文 | 基于Dify构建具备联网搜索能力的知识库问答助手

华为云FlexusDeepSeek征文 | 基于Dify构建具备联网搜索能力的知识库问答助手 一、构建知识库问答助手引言二、构建知识库问答助手环境2.1 基于FlexusX实例的Dify平台2.2 基于MaaS的模型API商用服务 三、构建知识库问答助手实战3.1 配置Dify环境3.2 创建知识库问答助手3.3 使用知…...

MyBatis-Plus 常用条件构造方法

1.常用条件方法 方法 说明eq等于 ne不等于 <>gt大于 >ge大于等于 >lt小于 <le小于等于 <betweenBETWEEN 值1 AND 值2notBetweenNOT BETWEEN 值1 AND 值2likeLIKE %值%notLikeNOT LIKE %值%likeLeftLIKE %值likeRightLIKE 值%isNull字段 IS NULLisNotNull字段…...