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

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

HTML前端开发:JavaScript 常用事件详解

作为前端开发的核心&#xff0c;JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例&#xff1a; 1. onclick - 点击事件 当元素被单击时触发&#xff08;左键点击&#xff09; button.onclick function() {alert("按钮被点击了&#xff01;&…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...