Keyboard 软键盘阻挡输入框爬坑指南
来源:互联网 发布:2016年网络群体性事件 编辑:程序博客网 时间:2024/06/16 08:21
导读:
日常开发中我们经常会用到EditText输入框,但有时我们的输入框会出现被软键盘界面阻挡,那么我们就会想到设置android:windowSoftInputMode属性
但是,当我们用的正爽的时候,又会出现什么布局上移,ba..ba..ba的bug
因此,本篇将针对个人在开发中遇到的”软键盘阻挡输入框”问题介绍
android:windowSoftInputMode属性说明
这个属性能影响两个事情:
- 当有焦点产生时,软键盘是隐藏还是显示
- 是否减少活动主窗口大小以便腾出空间放软键盘
它的设置必须是下面的一个值,或者”state…| adjust..”
正常情况来说,如果要防止软键盘挡住输入框如上表设置属性就能解决…
代码实现:
//adjustPangetWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);//adjustResizegetWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
清单文件对应节点实现:
<!-- 静态设置输入模式 --> <!-- android:windowSoftInputMode="adjustResize" --> <activity android:name=".activity.AdjustResize_KeyboardActivity" android:windowSoftInputMode="adjustResize"/> <!-- android:windowSoftInputMode="adjustPan" --> <activity android:name=".activity.AdjustPan_KeyboardActivity" android:windowSoftInputMode="adjustPan"/>
部分属性应用场景说明:
- 使用adjustPan,如果有几个EditText,当点击第一个EditText时,下方的EditText会被软键盘界面覆盖
- 使用adjustPan,如果输入框在软键盘后面,软键盘会把输入框以上的布局(包括ToolBar)挤出屏幕外
- 使用adjustResize,当使用LinearLayout作为输入框的父布局时,软键盘会覆盖输入框
- 使用adjustResize,当控件设置了background属性,或设置了权重的imageView会发生变形,该使用adjustPan
开发中遇到的Bug,以及解决方案:
设置了沉浸式/透明状态栏后,adjustResize失效问题
在根布局或者父布局设置android:fitsSystemWindows=”true”即可解决
“全屏模式”使用adjustResize无效,而且会类似adjustPan把上方布局挤出屏幕外
官方认为”全屏模式”指的是App自己接管了状态栏的控制,如使用了Fullscreen主题、使用了『状态色着色』、『沉浸式状态栏』、『Immersive Mode』等等
个人开发也有一段时间,由于很少遇到需要全屏(状态栏也隐藏)的应用,真说有的话,那就是游戏里的,不过一旦设置Fullscreen主题一类的全屏,点击EditText输入框会跳转到一个全屏模式的输入界面,因此这里就不作处理了,有兴趣的同学可以根据以下代码深入研究下,这里就不赘述了.使用:(最终效果类似adjustResize)1.把AndroidBug5497Workaround类复制到项目中2.在需要填坑的activity的onCreate方法中添加一句AndroidBug5497Workaround.assistActivity(this)即可。----------------------------------------------------/* * @本类描述 网上收集的,解决"全屏模式"下,adjustResize失效方案 * * @内容说明 解决: * 1.非全屏模式下使用adjustPan无效问题 * 2.全屏模式下使用adjustPan和adjustResize无效问题 * * @补充内容 有兴趣的同学可以学下里面的思路,自己写一个解决方案(*^__^*) 嘻嘻……) * * --------------------------------- * @更新时间 * @新增内容 * */public class AndroidBug5497Workaround { // For more information, see https://code.google.com/p/android/issues/detail?id=5497 // To use this class, simply invoke assistActivity() on an Activity that already has its content view set. public static void assistActivity(Activity activity) { new AndroidBug5497Workaround(activity); } private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private AndroidBug5497Workaround(Activity activity) { //拿到当前XML文件的根布局 FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); //监听当前View的状态,进行通知回调,即"软键盘弹出"" mChildOfContent = content.getChildAt(0); mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { public void onGlobalLayout() { possiblyResizeChildOfContent(); } }); frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); } /** * 重新设置高度 * <p> * 把界面高度设置为可用高度 */ private void possiblyResizeChildOfContent() { int usableHeightNow = computeUsableHeight(); if (usableHeightNow != usableHeightPrevious) { // int usableHeightSansKeyboard = activity.getWindowManager().getDefaultDisplay().getHeight();//获取屏幕尺寸,不包括虚拟功能高度 用这个可以完美解决 //findViewById(android.R.id.content).getMeasuredHeight() 也可以解决虚拟按键问题 int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; //排除其他View引起的变化,专注软键盘变化 if (heightDifference > (usableHeightSansKeyboard / 4)) { // keyboard probably just became visible frameLayoutParams.height = usableHeightSansKeyboard - heightDifference; } else { // keyboard probably just became hidden frameLayoutParams.height = usableHeightSansKeyboard; } mChildOfContent.requestLayout(); usableHeightPrevious = usableHeightNow; } } /** * 软键盘弹出后,可以显示内容的高度 * * @return */ private int computeUsableHeight() { Rect r = new Rect(); //这行代码能够获取到去除标题栏和被软键盘挡住的部分,所剩下的矩形区域 mChildOfContent.getWindowVisibleDisplayFrame(r); //r.top : 标题栏的高度 //屏幕高度-r.bottom : 软键盘的高度 //可用高度(全屏模式) : rect.bottom //可用高度(非全屏模式) : rect.bottom - rect.top return (r.bottom - r.top);// 全屏模式下: return r.bottom }}
工具类图解
一些”奇淫”技巧
有时候,我们需要将想要的Button按钮也显示在软键盘上面,如登录页面,如果我们只设置了adjustPan或adjustResize,那么Button按钮就会被挡住,体验不好.下面介绍几种解决方案
其实最简单的方式是将布局嵌套在ScrollView里,但体验一样不好,操作多了一步嘛
一、工具类方法(监听软键盘弹出位置,让我们的布局滑动到合适的位置)
Utils类
public class NestedScrollView_KeyboardUtils { private static final String TAG = "NestedScrollView_KeyboardUtils"; /** * @param activity 上下文 * @param viewId NestedScrollView_ID */ public static void assistActivity(Activity activity, int viewId) { new NestedScrollView_KeyboardUtils(activity, viewId); } private View mChildOfContent; /**android.support.v4包中,新版的ScrollView**/ private NestedScrollView mScrollView; private NestedScrollView_KeyboardUtils(Activity activity, int viewId) { //拿到当前XML文件的根布局 FrameLayout content = (FrameLayout) activity .findViewById(android.R.id.content); //监听当前View的状态,进行通知回调,即"软键盘弹出"" mChildOfContent = content.getChildAt(0); mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { possiblyResizeChildOfContent(); } }); mScrollView = (NestedScrollView) content.findViewById(viewId); } private void possiblyResizeChildOfContent() { int contentHeight = mChildOfContent.getRootView().getHeight(); int curDisplayHeight = computeUsableHeight(); if (contentHeight - curDisplayHeight > contentHeight / 4) { Log.e(TAG, "possiblyResizeChildOfContent: 1"); mScrollView.scrollTo(0, 600); // mScrollView.fullScroll(ScrollView.FOCUS_DOWN); } else { Log.e(TAG, "possiblyResizeChildOfContent: 2"); } } /** * 软键盘弹出后,获取屏幕可显示区域高度 * * @return */ private int computeUsableHeight() { Rect r = new Rect(); //这行代码能够获取到去除标题栏和被软键盘挡住的部分,所剩下的矩形区域 mChildOfContent.getWindowVisibleDisplayFrame(r); //r.top : 标题栏的高度 //屏幕高度-r.bottom : 软键盘的高度 //可用高度(全屏模式) : rect.bottom //可用高度(非全屏模式) : rect.bottom - rect.top return r.height(); }}
布局文件
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:clipToPadding="true" > <android.support.v4.widget.NestedScrollView android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/ll_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="200dp" android:background="#0000ff"/> <EditText android:id="@+id/account" android:layout_width="match_parent" android:layout_height="match_parent"/> <EditText android:layout_width="match_parent" android:layout_height="match_parent"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="确定"/> </LinearLayout> </android.support.v4.widget.NestedScrollView></LinearLayout>
主页面
// 改动过状态栏的状态,记得在XML布局文件根布局设置android:fitsSystemWindows="true"public class ScrollViewByClassActivity extends AppCompatActivity { private LinearLayout mParent; private EditText account; private NestedScrollView scroll; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_scroll_class); //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏 StatusBar.setStatusBarColor(this, Color.BLUE); mParent = (LinearLayout) findViewById(R.id.ll_parent); account = (EditText) findViewById(R.id.account); scroll = (NestedScrollView) findViewById(R.id.scroll_view); setScroll(); } //键盘不遮挡按钮 private void setScroll() { NestedScrollView_KeyboardUtils.assistActivity(this, R.id.scroll_view); //这个是别人给我的工具类,只用这个会有 hideKeyboard(); scroll.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { //scroll为parent外面那层布局()最好用NestedScrollView,ScrollView会有版本问题 @Override public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { v.smoothScrollTo(0, 450); //这个是滑动距离,随便大一点就好 } }); } /** * EditText失去焦点,隐藏软键盘 */ private void hideKeyboard() { mParent.setOnTouchListener(new View.OnTouchListener() { //parent为Editext外面那层布局 @Override public boolean onTouch(View v, MotionEvent event) { mParent.setFocusable(true); mParent.setFocusableInTouchMode(true); mParent.requestFocus(); InputMethodManager imm = (InputMethodManager) ScrollViewByClassActivity.this .getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(account.getWindowToken(), 0); //隐藏键盘,account为Editext,随便一个就好 return false; } }); }}
效果图
二、自定义View作为我们的根布局,监听软键盘弹出状态,将我们的布局滑动到合适位置
自定义View
public class KeyboardLayout extends FrameLayout { private KeyboardLayoutListener mListener; private boolean mIsKeyboardActive = false; //输入法是否激活 private int mKeyboardHeight = 0; // 输入法高度 public KeyboardLayout(Context context) { this(context, null, 0); } public KeyboardLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // 监听布局变化 getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardOnGlobalChangeListener()); } public void setKeyboardListener(KeyboardLayoutListener listener) { mListener = listener; } public KeyboardLayoutListener getKeyboardListener() { return mListener; } public boolean isKeyboardActive() { return mIsKeyboardActive; } /** * 测试弹出输入法时,系统做了什么 **/ private static int count = 0; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.e("onSizeChanged " + count++, "=>onResize called! w=" + w + ",h=" + h + ",oldw=" + oldw + ",oldh=" + oldh); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); Log.e("onLayout " + count++, "=>OnLayout called! l=" + l + ", t=" + t + ",r=" + r + ",b=" + b); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Log.e("onMeasure " + count++, "=>onMeasure called! widthMeasureSpec=" + widthMeasureSpec + ", heightMeasureSpec=" + heightMeasureSpec); } /** * 获取输入法高度 * * @return */ public int getKeyboardHeight() { return mKeyboardHeight; } public interface KeyboardLayoutListener { /** * @param isActive 输入法是否激活 * @param keyboardHeight 输入法面板高度 */ void onKeyboardStateChanged(boolean isActive, int keyboardHeight); } private class KeyboardOnGlobalChangeListener implements ViewTreeObserver.OnGlobalLayoutListener { int mScreenHeight = 0; private int getScreenHeight() { if (mScreenHeight > 0) { return mScreenHeight; } mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay().getHeight(); return mScreenHeight; } @Override public void onGlobalLayout() { Rect rect = new Rect(); // 获取当前页面窗口的显示范围 ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); int screenHeight = getScreenHeight(); int keyboardHeight = screenHeight - rect.bottom; // 输入法的高度 boolean isActive = false; if (Math.abs(keyboardHeight) > screenHeight / 4) { isActive = true; // 超过屏幕五分之一则表示弹出了输入法 mKeyboardHeight = keyboardHeight; } mIsKeyboardActive = isActive; if (mListener != null) { mListener.onKeyboardStateChanged(isActive, keyboardHeight); } } }}
布局文件
<?xml version="1.0" encoding="utf-8"?><zs.xmx.view.KeyboardLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/keyboardview" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" > <ScrollView android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="200dp" android:background="#00f0ff"/> <EditText android:id="@+id/account" android:layout_width="match_parent" android:layout_height="match_parent"/> <EditText android:layout_width="match_parent" android:layout_height="match_parent"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="确定"/> </LinearLayout> </ScrollView></zs.xmx.view.KeyboardLayout>
主页面
public class ScrollViewByLayoutActivity extends AppCompatActivity { private ScrollView scroll; private KeyboardLayout mKeyboardLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // requestWindowFeature(Window.FEATURE_NO_TITLE); // 去除标题 必须在setContentView()方法之前调用 // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏 setContentView(R.layout.activity_scroll_layout); StatusBar.setStatusBarColor(this, Color.GRAY); mKeyboardLayout = (KeyboardLayout) findViewById(R.id.keyboardview); scroll = (ScrollView) findViewById(R.id.scroll_view); addLayoutListener(); } /** * 监听键盘状态,布局有变化时,靠scrollView去滚动界面 */ public void addLayoutListener() { mKeyboardLayout.setKeyboardListener(new KeyboardLayout.KeyboardLayoutListener() { @Override public void onKeyboardStateChanged(boolean isActive, int keyboardHeight) { Log.e("onKeyboardStateChanged", "isActive:" + isActive + " keyboardHeight:" + keyboardHeight); if (isActive) { scrollToBottom(); } } }); } /** * 弹出软键盘时将SVContainer滑到底 */ private void scrollToBottom() { scroll.postDelayed(new Runnable() { @Override public void run() { scroll.smoothScrollTo(0, scroll.getBottom() + getStatusBarHeight(ScrollViewByLayoutActivity.this)); } }, 100); } /** * 获取状态栏高度 * * @param activity * @return */ public static int getStatusBarHeight(Activity activity) { //获取状态栏的高度 int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); return activity.getResources().getDimensionPixelSize(resourceId); }}
效果图:
总结:
- 如果EditText的位置写死在软键盘下方,会被覆盖.同理如果不想它移动可以写死,但是要注意屏幕适配问题
- 有些定制的键盘有精简模式,还有全屏/数字键盘大小不一样,可能有问题(目前没遇到)
- 若像我们登录界面,需要把登录按钮也上移,用scrollTo + 布局监听软键盘(即上面两种方案)
- 若有多个输入框,没什么特殊要求,使用ScrollView + adjustResize,基本能够满足开发需求
- 设置了沉浸式/透明状态栏,记得在布局文件设置android:fitsSystemWindows=”true”属性
本篇测试Demo:
Demo源码: keyboard_Demo
Keyboard 相关文章链接
Keyboard 输入框与软键盘联动
keyboard 动态启动或关闭软键盘
本篇文章到此结束,欢迎关注,后续有补充的会即时更新,有问题也欢迎评论,共同成长
待续:深入学下测量原理,写一个适配全屏的工具类
阅读全文
2 0
- Keyboard 软键盘阻挡输入框爬坑指南
- Keyboard 输入框与软键盘联动
- UITextField 与 keyboard (软键盘)
- IOS 软键盘灵活使用KeyBoard库
- keyboard 动态启动或关闭软键盘
- 软键盘输入
- js实现软键盘输入
- android软键盘输入,windowSoftInputMode
- android监听软键盘输入@
- 手写输入和软键盘输入
- Android软键盘输入详解
- 软键盘输入最佳实践
- Android软键盘输入imeOptions
- 股票输入软键盘,android初探自定义软键盘
- iOS解决键盘阻挡输入框
- 仿建设银行软键盘密码输入器
- js版 软键盘输入小例子
- 软键盘挡住输入框的解决方案
- Git零基础入门,简单明了实用
- 链表的概念以及原理 插入和删除元素
- poj1877 Flooded!(模拟)
- 如何将监控画面实时嵌入到微信公众号中进行直播
- PAT1008 Elevator (20)
- Keyboard 软键盘阻挡输入框爬坑指南
- php opencart config
- Spark之 DataFrame
- 问题及解决:uWSGI listen queue 队列溢出
- sqlserver存储过程学习(通俗易懂)
- Hadoop集群共享给多用户
- Redis异常分析-输出缓冲区过大
- R语言计算频数和频率
- 【kubernetes/k8s源码分析】kubelet源码分析-statusManager与probeManager