多行 EditText 的光标高度问题

问题

改变文本的行间距需要给 EditText 设置 lineSpacingExtralineSpacingMultiplier 这个时候的 EditText的光标高度就会变得很难看.

默认效果

解决办法

自定义 cursorDrawable 然后设置 padding

原理

EditText 在更新光标的视图的时候检查了光标 drawable 的 padding. 计算光标高度会算上相应的值.

相关源码:

Editor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) {
// ...
if (mTempRect == null) mTempRect = new Rect();
mCursorDrawable[cursorIndex].getPadding(mTempRect); //光标的 padding
// ...
// 上下边距的最终值为 top - mTempRect.top 和 bottom + mTempRect.bottom
mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width,
bottom + mTempRect.bottom);
}

那么 top 和 bottom 的值是多少 ?

Editor.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void updateCursorsPositions() {
//...
Layout layout = mTextView.getLayout();
Layout hintLayout = mTextView.getHintLayout();
final int offset = mTextView.getSelectionStart();
final int line = layout.getLineForOffset(offset); // 算出当前是第几行
final int top = layout.getLineTop(line); // 算出第 line 行的顶部坐标
final int bottom = layout.getLineTop(line + 1); // 算出第 line+1 行的顶部坐标
// ...
boolean clamped = layout.shouldClampCursor(line);
updateCursorPosition(0, top, middle,
getPrimaryHorizontal(layout, hintLayout, offset, clamped));
// ...
}

从源码看出 bottom 到 top 的距离为第 line 行的顶部到第 line+1 行的顶部. 也就是上面图中的效果.

解决方法:
自定义一个 cursor.xml, 然后设置 android:textCursorDrawable="@drawable/cursor"

cursor.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="https://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size
android:width="2dip" />
<solid
android:color="#f00" />
<padding
android:top="0dp"
android:bottom="-22dp" />
</shape>

注意上面设置了 shape 的 padding 计算出来 bottom = 行高 - 文字高度 = 42 - 20 = 22.当然这值是可以自己调整的.

设置后效果

文章来自: https://hanks.pub