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输入法弹出流程

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

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

相关文章

抖音小程序开发教学系列(5)- 抖音小程序数据交互

第五章&#xff1a;抖音小程序数据交互 5.1 抖音小程序的网络请求5.1.1 抖音小程序的网络请求方式和API介绍5.1.2 抖音小程序的数据请求示例和错误处理方法 5.2 抖音小程序的数据缓存和本地存储5.2.1 抖音小程序的数据缓存机制和使用方法5.2.2 抖音小程序的本地存储和数据持久化…

unity 接收拼接数据进行纹理替换且保存相机纹理到rtsp server(一)

1 rtsp 协议后编码解码 rtsp协议的问题就是&#xff0c;拼接完成后&#xff0c;还需要编码&#xff0c;而unity里面再需要解码&#xff0c;需要的过程多了一步编码再解码&#xff0c;大大加重了 2 rtsp 协议后轻量编码 rtsp协议使用mjpeg进行图片传输。why&#xff1f;这样做…

vite + react + typescript + uni-app + node 开发一个生态系统

简介 使用 vite react typescript uni-app node 来开发一个简易的生态系统案例&#xff0c;包含 APP&#xff0c;H5&#xff0c;微信小程序&#xff0c;控制台&#xff0c;服务端 开发 admin 技术栈&#xff1a;vite react typescript初始化控制台项目选择自定义预设…

【C语言】指针的“最后一站”【进阶版】

欢迎各位看官^_^ 目录 1、字符指针 2、指针数组 3、数组指针 3.1数组指针的定义 3.2数组指针的使用 4、数组指针和指针数组的区别 5、数组参数&#xff0c;指针参数 5.1数组参数定义 5.2指针参数定义 5.3一维数组传参 5.4二维数组传参 5.5一级指针传参 5.6二级指…

《Docker 容器化的艺术:深入理解容器技术》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…

javaweb04-vue基础

话不多说&#xff0c;参考官网地址Vue官网集成Vue应用。 一、Vue快速入门 &#xff08;1&#xff09;新建HTML页面&#xff0c;引入Vue.js 我这里用的是CDN方式 <script src"https://unpkg.com/vue3/dist/vue.global.js"></script> &#xff08;2&am…

windows10系统下Python3.11中安装Numpy库教程

Python3.11中安装Numpy库目录 项目场景&#xff1a;问题描述解决方案&#xff1a;①下载Numpy文件②把NumPy文件放到Python安装的Scripts文件夹里。③安装numpy④安装验证 项目场景&#xff1a; numpy是开源的数值计算扩展&#xff0c;用于数据分析、机器学习、科学计算的重要…

【Java 基础篇】Java TreeSet 详解:红黑树实现的有序集合

Java 集合框架提供了多种数据结构&#xff0c;用于存储和操作数据。其中&#xff0c;TreeSet 是一种特殊类型的集合&#xff0c;它通过红黑树&#xff08;Red-Black Tree&#xff09;数据结构实现了有序的、唯一元素存储。本篇博客将深入探讨 TreeSet&#xff0c;包括其概念、特…

小程序中使用分包

前言 小程序在未使用的分包的情况下仅支持大小为2M,如果图片等资源过多的情况下可以使用分包功能&#xff0c;使用分包的情况下单个分包大小不能超过2M,总大小不能超过20M&#xff0c;分包有两种情况&#xff1a;普通分包和独立分包&#xff0c;下面介绍的是普通分包。官方文档…

《向量数据库指南》——哪些需求推动了如Milvus Cloud等的向量数据库的更新和迭代?

这个问题需要深入讨论大模型与向量数据库之间的关系。从去年 ChatGPT 推出时这个问题就开始引发我们的思考。在当时,我们敏锐地意识到这将是一个机遇。然而,在国内,这个概念的认知需要更长的时间。我个人在去年四五月份的美国之行中注意到,数据库在美国已经是一个非常热门的…

算法通关村第十九关:青铜-动态规划是怎么回事

