避免 ScrollView 在 EditText 换行时自动滚动

注意:该问题只出现在 Android N 以下的一些系统版本中

无意中发现了一个奇怪的现象,当 ScrollView 中包含一个设置了 minLines 和 maxLines 属性的 EditText,且当前 Activity 设置的 windowSoftInputMode 为 adjustResize 时,在 EditText 输入回车进行多次换行操作后,ScrollView 会向上滚动,尝试显示被弹起的键盘所覆盖的区域。

如图所示:
避免 ScrollView 在 EditText 换行时自动滚动_第1张图片

布局代码如下:


<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="left|top"
            android:maxLines="8"
            android:minLines="8"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="56dp"
            android:textColor="#fff"
            android:textSize="16sp"
            android:gravity="center_vertical"
            android:text="ScrollView 靠下的控件"
            android:layout_marginTop="200dp"
            android:background="#33b5e5" />

    LinearLayout>

ScrollView>

换行操作用户的关注点在输入框,而不是界面的其它地方,ScrollView 的滚动却将输入框滚动到用户不可见的地方,显然这种行为对用户是极为不友好的。

一番搜索后在 google issue 上发现了相关问题 EditText with maxLines in RecyclerView scrolls out of the screen with new line chars 其中最后一贴提到这是个框架级的 bug,且会在 N 进行修复:

So turns out this was a bug in the framework, in addition to the RV :/. 
RV part will be fixed in the next release but as long has the TextView is not a direct child of the RecyclerView, this fix wont work :/. There is nothing we can do about this particular issue because the layout in between is eating crucial information. It will be fine on N.

继续到 Android 源代码库中找到修复这个 bug 的提交 Fix requestRectangleOnScreen ,它的 commit 信息是:

Fix requestRectangleOnScreen

This CL fixes a bug in View#requestRectangleOnScreen where the
position was not being moved from child's coordinate space to
the parent's coordinate space properly.

I've also added more documentation to clarify the API.

看了看它的代码(分析过程不表),照猫画虎的话,在 N 以下的机器重写 EditText 的 requestRectangleOnScreen 方法就可以避免问题发生,代码如下:

@Override
    public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
            rectangle.offset(-getScrollX(), -getScrollY());
        }
        return super.requestRectangleOnScreen(rectangle, immediate);
    }

在后续跟进问题的过程中,发现从 4.2 到 6.0 的系统源码都是出现问题的那一套,所以上面的代码判断是这样一个版本区间。我在实测真机的过程中发现 4.0(联想) 不会出现问题,但是 5.0(索尼) 和 6.0(模拟器) 都出现了问题,有需要的朋友可以根据自己的需求来修改版本判断

你可能感兴趣的:(避免 ScrollView 在 EditText 换行时自动滚动)