当前位置: 首页 > 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 。 …...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

如何为服务器生成TLS证书

TLS&#xff08;Transport Layer Security&#xff09;证书是确保网络通信安全的重要手段&#xff0c;它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书&#xff0c;可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错

出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上&#xff0c;所以报错&#xff0c;到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本&#xff0c;cu、torch、cp 的版本一定要对…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...