成都网站建设07fly,上传视频网站开发,免费下优化大师,莆田做外贸网站平台
RK3288 Android 8.1 显示: 1920x1080 160 dpi
概述
碰到一个问题#xff1a; 弹出的输入法会覆盖文本输入框。 原因#xff1a;输入框使用了setTranslationY() 位置偏移后#xff0c; 输入法无法正确获取焦点的位置。
分析
先上图: 初始布局 调用etTranslation…平台
RK3288 Android 8.1 显示: 1920x1080 160 dpi
概述
碰到一个问题 弹出的输入法会覆盖文本输入框。 原因输入框使用了setTranslationY() 位置偏移后 输入法无法正确获取焦点的位置。
分析
先上图: 初始布局 调用etTranslationY(700); 弹出输入法 最后一张图中 输入框大概在红框的位置 也是本文所描述的问题 输入法遮挡了输入框控件 布局
?xml version1.0 encodingutf-8?
RelativeLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:orientationverticalandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentLinearLayout android:idid/llEditandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:orientationverticalTextViewandroid:idid/tvandroid:textSize28spandroid:gravitycenterandroid:text--------------FOOTER---------------------android:layout_widthmatch_parentandroid:layout_heightwrap_content/EditText android:idid/etandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:textSize36sp/TextViewandroid:idid/tv2android:textSize28spandroid:gravitycenterandroid:text--------------HEADER---------------------android:layout_widthmatch_parentandroid:layout_heightwrap_content//LinearLayoutButton android:idid/btTyandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_centerInParenttrueandroid:textTranslationY/
/RelativeLayoutjava
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
//未打开输入法
getCurrY0,mScrollY0,mCurScrollY0//打开输入法
getCurrY372,mScrollY372,mCurScrollY372准确地说是滚上去的 #mermaid-svg-LKNdHuJSmtlrHcwY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY .error-icon{fill:#552222;}#mermaid-svg-LKNdHuJSmtlrHcwY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LKNdHuJSmtlrHcwY .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-LKNdHuJSmtlrHcwY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LKNdHuJSmtlrHcwY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LKNdHuJSmtlrHcwY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LKNdHuJSmtlrHcwY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LKNdHuJSmtlrHcwY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LKNdHuJSmtlrHcwY .marker.cross{stroke:#333333;}#mermaid-svg-LKNdHuJSmtlrHcwY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LKNdHuJSmtlrHcwY .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LKNdHuJSmtlrHcwY text.actortspan{fill:black;stroke:none;}#mermaid-svg-LKNdHuJSmtlrHcwY .actor-line{stroke:grey;}#mermaid-svg-LKNdHuJSmtlrHcwY .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY .sequenceNumber{fill:white;}#mermaid-svg-LKNdHuJSmtlrHcwY #sequencenumber{fill:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY .messageText{fill:#333;stroke:#333;}#mermaid-svg-LKNdHuJSmtlrHcwY .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LKNdHuJSmtlrHcwY .labelText,#mermaid-svg-LKNdHuJSmtlrHcwY .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-LKNdHuJSmtlrHcwY .loopText,#mermaid-svg-LKNdHuJSmtlrHcwY .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-LKNdHuJSmtlrHcwY .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LKNdHuJSmtlrHcwY .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-LKNdHuJSmtlrHcwY .noteText,#mermaid-svg-LKNdHuJSmtlrHcwY .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-LKNdHuJSmtlrHcwY .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LKNdHuJSmtlrHcwY .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LKNdHuJSmtlrHcwY .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LKNdHuJSmtlrHcwY .actorPopupMenu{position:absolute;}#mermaid-svg-LKNdHuJSmtlrHcwY .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-LKNdHuJSmtlrHcwY .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LKNdHuJSmtlrHcwY .actor-man circle,#mermaid-svg-LKNdHuJSmtlrHcwY line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-LKNdHuJSmtlrHcwY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 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) {// Well assume that we arent 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 hasnt 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 WeakReferenceView(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 cant 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 cant 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 w1920 h1080 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 aid1073741824}: 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; scrollY372
Pan scroll changed: old0 , new372//不弹
Eval scroll: focusandroid.widget.EditText{40fcecd VFED..CL. .F..H.I. 0,828-1920,898 #7f03000a app:id/et aid1073741824} rectanglenull ciRect(0, 24 -
Need to scroll?
Root w1920 h1080 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 aid1073741824}: focusRect[2,10][6,70]
Focus in window: focusRect[2,926][6,986] visRect[0,846][1920,916]if (mTempRect.intersect(mVisRect)) 对应的两个矩形:
focusRect[2,926][6,986] visRect[0,916][1920,986] - 弹focusRect[2,926][6,986] visRect[0,846][1920,916] - 不弹无交集
参考
Android软键盘弹出时把布局顶上去的解决方法 Android EditText默认不弹出输入法的实现方法 5种方法完美解决android软键盘挡住输入框方法详解 Android输入法弹出流程