android 代码设置 键盘适应_Android EditText和Keyboard软键盘距离调整方案

不念不忘少年蓝@ 2023-01-03 14:19 529阅读 0赞

5aef0fc06330ba922c095199333e958e.png

效果:

b9ea130215cc8fa7389d1c8a5024d2f2.png

https://www.zhihu.com/video/1220293325850443776

今天来了个需求EditText和Keyboard保持一定的距离,让用户操作友好一些.

本着不懂就问的心态翻了一圈百度,说的最多的是EditText加个底部距离然后软键盘弹出就会距离EditText更远了.但是这个方案达不到我的需求因为APP太多EditText内容了,如果一个一个的修改,我是不能接受的.

然后把问题抛给了群友,热心的群友给了解答

思路如下:将Activity的R.id.content的布局做一个上移的动画效果 用来达到EditText和Keybooard软键盘保持距离的操作

先说一下我为什么采用这种方案:因为项目的架构统一处理了Activity的生命周期,所以我只要在统一处理生命周期的地方加入代码去改变一下整体布局的上移就可以达到效果,满足了我特别不想改一堆控件的诉求,节省了时间

然后借鉴了XPopup的KeyboardUtils处理方法经验 ,贴一下库,帮人推广,饮水思源

li-xiaojun/XPopup​github.com

c449bdc2a114defe75929f0f1d570db2.png

最后贴一下我的代码

这里要说明下 moveUpToKeyboard()方法里面是按照自己需求更改的,在这个项目里面因为我的Activity都是加了 android:windowSoftInputMode=”adjustPan” 标识的,所以键盘默认是会上移到EditText的文字下面的,所以我这边处理就是设置一个阈值,阈值代表的是键盘到EditText的距离.比如我这边的KE_VALUE

然后根据设置的值 做相应的判断

1.如果EditText不会遮挡则不执行动画

2.如果遮挡发生则adjust会上移一部分,那我们就按照KE_VALUE来上移

3.如果处于(0,KE_VALUE)区间则上移剩下的一部分距离即可

