Android 软键盘适配:adjustPan vs adjustResize 到底差在哪
写 Android 界面的人几乎都踩过这个坑:输入框在页面底部,一点击,软键盘弹出来,要么把输入框挡住了,要么把整个布局挤得变形。解决这两个问题的核心,就是 android:windowSoftInputMode。
但它有好几个值,adjustPan、adjustResize、adjustNothing、stateVisible、stateAlwaysHidden……名字相近,效果却天差地别。这篇文章用图示帮你一次搞懂。
一、windowSoftInputMode 是什么
它是 Activity(或 <application>)级别的一个属性,告诉系统当软键盘弹出/收起时,窗口该如何处理。它有两组、可以组合使用的值。
第一组:键盘是否显示(state 系列) —— 控制键盘”什么时候出现”
| 值 | 含义 |
|---|---|
stateUnspecified |
默认,由系统决定 |
stateUnchanged |
保持上一个界面的键盘状态 |
stateHidden |
键盘默认隐藏 |
stateAlwaysHidden |
键盘始终隐藏,除非用户手动拉出 |
stateVisible |
键盘默认显示 |
stateAlwaysVisible |
键盘始终显示 |
第二组:布局如何调整(adjust 系列) —— 控制键盘”出现后,界面怎么变”。这才是本篇文章的主角。
| 值 | 含义 |
|---|---|
adjustNothing |
什么都不做 |
adjustPan |
界面整体上移,让焦点输入框露出来 |
adjustResize |
界面重新调整大小(压缩),给键盘腾出空间 |
在 AndroidManifest 里这样写(两组值用 | 拼接):
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize|stateHidden"
... />
下面重点讲 adjustPan 和 adjustResize。
二、初始状态
先看没有键盘弹出时,一个典型页面的样子:顶部标题栏,中间是可滚动内容区,底部是输入框。

窗口高度等于完整屏幕高度,一切正常。
三、adjustResize:压缩布局,给键盘让路
核心思路:窗口的高度直接缩小,缩掉的部分就是键盘占据的高度。
整个 Activity 的可用区域变矮,布局系统重新测量,于是:
match_parent的内容会被压缩- 底部的内容(比如输入框)会被”顶”到键盘上方
- 如果你的布局是 flex 式、可滚动的(
ScrollView、RecyclerView),会自动重排

关键点:adjustResize 会把窗口高度真的缩小,相当于你屏幕变矮了。
适用场景:
– 底部有输入框,且希望输入框始终紧贴键盘上方
– 布局是可滚动、可伸缩的(聊天界面的典型做法)
代价/坑:
– 如果布局里有固定高度、不可伸缩的元素,可能被挤压错位
– 图片、背景会被拉伸或裁剪
– 需要 android:fitsSystemWindows 配合,处理沉浸式/状态栏的场景
四、adjustPan:整体平移,把焦点框”抬”上来
核心思路:窗口大小不变,整个界面向上平移一段距离。
系统计算出当前聚焦的输入框距离键盘顶部有多远,然后把整个界面整体上移,刚好让输入框出现在键盘上方可见的位置。

关键点:窗口高度不变,只是”画面”往上顶,顶部的内容会被顶出屏幕看不见。
适用场景:
– 界面顶部内容不重要、被顶掉没关系的场景
– 布局不允许压缩(比如固定尺寸的全屏游戏、全屏视频)
– 某些把内容写死、不希望触发重新测量的复杂布局
代价/坑:
– 顶部内容(标题栏、工具栏)可能被顶出屏幕
– 无法做到”输入框贴键盘”这种精细效果
– 滚动位置可能受影响
五、一张图看懂区别

| 维度 | adjustResize |
adjustPan |
|---|---|---|
| 窗口高度 | 改变(缩小) | 不变 |
| 界面怎么动 | 布局重新测量、压缩 | 整个窗口平移 |
| 顶部内容 | 保留(被压缩) | 可能被顶出屏幕 |
| 底部输入框 | 贴住键盘上方 | 被”抬”到可见位置 |
| 是否触发重排 | 是 | 否 |
| 典型用途 | 聊天/表单 | 全屏/固定布局 |
六、最常见的坑(很多人栽在这里)
1. 设置了 adjustResize,但界面还是没反应?
八成是你的 Activity 用了全屏/沉浸式模式,或者 Theme 里 windowFullscreen 盖过了 windowSoftInputMode。
2. 用了 adjustResize,背景图被压扁了
因为窗口真的变小了。解决:把背景放在不会因 resize 扭曲的层级,或用 .9.png / 独立的 Background view。
3. 想”输入框贴键盘”,两个都能实现吗?
只有 adjustResize 能保证”贴住”。adjustPan 只是”露出”输入框,间距不可控。
4. 组合使用
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"
stateAlwaysHidden 表示:刚进页面键盘不弹,等用户点输入框才弹。这是表单页很常用的组合。
七、代码示例(一个可跑的最小 Demo)
一个底部输入框 + 顶部标题的页面,切换两种模式对比。
AndroidManifest.xml
<application ...>
<activity
android:name=".ResizeActivity"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".PanActivity"
android:windowSoftInputMode="adjustPan" />
</application>
activity_resize.xml(底部输入框,中间可滚动)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="标题栏(adjustResize 下会被压缩但不消失)" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<!-- 中间内容 -->
</ScrollView>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入框" />
</LinearLayout>
把两个 Activity 都跑一遍,你就能直观看到:ResizeActivity 里标题栏还在、输入框贴键盘;PanActivity 里标题栏被顶没了。
八、总结
- 要输入框始终贴键盘、要界面优雅收缩 → 用
adjustResize - 要简单粗暴、不管顶部死活、布局不能压缩 → 用
adjustPan - 两者可以搭配
state...系列一起用 - 实际开发中,聊天/表单页面绝大多数用
adjustResize
