Rahul Gupta
Rahul Gupta

Reputation: 5295

Edittext linespacing extra and cursor position issue

i am trying to make an Edittext with a good amount of line spacing. It is working but the cursor is not in the correct position. Look at the image below. As you can see the cursor is halfway in between the lines. Here is the Edittext Code :-

     <EditText
            android:id="@+id/editText1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/transparent"
            android:fadingEdge="vertical"
            android:gravity="left|center_vertical"
            android:lineSpacingExtra="10dp"
            android:lineSpacingMultiplier="2"
            android:padding="18dp"
            android:scrollbars="vertical"
            android:textSize="22sp" >

            <requestFocus />
        </EditText>

enter image description here

Upvotes: 3

Views: 4450

Answers (2)

ywwynm
ywwynm

Reputation: 11695

There is another problem if you're using textCursorDrawble with lineSpacingExtra: when your app is running above Lollipop, because the line height won't be changed for the last line, if you defines a negative bottom padding in your custom textCursorDrawable, the cursor will be shrank in last line like this:

enter image description here

To achieve more dramatic effect, I set the lineSpacingExtra to 8dp, and a part of my custom textCursorDrawable is <padding android:bottom="-8dp"/>. As a result, you can see the small cursor appearing in last line.

Of course we don't like this. I searched this in Internet for hours and days, but no page helps. But don't worry for this if you saw this answer since I've solved it in my way, which is a little hacky but works well as far as I know.

The root cause of our problem is that we've set a bottom padding to the textCursorDrawable, made it shorter for all lines. Let's say the text size is 12dp, then the original line height is also 12dp(just make things easier). After we define a lineSpacingExtra="8dp", the line height will increase to 20dp. If we don't do anything to textCursorDrawable, the cursor height will also be 20dp now, which is longer than text size apparently, so we shorten it, cut the bottom 8dp by defining <padding android:bottom="-8dp"/> in custom textCursorDrawable shape xml file. However, above Lollipop, lineSpacingExtra is ignored for the last line, so the line height of that remains at 12dp. So does the cursor of last line if we don't use custom one. And then we cut 8dp of that and now we see a 4dp cursor, which is wrong, while the correct operation is stopping setting the -8dp bottom padding in custom textCursorDrawable.

So the solution is: use different textCursorDrawable for normal lines and last line, showing enough respect to special one.

Then, when should we change the textCursorDrawable? My answer is: when user clicks last line of your EditText. This is reasonable because the cursor can only appear after this operation(maybe not the last operation if user clicks "enter" after this, but that doesn't influence our solution).

Next step is listening for user's clicking on last line. My solution is crude:

mEditText.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            EditText et = (EditText) v;
            // lineSpacingExtra's unit is px.
            int lastLineHeight = (int) (et.getLineHeight() - et.getLineSpacingExtra());
            if (et.getHeight() - event.getY() <= lastLineHeight) {
                setTextCursorDrawable(et, lastLineCursorRes);
            } else {
                setTextCursorDrawable(et, normalCursorRes);
            }
            return true;
        }
        return false;
    }
});

And This is the not elegant(but we have to) implementation for setTextCursorDrawable:

public void setTextCursorDrawable(EditText et, @DrawableRes int res) {
    try {
        Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
        f.setAccessible(true);
        f.set(et, res);
    } catch (Exception ignored) {}
}

Finally, the special part of lastLineCursorRes:

<padding android:bottom="0dp"/>

OK, there you go. Please be noticed that I wrote this answer just after I tried on this solution and found it worked, which means I didn't check it very carefully. If you meet any problems with this solution, please comment.

REFERENCES

To know why lineSpacingExtra/Multiplier is ignored in last line of TextView and its subclasses, check: How LineSpace Affects StaticLayout Height in One Line Text, Android TextView 行间距解析(in Chinese) and StaticLayout源码解析(in Chinese).

Upvotes: 1

dodushkaxy
dodushkaxy

Reputation: 19

The answer that you have to use android:textCursorDrawable="@drawable/name_of_your_drawable" and set your own drawable of cursor and in padding you should set value of android:bottom="-20sp"(or your size of text) .Like in example.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.androisxxxsxsxd.com/apk/res/android"
    android:shape="rectangle" >
    <size 
        android:width="2dip" />
    <solid
        android:color="@color/red" />
    <padding 
        android:top="2sp"
        android:bottom="-20sp" />
</shape>

myxml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <EditText
    android:id="@+id/edittext"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    android:paddingTop="18dp"
    android:gravity="left|center_vertical"
    android:lineSpacingExtra="20dp"
    android:textCursorDrawable="@drawable/d"
    android:text="test" 
    />

Upvotes: 1

Related Questions