如何解决软键盘弹出引起的各种不适

本文介绍了如何在Android中通过配置窗口参数解决登录和注册页面中软键盘遮挡输入框的问题,包括使用ScrollView作为根布局、定义View分配空间以及配置windowSoftInputMode属性。通过案例分析,展示了如何实现输入框在软键盘显示时不被遮挡,同时保持界面的美观和用户体验。

在做登录和注册页面的时候,经常会遇到诸如软键盘挡住输入框的情况,android为此提供了一系列的的配置参数供选择,你可以在androidmanufist.xml的对应Activity的windowSoftInputMode属性中选择如下4者之一进行配置(紫色字):

int SOFT_INPUT_ADJUST_NOTHING

Adjustment option forsoftInputMode:

set to have a window not adjust for a shown input method.

int SOFT_INPUT_ADJUST_PAN

Adjustment option forsoftInputMode:

set to have a window pan when an input method is shown,so it doesn't need to deal with resizing but just panned by

the framework to ensure the current input focus is visible.

int SOFT_INPUT_ADJUST_RESIZE

Adjustment option forsoftInputMode:

set to allow the window to be resized when an input method is shown,so that its contents are not covered by the input method.

int SOFT_INPUT_ADJUST_UNSPECIFIED

Adjustment option forsoftInputMode:

nothing specified.

<activity android:name=".LoginAc"

android:label="@string/app_name"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

四个参数意思如下:

SOFT_INPUT_ADJUST_NOTHING: 不调整(输入法完全直接覆盖住,未开放此参数)

SOFT_INPUT_ADJUST_PAN: 把整个Layout顶上去露出获得焦点的EditText,不压缩多余空间,见图1

SOFT_INPUT_ADJUST_RESIZE: 整个Layout重新编排,重新分配多余空间,见图2

SOFT_INPUT_ADJUST_UNSPECIFIED: 系统自己根据内容自行选择上两种方式的一种执行(默认配置)

这里的多余空间指的是控件们通过weight分配机制得到的额外空间。

图1

图2

图3

代码实现方式为:

Java代码 收藏代码
  1. getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

但是,这样的配置方法一般都很难完全满足需要,有得应用会做得比较好,让顶上去的Layout能够通过scrollbar滚动。这种解决方法网上有各种介绍,本人也是第一时间从网上找解决方法参考,但最终发现都并未把原理说清,而且大多数有错误,或者有多余配置,于是,我从android系统中源码中找参考案例,在Email应用中,找到了我想要的。效果如图4,5。

图4

图5

其对应的Activity是AccountSetupBasics.java,对应的xml文件为account_setup_basics.xml。

来学习下它的xml写法:

Java代码 收藏代码
  1. <?xmlversion="1.0"encoding="utf-8"?>
Java代码 收藏代码
  1. <ScrollViewxmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent"
  4. android:fillViewport="true"
  5. android:scrollbarStyle="outsideInset">
  6. <LinearLayout
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent"
  9. android:orientation="vertical">
  10. <LinearLayout
  11. android:layout_width="fill_parent"
  12. android:layout_height="wrap_content"
  13. android:layout_weight="1"
  14. android:orientation="vertical">
  15. <TextView
  16. android:id="@+id/instructions"
  17. android:layout_width="fill_parent"
  18. android:layout_height="wrap_content"
  19. android:layout_marginTop="10dip"
  20. android:textSize="20sp"
  21. android:text="@string/accounts_welcome"
  22. android:textColor="?android:attr/textColorPrimary"/>
  23. <View
  24. android:layout_width="fill_parent"
  25. android:layout_height="0dip"
  26. android:layout_weight="1"/>
  27. <EditText
  28. android:id="@+id/account_email"
  29. android:hint="@string/account_setup_basics_email_hint"
  30. android:inputType="textEmailAddress"
  31. android:imeOptions="actionNext"
  32. android:layout_height="wrap_content"
  33. android:layout_width="fill_parent"/>
  34. <EditText
  35. android:id="@+id/account_password"
  36. android:hint="@string/account_setup_basics_password_hint"
  37. android:inputType="textPassword"
  38. android:imeOptions="actionDone"
  39. android:layout_height="wrap_content"
  40. android:layout_width="fill_parent"
  41. android:nextFocusDown="@+id/next"/>
  42. <CheckBox
  43. android:id="@+id/account_default"
  44. android:layout_height="wrap_content"
  45. android:layout_width="fill_parent"
  46. android:text="@string/account_setup_basics_default_label"
  47. android:visibility="gone"/>
  48. <View
  49. android:layout_width="fill_parent"
  50. android:layout_height="0dip"
  51. android:layout_weight="1"/>
  52. </LinearLayout>
  53. <RelativeLayout
  54. android:layout_width="fill_parent"
  55. android:layout_height="54dip"
  56. android:background="@android:drawable/bottom_bar">
  57. <Button
  58. android:id="@+id/manual_setup"
  59. android:text="@string/account_setup_basics_manual_setup_action"
  60. android:layout_height="wrap_content"
  61. android:layout_width="wrap_content"
  62. android:minWidth="@dimen/button_minWidth"
  63. android:layout_alignParentLeft="true"
  64. android:layout_centerVertical="true"/>
  65. <Button
  66. android:id="@+id/next"
  67. android:text="@string/next_action"
  68. android:layout_height="wrap_content"
  69. android:layout_width="wrap_content"
  70. android:minWidth="@dimen/button_minWidth"
  71. android:drawableRight="@drawable/button_indicator_next"
  72. android:layout_alignParentRight="true"
  73. android:layout_centerVertical="true"/>
  74. </RelativeLayout>
  75. </LinearLayout>
  76. </ScrollView>

