Android中adjustPan对ScrollView外控件失效问题咨询
我来帮你解决这个问题——adjustPan确实能让ScrollView里的内容跟着软键盘上移,但它的全局平移特性很容易影响到ScrollView外的控件,导致尺寸异常。下面是几个针对性的解决方案,按优先级推荐:
1. 先检查ScrollView外控件的布局约束
adjustPan会把整个Activity窗口向上平移,如果你外部控件的布局是依赖父布局底部的(比如RelativeLayout里的layout_alignParentBottom="true",或者ConstraintLayout里的constraintBottom_toBottomOf="parent"),窗口上移后这些控件会被挤压或者位置偏移。
解决办法:把外部控件的约束改成依赖ScrollView的底部,比如如果ScrollView是占满中间区域,底部控件设置layout_below="@id/scroll_view"(RelativeLayout)或者constraintTop_toBottomOf="@id/scroll_view"(ConstraintLayout),这样控件的位置会跟着ScrollView走,不会被软键盘影响尺寸。
2. 换用adjustResize+自定义ScrollView(推荐)
adjustResize会让窗口调整大小来适配软键盘,这样外部控件的尺寸能保持正常,但默认情况下ScrollView不会自动滚动到焦点EditText。我们可以自定义一个ScrollView,让它在软键盘弹出时自动滚动到焦点控件:
public class AutoScrollScrollView extends ScrollView { public AutoScrollScrollView(Context context) { super(context); } public AutoScrollScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public AutoScrollScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); // 找到当前获取焦点的EditText View focusedChild = findFocus(); if (focusedChild != null) { // 计算滚动距离,让焦点控件居中显示 int scrollTarget = focusedChild.getTop() - (getHeight() / 2); smoothScrollTo(0, scrollTarget); } } }
然后做两步修改:
- 把Activity的
android:windowSoftInputMode改成adjustResize - Fragment布局里的普通ScrollView替换成这个
AutoScrollScrollView
这样既保证了外部控件尺寸不受影响,又能让ScrollView内的EditText自动滚动到可见区域,完美替代adjustPan的效果。
3. 给外部控件设置固定/最小尺寸
如果外部控件是因为窗口平移被压缩(比如用了wrap_content或者match_parent作为高度),可以直接给它们设置固定的android:layout_height或者android:minHeight,强制保持尺寸不变。比如:
<TextView android:id="@+id/outer_text" android:layout_width="match_parent" android:layout_height="100dp" <!-- 固定高度 --> android:text="ScrollView外的控件"/>
4. 监听软键盘状态手动调整尺寸
如果上面的方法都不适用,可以通过监听窗口布局变化来判断软键盘状态,手动调整外部控件的尺寸:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); View outerView = view.findViewById(R.id.outer_view); View rootView = view.getRootView(); rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect visibleFrame = new Rect(); rootView.getWindowVisibleDisplayFrame(visibleFrame); int screenHeight = rootView.getHeight(); int keyboardHeight = screenHeight - visibleFrame.bottom; // 当键盘高度超过屏幕15%时,判断为弹出状态 if (keyboardHeight > screenHeight * 0.15) { // 调整外部控件高度为固定值 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) outerView.getLayoutParams(); params.height = 200; outerView.setLayoutParams(params); } else { // 恢复原来的布局参数 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) outerView.getLayoutParams(); params.height = ViewGroup.LayoutParams.WRAP_CONTENT; outerView.setLayoutParams(params); } } }); // 记得在销毁时移除监听,避免内存泄漏 view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) {} @Override public void onViewDetachedFromWindow(View v) { rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); }
这个方法比较灵活,但需要处理好监听的移除,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Hadi Ahmadi

