专业编程基础技术教程

网站首页 > 基础教程 正文

EditText与软键盘的相爱相杀(edittext点击不弹出键盘)

ccvgpt 2024-08-02 12:03:05 基础教程 15 ℃

这周完成了一个需求,涉及到EditText和软键盘,其中一个要求是:键盘出现时,文本输入框和键盘的间距大于16dp。

现在回头看,没想到EditText能折腾出新鲜东西。

EditText与软键盘的相爱相杀(edittext点击不弹出键盘)

整个页面的布局走常规路线,我画了一张草图,其中ScrollView和EditText用红色标注了一下:


一、键盘出现时,遮盖住了部分EditText

这个问题,写项目的时候我是没有遇到的,为了这篇文章写Demo时出现了,真是amazing。

然后这个问题的解法,在搜索引擎上查找到,但是结果并不理想,看到的方案太复杂,比如:使用ViewTreeObserver.OnGlobalLayoutListener监听整个页面布局变化,在回调里滑动ScrollView

于是我对比了一下项目和Demo关于使用EditText的代码。发现区别在于:layout_height的值。

  • 当layout_height="xxdp"时,会出现软键盘遮盖部分EditText的问题
  • 下面这种写法则不会导致软键盘遮盖的问题
    <EditText ... android:paddingTop="18dp" android:paddingBottom="18dp" android:layout_height="wrap_content"/>

所以,这个问题我们可以规避掉。至于这两种写法,为何会造成键盘表现不同,有兴趣的朋友可以深拔拔看,又知道的也请告知一下,感谢~

图【EditText 被键盘遮盖部分底部】:


二、如何控制文本输入框和键盘的间距

首先,ScrollView会自动滚动合适的距离将带焦点的View滑动到屏幕的可见区域,这个知识在做这次的需求之前我已知道。后面所有的思路,就是如何利用ScrollView的这一特点。

解法一:

设置EditText的bottom padding,结合InsetDrawable,在视觉上可以以假乱真。

最后没有采取这种方案,这个方案会导致EditText和它底部临近的控件间距变大(如果没底部没有其他控件这个方案就很适合)

效果大概是这样的:

解法二:

巧用ScrollView自动滚动带焦点View的特点。当带焦点的View不在屏幕可见的范围时,ScrollView内部会根据这个View的bounds,计算一个合适的偏移量,然后将该View滚动到可见的合适范围。

我们看一下ScrollView#onSizeChanged()方法的源码:


@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {    
    super.onSizeChanged(w, h, oldw, oldh);    
    View currentFocused = findFocus();    
    if (null == currentFocused || this == currentFocused)        
        return;    
    // If the currently-focused view was visible on the screen when the    
    // screen was at the old height, then scroll the screen to make that   
    // view visible with the new screen height.    
    if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) { 
        //获取View的位置宽高信息等
        currentFocused.getDrawingRect(mTempRect);        
        offsetDescendantRectToMyCoords(currentFocused, mTempRect);
        //根据此时获取到焦点的View计算滑动距离,
        int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect);        
        doScrollY(scrollDelta);
    }
}


computeScrollDeltaToGetChildRectOnScreen方法名好长,打开这个方法的文档,我们能看到:

Compute the amount to scroll in the Y direction in order to get a rectangle completely on the screen (or, if taller than the screen, at least the first screen size chunk of it).

大意是:计算rectangle表示的View完全可见需要的的滑动距离,这个是我们需要的方法。该方法的修饰符是protected,意味着我们通过可以继承的方式重写该方法,通过修改该方法的返回值,达到我们的目的,整个修改的开发量巨小,如下:


class FITBScrollView(
    context: Context?,
    attrs: AttributeSet?
) : ScrollView(context, attrs) {
    var extraMargin = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP, 
        16f, 
        resources.displayMetrics
    ).roundToInt()
    
    /**
     *  EditText获取焦点后,ScrollView会将它滑动到屏幕显示的范围内,这个时候这个方法会被触发,用来计算Y轴滚动距离
     */
    override fun computeScrollDeltaToGetChildRectOnScreen(rect: Rect?): Int {
        //Child非完全可见时
        if (rect?.bottom ?: 0 > scrollY + height - extraMargin) {
            return super.computeScrollDeltaToGetChildRectOnScreen(rect) + extraMargin        
        }
        return super.computeScrollDeltaToGetChildRectOnScreen(rect)
    }
}


效果大概是这样的:


希望对有类似问题的开发朋友们有帮助。尤其是EditText被软键盘遮挡的问题,不希望大家用太复杂的方案,因为复杂的方案常常意味着更多的风险。


Tags:

最近发表
标签列表