1 它完全把ScrollView作为了一个根Layout,而不是网上好多文章写的在一个Linearlayout里面嵌入一个ScrollView(貌似这种是行不通的)。

然后把我们原来的根Layout搬入ScrollView(ScrollView只能有一个子控件),我查了下androidmanifist.xml和代码,未做任何以上2种方法的配置。

2 它定义了2个0dip的View帮助分配空间(设置其weight吃掉剩余空间,保证输入框处于界面中心位置),可以猜测出这里系统调用的是SOFT_INPUT_ADJUST_RESIZE参数,当所有有实际内容的控件空间总和超出特定范围时,ScrollView开始发挥作用。

如此,完美的解决我们遇到的问题。

另外,网上有人说想用SOFT_INPUT_ADJUST_RESIZE,但又不希望背景图片被压缩,只要按如上方法把Linearlayout的背景图片设置好即可。

package com.boncfc.mgrapp.fhVideo.teller; import android.app.Activity; import android.app.Notification; import android.app.ProgressDialog; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.RelativeLayout; import androidx.appcompat.app.AppCompatActivity; import com.boncfc.mgrapp.R; import com.fhuvideo.FHLiveMacro; import com.fhuvideo.FHLiveSessionParams; import com.fhuvideo.FHParams; import com.fhuvideo.adviser.FHTellerParams; import com.fhuvideo.adviser.tool.FHAPlayer; import com.fhuvideo.bank.FHPermission; import com.fhuvideo.bean.FHLog; import com.fhvideo.bankui.FHLivelisenter; import com.fhvideo.bankui.FHVideoManager; import com.fhvideo.bankui.bean.FHUIEvent; import com.fhvideo.bankui.call.FHCallActivity; import com.fhvideo.bankui.live.FHLiveActivity; import com.fhvideo.bankui.util.ToastUtil; import com.fhvideo.fhcommon.FHBusiCallBack; public class MainFhActivity extends AppCompatActivity { private static final String TAG = "MainFhActivity"; private Activity activity; private boolean isMetting = false; private ProgressDialog pd; private RelativeLayout outViewGroup; private WebView outWebView; private Handler mHandler = new Handler(); // 状态跟踪变量(新增) public static boolean isLiveActive = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 修改点1:设置透明背景避免白屏 getWindow().setBackgroundDrawable(null); setContentView(new View(this)); getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); activity = this; FHLiveSessionParams.setImChannelType(FHTellerParams.TX); setNoti(); initPD(); FHPermission.getInstance().checkPermission(activity); login(); } private void setNoti() { NotifyObject notifyObject = new NotifyObject(); notifyObject.content = "投屏内容"; notifyObject.title = "投屏标题"; notifyObject.subText = ""; notifyObject.param = ""; Notification notification = NotificationUtil.createScreenNotification(getApplicationContext(), notifyObject); //设置投屏通知 FHVideoManager.getInstance().setScreenNotification(notification); NotifyObject LivennotifyObject = new NotifyObject(); LivennotifyObject.content = "会话内容"; LivennotifyObject.title = "会话标题"; LivennotifyObject.subText = ""; LivennotifyObject.param = ""; Notification Livenotification = NotificationUtil.createScreenNotification(getApplicationContext(), LivennotifyObject); FHVideoManager.getInstance().setLiveForegroundNotification(Livenotification); } private void initPD() { pd = new ProgressDialog(activity); pd.setMessage("加载中..."); pd.setCanceledOnTouchOutside(false); } private void showPd() { if (!pd.isShowing()) { pd.show(); } } private void cancelPd() { if (pd.isShowing()) pd.cancel(); } private void login() { if (!FHTellerParams.isConnected()) { ToastUtil.getInatance(activity).show(getString(R.string.fh_not_net)); return; } String uid = getIntent().getStringExtra("tellerUid"); String pwd = getIntent().getStringExtra("password"); String dept = "0_2024"; String authNumber = getIntent().getStringExtra("authNumber"); if (TextUtils.isEmpty(uid) || TextUtils.isEmpty(pwd)) { ToastUtil.getInatance(activity).show("请输入视频柜员工号和密码!"); return; } showPd(); FHVideoManager.getInstance().loginAutoCreate(uid, pwd, dept, authNumber, new FHBusiCallBack() { @Override public void onSuccess(String s) { Log.i(TAG, "登录成功"); cancelPd(); startLiveActivity(); } @Override public void onError(String s) { Log.e(TAG, "登录失败: " + s); cancelPd(); if (!TextUtils.isEmpty(s) && (s.contains("40100") || s.contains("40024"))) { FHVideoManager.getInstance().forceFinishLastSession(FHParams.getUid(), null); } ToastUtil.getInatance(activity).show(s); finish(); } }); } private void startLiveActivity() { if (FHCallActivity.isStart) return; // 修改点3:明确状态标记 isLiveActive = true; isMetting = true; Log.d(TAG, "启动视频页面"); FHLiveActivity.setLiveLisenter(new FHLivelisenter() { @Override public void onVideoEvent(String type, String msg) { switch (type) { case FHTellerParams.FH_ON_START: configOutViewGroup(); Log.d(TAG, "会话开始"); break; case FHTellerParams.FH_VIDEO_END: case FHTellerParams.FH_ON_QUIT: finishCallMeeting(msg); break; case FHTellerParams.FH_ON_LEAVE: minimizeAppSafely(); break; case FHUIEvent.ONCLICK_FLOAT: handleFloatClick(); break; } } }); Intent intent = new Intent(activity, FHLiveActivity.class); startActivity(intent); // 修改点4:禁用跳转动画 overridePendingTransition(0, 0); } // 修改点5:新增安全最小化方法 private void minimizeAppSafely() { Log.d(TAG, "执行安全最小化"); moveTaskToBack(true); mHandler.postDelayed(() -> isLiveActive = false, 1000); } // 修改点6:优化小窗点击处理 private void handleFloatClick() { Log.d(TAG, "处理小窗点击"); mHandler.postDelayed(() -> { if (isMetting) { Intent intent = new Intent(activity, FHLiveActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(intent); overridePendingTransition(0, 0); } }, 200); } // 保留原有方法(未修改) private void configOutViewGroup() { if (outViewGroup == null) { outViewGroup = new RelativeLayout(getApplicationContext()); outViewGroup.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT )); } if (outWebView == null) { outWebView = new WebView(outViewGroup.getContext()); WebSettings webSettings = outWebView.getSettings(); webSettings.setJavaScriptEnabled(true); outWebView.setWebViewClient(new WebViewClient()); outWebView.loadUrl(""); // 配置业务URL } outViewGroup.removeAllViews(); outViewGroup.addView(outWebView); FHVideoManager.getInstance().configOutViewGroup(outViewGroup); } // 保留原有方法(未修改) private void destroyOutViewGroup() { if (outWebView != null) { outWebView.stopLoading(); outWebView.removeAllViews(); outWebView.destroy(); outWebView = null; } if (outViewGroup != null) { outViewGroup.removeAllViews(); } } private void finishCallMeeting(String msg) { Log.d(TAG, "结束会话: " + msg); isMetting = false; isLiveActive = false; destroyOutViewGroup(); } @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume"); mHandler.removeCallbacksAndMessages(null); } @Override protected void onRestart() { super.onRestart(); Log.d(TAG, "onRestart isMetting=" + isMetting); // 修改点7:优化返回处理 mHandler.postDelayed(() -> { if (isMetting && !isLiveActive) { Log.d(TAG, "恢复视频页面"); Intent intent = new Intent(activity, FHLiveActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivity(intent); overridePendingTransition(0, 0); } }, 300); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); // 修改点8:确保资源释放 mHandler.removeCallbacksAndMessages(null); FHAPlayer.getInstance().stopPlayer(); destroyOutViewGroup(); } }这个写的java中,这是一个视频功能调用sdk,启动视频各种功能,但是现在首次点击最小化,会有一个白屏/黑屏,再点返回才能到app(混合开发的vue页面),后续的最小化都没有问题,根据上面的代码,分析什么原因导致的,并给解决的办法,修改什么地方才能解决
最新发布
08-02
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值