10

Is it possible to define individual line spacings for each text line of a TextView?

Example:

TextView tv = new TextView(context);
tv.setText("line1\nline2\nline3");

The method setLineSpacing(float add, float mult) defines the line spacings for all text lines of the TextView. I would like to define another line spacing between line1 and line2 and a different line spacing between line2 and line3.

Any ideas how to do this ?

Does a spannable provide a solution ?

Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98
Anne Droid
  • 3,151
  • 4
  • 16
  • 15

7 Answers7

19

Yes, you can do it by utilizing the LineHeightSpan interface. Here's a quick and dirty sample code on how to do this:

public class MyActivity extends Activity {

    private static class MySpan implements LineHeightSpan {
        private final int height;

        MySpan(int height) {
            this.height = height;
        }

        @Override
        public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v,
                FontMetricsInt fm) {
            fm.bottom += height;
            fm.descent += height;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final TextView tv = new TextView(this);
        setContentView(tv);

        tv.setText("Lines:\n", BufferType.EDITABLE);
        appendLine(tv.getEditableText(), "Line 1 = 40\n", 40);
        appendLine(tv.getEditableText(), "Line 2 = 30\n", 30);
        appendLine(tv.getEditableText(), "Line 3 = 20\n", 20);
        appendLine(tv.getEditableText(), "Line 4 = 10\n", 10);
    }

    private void appendLine(Editable text, String string, int height) {
        final int start = text.length();
        text.append(string);
        final int end = text.length();
        text.setSpan(new MySpan(height), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

}
Joe
  • 14,039
  • 2
  • 39
  • 49
  • Thank you Joseph, that is an excellent and very useful example, I have tested it and it works ! – Anne Droid Jun 20 '12 at 20:25
  • This seems to add extra space of size 40 after Line 4. Is there an elegant way to get rid of this extra space? – erin Mar 26 '13 at 17:47
  • @erin I added `tv.getEditableText().append("Line 5\nLine 6");` after the last appendLine() call in onCreate() and didn't see the extra space that you mentioned, maybe we are not talking about the same thing? – Joe Mar 31 '13 at 16:38
  • You also have to set the BufferType back to BufferType.NORMAL, so the TextView's style persists. I had this issue in a recycler view, where the TextView lost its style because of that, so after you append every line, don't forget to setText(getText(), BufferType.NORMAL); – box Oct 09 '15 at 09:40
  • How can I do this with regular expressions? I'm not appending lines and on which line is the text matched by my regular expression. I'm using mutliple superscripts and my text is overlapping, so is there a way to do it? – Atieh Feb 13 '17 at 03:39
  • Correct answer! – ATES May 16 '20 at 15:13
3

Android 10 (API 29) added LineHeightSpan.Standard to set an absolute line height. If you need backward compatibility, use the following implementation which I based on LineHeightSpan.Standard:

private static class AbsoluteHeightSpan implements LineHeightSpan {
    private final int _height;

    AbsoluteHeightSpan(int height) {
        _height = height;
    }

    @Override
    public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, FontMetricsInt fm) {
        final int originHeight = fm.descent - fm.ascent;
        // If original height is not positive, do nothing
        if (originHeight <= 0)
            return;
        final float ratio = _height * 1.0f / originHeight;
        fm.descent = Math.round(fm.descent * ratio);
        fm.ascent = fm.descent - _height;
    }
}
grebulon
  • 7,697
  • 5
  • 42
  • 66
1

you can do these kind of specific styles to the textview using Html. Try this example,

tv.setText(Html.fromHtml("<h2>Text1</h2><br><p>Text2</p>"));

Different kind of tags allowed here are,

<a href="...">
<b>
<big>
<blockquote>
<br>
<cite>
<dfn>
<div align="...">
<em>
<font size="..." color="..." face="...">
<h1>
<h2>
<h3>
<h4>
<h5>
<h6>
<i>
<img src="...">
<p>
<small>
<strike>
<strong>
<sub>
<sup>
<tt>
<u>
Akilan
  • 1,707
  • 1
  • 16
  • 30
  • while this works it is very expensive since a whole bunch of xml parsing code gets invoked internally. be careful with this – martyglaubitz Jun 05 '14 at 08:56
0

Recently I'm faced with a similar problem. I use a string resource from string.xml and display it in a dialog. I solve my issue by using <font> tag inside <string> and specify a font size for \n in it. The first \n is simply a line break, and the second is a new empty line, with height specified by <font size="YOUR_SIZE_FOR_THE_LINE"></font>.

Sample code:

    <string name="specification_msg"><b>1 Quick Action</b>
        \nQuick-Action is a small window which is supposed to be fast so it can offer you a convenient way of recording income and expenditure.
        <font size="4">\n\n</font><b>︎︎1.1 Brief Mode</b>
        \nDate and time will be hidden in window and set to current time. If you scarcely adjust them, turning on this feature will give you a cleaner view.
        <!-- Other lines -->
    </string>

Screenshot: Screenshot of TextView inside AlertDialog

enter image description here

(The line spaces are originally designed for Chinese characters, so the difference may seem less apparent in English.)

I know it's a dirty method but it can sometimes help...

zhangxaochen
  • 32,744
  • 15
  • 77
  • 108
Simon Smith
  • 414
  • 3
  • 6
0

I did it via AbsoluteSizeSpan. Kotlin Extensions for add space between text in dip

Usage

buildSpannedString { 
    append("smth")
    spaceInDip(16)
    append("smth2")
}

Code

public fun SpannableStringBuilder.sizeInDip(
    size: Int,
    builderAction: SpannableStringBuilder.() -> Unit
): SpannableStringBuilder = inSpans(
    AbsoluteSizeSpan(size, true),
    builderAction = builderAction
)

public fun SpannableStringBuilder.spaceInDip(
    size: Int
): SpannableStringBuilder = sizeInDip(
    size = size,
    builderAction = {
        appendLine()
        sizeInDip(size) { appendLine() }
    }
)
P1NG2WIN
  • 764
  • 1
  • 9
  • 24
-3

Its not possible.This technique is use in c ,c++.What you use inside setText(),it shows the whole text you write .

MBMJ
  • 5,323
  • 8
  • 32
  • 51
-3

Try this:

android:lineSpacingMultiplier = "3"

Line spacing can also be in decimal format.

Hussein El Feky
  • 6,627
  • 5
  • 44
  • 57
Manmohan Pal
  • 1,577
  • 1
  • 12
  • 13
  • I haven't had a chance to verify this 100% but I think this actually solves the problem for my use case, not sure why it was down voted. – CodyEngel Sep 23 '16 at 21:18
  • 2
    @HuskyHuskie This changes the line spacing for the entire view, while OP wants to change the line spacing for individual lines. – Darkwater Oct 18 '16 at 01:02