最后还判断了下EditText如果特别高,上移导致光标出屏幕的现象,这里需要保证EditText的top在navagation之下,代码仅供参考,还有很多地方值得优化大体思路是这样的:

  1. public final class KeyboardUtils {
  2. public static int sDecorViewInvisibleHeightPre;
  3. private static ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener;
  4. private static HashMap<View,OnSoftInputChangedListener> listenerMap = new HashMap<>();
  5. private KeyboardUtils() {
  6. throw new UnsupportedOperationException("u can't instantiate me...");
  7. }
  8. private static int sDecorViewDelta = 0;
  9. private static int getDecorViewInvisibleHeight(final Activity activity) {
  10. final View decorView = activity.getWindow().getDecorView();
  11. if (decorView == null) return sDecorViewInvisibleHeightPre;
  12. final Rect outRect = new Rect();
  13. decorView.getWindowVisibleDisplayFrame(outRect);
  14. int delta = Math.abs(decorView.getBottom() - outRect.bottom);
  15. if (delta <= getNavBarHeight()) {
  16. sDecorViewDelta = delta;
  17. return 0;
  18. }
  19. return delta - sDecorViewDelta;
  20. }
  21. /**
  22. * Register soft input changed listener.
  23. *
  24. * @param activity The activity.
  25. * @param listener The soft input changed listener.
  26. */
  27. public static void registerSoftInputChangedListener(final Activity activity, final OnSoftInputChangedListener listener) {
  28. final int flags = activity.getWindow().getAttributes().flags;
  29. if ((flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) != 0) {
  30. activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
  31. }
  32. final FrameLayout contentView = activity.findViewById(android.R.id.content);
  33. sDecorViewInvisibleHeightPre = getDecorViewInvisibleHeight(activity);
  34. listenerMap.put(contentView, listener);
  35. onGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
  36. @Override
  37. public void onGlobalLayout() {
  38. int height = getDecorViewInvisibleHeight(activity);
  39. if (sDecorViewInvisibleHeightPre != height) {
  40. //通知所有弹窗的监听器输入法高度变化了
  41. for (OnSoftInputChangedListener changedListener: listenerMap.values()) {
  42. changedListener.onSoftInputChanged(height);
  43. }
  44. sDecorViewInvisibleHeightPre = height;
  45. }
  46. }
  47. };
  48. contentView.getViewTreeObserver()
  49. .addOnGlobalLayoutListener(onGlobalLayoutListener);
  50. }
  51. public static void removeLayoutChangeListener(View decorView, View popupView){
  52. onGlobalLayoutListener = null;
  53. if(decorView==null)return;
  54. View contentView = decorView.findViewById(android.R.id.content);
  55. if(contentView==null)return;
  56. contentView.getViewTreeObserver().removeGlobalOnLayoutListener(onGlobalLayoutListener);
  57. listenerMap.remove(popupView);
  58. }
  59. private static int getNavBarHeight() {
  60. Resources res = Resources.getSystem();
  61. int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
  62. if (resourceId != 0) {
  63. return res.getDimensionPixelSize(resourceId);
  64. } else {
  65. return 0;
  66. }
  67. }
  68. public static void showSoftInput(View view) {
  69. InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
  70. imm.showSoftInput(view, InputMethodManager.SHOW_FORCED);
  71. }
  72. public static void hideSoftInput(View view) {
  73. InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
  74. imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
  75. }
  76. public interface OnSoftInputChangedListener {
  77. void onSoftInputChanged(int height);
  78. }
  79. //Keyboard到EditText的距离
  80. public final static int KE_VALUE = AutoSizeUtils.dp2px(Utils.getApp(),100);
  81. public static void moveUpToKeyboard(int keyboardHeight, Activity activity) {
  82. FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
  83. //找到所有输入框
  84. ArrayList<EditText> allEts = new ArrayList<>();
  85. findAllEditText(allEts, content);
  86. EditText focusEt = null;
  87. //找到焦点EditText
  88. for (EditText et : allEts) {
  89. if (et.isFocused()) {
  90. focusEt = et;
  91. break;
  92. }
  93. }
  94. //dy是整个屏幕需要上移的高度 需求y1 = 软键盘高度+EditText在屏幕的高度+阈值(键盘距离目标EditText保持的位置)
  95. int dy = 0;
  96. int windowHeight = getWindowHeight(activity);
  97. int focusEtTop = 0;
  98. int focusBottom = 0;
  99. if (focusEt != null) {
  100. int[] locations = new int[2];
  101. //找到焦点EditText的屏幕位置
  102. focusEt.getLocationOnScreen(locations);
  103. focusEtTop = locations[1];
  104. focusBottom = focusEtTop + focusEt.getMeasuredHeight();
  105. }
  106. //屏幕顶点(坐标原点)到 焦点EditText 底部的距离 = focusBottom的值 + keyboardHeight(监听键盘变化的值)
  107. //+自定义的阈值 - 屏幕的高度 = dy
  108. int overflowHeight = (focusBottom + keyboardHeight) + KE_VALUE - windowHeight;
  109. if(overflowHeight < 0){
  110. //焦点比较高不考虑
  111. dy = overflowHeight;
  112. }else if(overflowHeight >= KE_VALUE){
  113. overflowHeight = KE_VALUE;
  114. }else{
  115. //当overflowHeight 在(0,KE_VALUE)区间时候
  116. dy = overflowHeight;
  117. }
  118. if (focusEt != null && overflowHeight > 0) {
  119. dy = overflowHeight;
  120. }
  121. //最后计算出来动画的偏移高度 这里需要加一个判断如果EditText特别的高,会因为我们的动画上移屏幕导致光标看不到了.
  122. //解决方案就是把上移动画 高度最后再修正一下 根据EditText的Top来计算
  123. int etMinHeight = focusEtTop - getNavBarHeight();//上移的最低距离
  124. dy = Math.min(etMinHeight,dy);
  125. //dy<0说明没有需要变化
  126. if (dy < 0) return;
  127. content.animate().translationY(-dy)
  128. .setDuration(200)
  129. .setInterpolator(new OvershootInterpolator(0))
  130. .start();
  131. }
  132. public static int getWindowWidth(Context context) {
  133. return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
  134. }
  135. public static int getWindowHeight(Context context) {
  136. return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight();
  137. }
  138. public static int dp2px(Context context, float dipValue) {
  139. final float scale = context.getResources().getDisplayMetrics().density;
  140. return (int) (dipValue * scale + 0.5f);
  141. }
  142. public static int getStatusBarHeight() {
  143. Resources resources = Resources.getSystem();
  144. int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
  145. return resources.getDimensionPixelSize(resourceId);
  146. }
  147. public static void findAllEditText(ArrayList<EditText> list, ViewGroup group) {
  148. for (int i = 0; i < group.getChildCount(); i++) {
  149. View v = group.getChildAt(i);
  150. if (v instanceof EditText && v.getVisibility() == View.VISIBLE) {
  151. list.add((EditText) v);
  152. } else if (v instanceof ViewGroup) {
  153. findAllEditText(list, (ViewGroup) v);
  154. }
  155. }
  156. }
  157. public static void registerKeyboard(Activity activity){
  158. FrameLayout contentView = (FrameLayout) activity.findViewById(android.R.id.content);
  159. KeyboardUtils.registerSoftInputChangedListener(activity, new KeyboardUtils.OnSoftInputChangedListener() {
  160. @Override
  161. public void onSoftInputChanged(int height) {
  162. Log.d("onSoftInputChanged","height ="+ height);
  163. if (height == 0) {
  164. // 说明对话框隐藏
  165. //没有变化的软键盘
  166. contentView.animate().translationY(0)
  167. .setInterpolator(new OvershootInterpolator(0))
  168. .setDuration(200).start();
  169. } else {
  170. //发生了变化的软键盘
  171. //when show keyboard, move up
  172. moveUpToKeyboard(height, activity);
  173. }
  174. }
  175. });
  176. }
  177. public static void unregisterKeyboard(Activity activity){
  178. FrameLayout contentView = (FrameLayout) activity.findViewById(android.R.id.content);
  179. if(contentView==null)return;
  180. contentView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
  181. listenerMap.remove(contentView);
  182. }
  183. }

发表评论

表情:
评论列表 (有 0 条评论,529人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android隐藏键盘

    网上好多方法说的隐藏方法,其实是隐藏/显示方法,即,当前键盘显示,调用一下,隐藏,在调用一下,又显示了。下面提供两种彻底隐藏的方法: / 软键盘显