青铜挑战-动态规划是怎么回事 动态规划&#xff08;简称DP&#xff0c;Dynamic Programming&#xff09;&#xff1a;最热门、最重要的算法之一。面试中大量出现&#xff0c;整体偏难。 1. 热身&#xff1a;重复计算和记忆化搜索&#xff08;如何说一万次"我爱你"&…

【LeetCode-中等题】59. 螺旋矩阵 II

文章目录 题目方法一&#xff1a;二维数组缩圈填数字方法二&#xff1a; 题目 方法一&#xff1a;二维数组缩圈填数字 定义四个边界条件&#xff0c;每转一圈&#xff0c;把数值填进去&#xff0c;然后缩小一圈&#xff0c;直到不满足条件位置 结束循环条件可以是&#xff1a; …

QML android 采集手机传感器数据 并通过udp 发送

利用 qt 开发 安卓 app &#xff0c;采集手机传感器数据 并通过udp 发送 #ifndef UDPLINK_H #define UDPLINK_H#include <QObject> #include <QUdpSocket> #include <QHostAddress>class UdpLink : public QObject {Q_OBJECT public:explicit UdpLink(QObjec…

使用ExcelJS快速处理Node.js爬虫数据

什么是ExcelJS ExcelJS是一个用于处理Excel文件的JavaScript库。它可以让你使用JavaScript创建、读取和修改Excel文件。 以下是ExcelJS的一些主要特点&#xff1a; 支持xlsx、xlsm、xlsb、xls格式的Excel文件。可以创建和修改工作表、单元格、行和列。可以设置单元格样式、字…

leetcode:67. 二进制求和

题目&#xff1a; 函数原型&#xff1a; char * addBinary(char * a, char * b) 思路&#xff1a; 二进制相加&#xff0c;首先我们考虑先将字符串逆序。由此要写一个逆序函数reserve。字符串逆序后&#xff0c;从前往后相加&#xff0c;以较长的字符串的长度为标准长度n&#…

可视化大屏设计模板 | 主题皮肤(报表UI设计)

下载使用可视化大屏设计模板&#xff0c;减少重复性操作&#xff0c;提高报表制作效率的同时也确保了报表风格一致&#xff0c;凸显关键数据信息。 软件&#xff1a;奥威BI系统&#xff0c;又称奥威BI数据可视化工具 所属功能板块&#xff1a;主题皮肤上传下载&#xff08;数…

Python实现机器学习(下)— 数据预处理、模型训练和模型评估

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本门课程将介绍人工智能相关概念&#xff0c;重点讲解机器学习原理机器基本算法&#xff08;监督学习及非监督学习&#xff09;。使用python&#xff0c;结合sklearn、Pycharm进行编程&#xff0c;介绍iris&#xff08;鸢尾…

Excel学习 WPS版

Excel学习 1.界面基础1.1 方格移动快捷键1.2 自动适配文字长度1.3 跨栏置中1.4 多个单元格同宽度&#xff1a;1.5 下拉框选择1.6 打印预览1.7 绘制边框1.8 冻结一行多行表头1.9 分割视图 2.日期相关2.1 今日日期快捷键2.2 月份提取 3.数学公式3.1 自动增长3.2 排序3.3 筛选3.4 …

数据结构——排序算法——冒泡排序

冒泡排序1 void swap(vector<int> arr, int i, int j) {int temp arr[i];arr[i] arr[j];arr[j] temp;}void bubbleSort1(vector<int> arr) {for (int i 0; i < arr.size() - 1; i){for (int j 0; j < arr.size() - 1 - i; j){if (arr[j] > arr[j 1…

【Unity编辑器扩展】| 顶部菜单栏扩展 MenuItem

前言【Unity编辑器扩展】 | 顶部菜单栏扩展 MenuItem一、创建多级菜单二、创建可使用快捷键的菜单项三、调节菜单显示顺序和可选择性四、创建可被勾选的菜单项五、右键菜单扩展5.1 Hierarchy 右键菜单5.2 Project 右键菜单5.3 Inspector 组件右键菜单六、AddComponentMenu 特性…