问题场景
- 业务需求:弹出键盘时,固定标题栏,并且图片和编辑框同时变更为原大小的1/2,获取焦点。

原本用的是adjustPan,获取焦点时,会挤出标题栏和滚动页面。与需求大相径庭。 - 使用 adjustResize 后,一点编辑框,全部页面会跳到键盘下面,被键盘遮挡。
WindowsSoftInputMode 参数
定义键盘是否弹出的参数:
-
stateUnspecified:界面没有设置"android:windowSoftInputMode"时的状态.这个状态是弹出有EditText的界面时时不弹出软键盘的,当EditText获取焦点的时候弹出软件盘,用到时才弹
-
stateUnchanged:状态不改变,意思就是和上一个界面相同,上一个界面弹出软键盘,跳转到这个界面时,软键盘也是弹出状态.
-
stateHidden:除了从上一个页面返回时,沿用上一个页面,其他情况都隐藏
-
stateAlwaysHidden:一直隐藏
-
stateVisible:除了从上一个页面返回时,沿用上一个页面,其他情况都显示
-
stateAlwaysVisible:一直显示键盘
-
Android-软键盘一招搞定(原理篇)


我个人理解是: -
adjustPan 会移动整个页面,以让Edit显示,并不会管上面的内容是什么,它主打一个不改变原有页面,不做任何自以为是的修饰。

-
adjustResize 我的理解是 adjustResize 比较自恋,它会监听键盘的高度,算出当前APP可视高度,再重新调整布局。
- 但是,人家也只会对能调的进行调整,比如 wrap_content 那种,可以挤一挤的,就挤一挤。
- 尤其是对于 ScrollView ,或者 ConstaintLayout 这种约束布局,它会调整当前大小,而不会动标题,以及一些固定大小的View。
- 如果整个布局都写死了高度,Edit 逃无可逃,那点击了 Edit 它也确实逃无可逃,它会逃到键盘下面,消失不见(如果本来就被遮挡的话)
- 一般也会倾向于用 adjustResize ,在AndroidManifest.xml 的对应activity里设置 WindosSoftInputMode值。
-
adjustNothing 就是什么都不干咯。
键盘显示监听
对于一些要监听键盘是否要显示的场景,以及要在原生键盘上,设置功能键的需求,如上面这个完成键,该怎么做呢?

- 监听整个页面的DecorView
- 记录可视高度
mViewRoot = getActivity().getWindow().getDecorView();
mViewRoot.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
Rect rect = new Rect();
mViewRoot.getWindowVisibleDisplayFrame(rect);
mPresenter.handleKeyboard(rect.height());
});
对可视高度变化进行判断,对于键盘显示的场景做出处理
@Override
public void handleKeyboard(final int visibleHeight) {
if(mVisibleHeight <= 0) {
mVisibleHeight = visibleHeight;
Logger.d(TAG, "handleKeyboard()=>设置可见高度,mVisibleHeight=" + mVisibleHeight);
} else {
if (mVisibleHeight == visibleHeight) {
Logger.d(TAG, "handleKeyboard()=>键盘无变化,mVisibleHeight=" + mVisibleHeight);
return;
}
if (mVisibleHeight - visibleHeight > 200) {
mVisibleHeight = visibleHeight;
Logger.d(TAG, "handleKeyboard()=>显示键盘,mVisibleHeight=" + mVisibleHeight);
return;
}
if (visibleHeight - mVisibleHeight > 200) {
mView.hideKeyboard();
mVisibleHeight = visibleHeight;
Logger.d(TAG, "handleKeyboard()=>隐藏键盘,mVisibleHeight=" + mVisibleHeight);
}
}
}
键盘上加工具栏
其实也是利用了 adjustResize 的属性,把工具栏放在布局最底部,当显示键盘的时候也显示工具栏,由于会重新布局,这样工具栏就被放在了键盘上面。
这是一个举一反三的过程。
键盘按钮自定义
- 系统键盘上有很多键,有换行/完成/下一个等等,可以自定义这些键盘。
editText.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (listener != null) {
// 可添加抛出收起事件代码
}
return true;
}
return false;
}
});
- 监听点击软键盘外部,设置事件
@Override
public boolean onTouchEvent(MotionEvent event) {
if (indexOfChild(editText) > -1) {
// 可添加抛出收起事件代码
}
return super.onTouchEvent(event);
}
fitsSystemWindows 与键盘的关联
再学一遍android:fitsSystemWindows属性
解决业务场景的第二个问题,点击键盘时,所有页面错位,跳到了键盘下面。
这是因为 fitsSystemWindows 设定了是否为沉浸式导航栏:
- 如果 android:fitsSystemWindows=“true” 说明不需要沉浸式导航栏,正常显示状态栏。
- 因为最原始的View是全局的,没有区分什么状态栏。
- 如果不需要沉浸式,那需要在写顶部 View 的时候自动位移一个状态栏。使得 top 位的 View 都是在状态栏之下的。
- 如果是 false ,就说明是沉浸式导航栏,再写顶部 view 就会和状态栏覆盖。
完整代码
这是做Demo用到的代码,主要看ScrollView和软键盘的适配。
layout_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:fitsSystemWindows="false">
<ScrollView
android:id="@+id/scrollview"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintVertical_weight="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/edit"
android:layout_marginBottom="10dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
</androidx.recyclerview.widget.RecyclerView>
</ScrollView>
<EditText
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_constraintBottom_toTopOf="@+id/botton_text"
android:text="爱上;的六块腹肌啊;了啊;" />
<TextView
android:id="@+id/botton_text"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/purple_500"
android:text="阿迪舒服"
android:textColor="@color/white"
android:gravity="center"
android:textSize="16sp"
android:layout_marginTop="10dp"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
...
AndroidManifest.xml
//使用自适应
<activity android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
</activity>
Adapter适配器
public class MySimpleAdapter extends RecyclerView.Adapter<MySimpleAdapter.SimpleViewHolder> {
String[] mData;
Context mContext;
public MySimpleAdapter(Context context, String[] data) {
mContext = context;
mData = data;
}
@NonNull
@Override
public SimpleViewHolder onCreateViewHolder( ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_list_item,parent,false);
return new SimpleViewHolder(view);
}
@Override
public void onBindViewHolder(MySimpleAdapter.SimpleViewHolder holder, int position) {
holder.mTextView.setText(mData[position]);
}
@Override
public int getItemCount() {
return mData.length;
}
public class SimpleViewHolder extends RecyclerView.ViewHolder{
private TextView mTextView;
public SimpleViewHolder( View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.textview);
}
}
}
MainActivity.java
/**
* 测试软键盘自适应
*/
private void softWindowInput(){
mRecyclerView = findViewById(R.id.recyclerview);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAdapter = new MySimpleAdapter(this, mData);
mRecyclerView.setAdapter(mAdapter);
}

7839

被折叠的 条评论
为什么被折叠?



