13

I have been trying to figure this one out for the last couple of days now, and have had no success...

I'm learning android right now, and am currently creating a calculator with history as my learning project. I have a TextView that is responsible for displaying all history... I'm using a digital font that looks like a calculator font, but this only looks good for digits and decimals and comma's. I want all operators to be highlighted and in a different font (Arial Narrow at the moment). I have been able to get this to work beautifully using a spannable string where I'm specifying a font color as well as a font using a CustomTypeFaceSpan class to apply my custom fonts.

The problem... When I mix the Typefaces, there seems to be an issue with the line height, so I found this post which demonstrates using another custom defined class to apply a line height to each added line of spannable text:

public class CustomLineHeightSpan implements LineHeightSpan{
    private final int height;

    public CustomLineHeightSpan(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;
    }

}

This does not seem to work, and I can not figure out why. If I don't apply the different typefaces, then it displays as expected with no space above the first line, and about 5px spacing between lines. When I apply the alternate typefaces, there is a space of about 10 to 15px above the first line of text and the line spacing is about the same 10 to 15px.

There is no difference in the font size, only the typeface. What am I missing. I implemented the CustomLineHeightSpan which implements LineHeightSpan and overrides the chooseHeight method. I call it like so:

WordtoSpan.setSpan(new CustomLineHeightSpan(10), operatorPositions.get(ii), operatorPositions.get(ii) + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

It does not seem to matter what I put in the call to CustomLineHeightSpan. Nothing changes...

Anybody have any idea what I'm missing... I'm sure it's an "I can't believe I missed that" answer, but can't seem to figure it out at the moment.

Thanks for the help guys :-)

Community
  • 1
  • 1
titanic_fanatic
  • 593
  • 2
  • 5
  • 13
  • Does this answer your question? [Individual line spacing for each line](https://stackoverflow.com/questions/11111129/individual-line-spacing-for-each-line) – grebulon Nov 26 '19 at 10:05
  • See this: https://stackoverflow.com/a/59048332/2603965 – grebulon Nov 26 '19 at 10:05

1 Answers1

22

I finally found a more in depth example of the use of LineHeightSpan... Actually LineHeightSpan.WithDensity to be more precise... The following is the excerpt that helped me to resolve my issue:

private static class Height implements LineHeightSpan.WithDensity {
    private int mSize;
    private static float sProportion = 0;

    public Height(int size) {
        mSize = size;
    }

    public void chooseHeight(CharSequence text, int start, int end,
                             int spanstartv, int v,
                             Paint.FontMetricsInt fm) {
        // Should not get called, at least not by StaticLayout.
        chooseHeight(text, start, end, spanstartv, v, fm, null);
    }

    public void chooseHeight(CharSequence text, int start, int end,
                             int spanstartv, int v,
                             Paint.FontMetricsInt fm, TextPaint paint) {
        int size = mSize;
        if (paint != null) {
            size *= paint.density;
        }

        if (fm.bottom - fm.top < size) {
            fm.top = fm.bottom - size;
            fm.ascent = fm.ascent - size;
        } else {
            if (sProportion == 0) {
                /*
                 * Calculate what fraction of the nominal ascent
                 * the height of a capital letter actually is,
                 * so that we won't reduce the ascent to less than
                 * that unless we absolutely have to.
                 */

                Paint p = new Paint();
                p.setTextSize(100);
                Rect r = new Rect();
                p.getTextBounds("ABCDEFG", 0, 7, r);

                sProportion = (r.top) / p.ascent();
            }

            int need = (int) Math.ceil(-fm.top * sProportion);

            if (size - fm.descent >= need) {
                /*
                 * It is safe to shrink the ascent this much.
                 */

                fm.top = fm.bottom - size;
                fm.ascent = fm.descent - size;
            } else if (size >= need) {
                /*
                 * We can't show all the descent, but we can at least
                 * show all the ascent.
                 */

                fm.top = fm.ascent = -need;
                fm.bottom = fm.descent = fm.top + size;
            } else {
                /*
                 * Show as much of the ascent as we can, and no descent.
                 */

                fm.top = fm.ascent = -size;
                fm.bottom = fm.descent = 0;
            }
        }
    }
}

This was taken from this example.

What it does is as quoted below:

Forces the text line to be the specified height, shrinking/stretching the ascent if possible, or the descent if shrinking the ascent further will make the text unreadable.

I hope this helps the next person :-)

titanic_fanatic
  • 593
  • 2
  • 5
  • 13
  • 2
    how to call this method chooseHeight? – AndroidCrazy Dec 06 '13 at 01:10
  • Android calls this method internally. All you have to do is give your String the span. If you want to know more, check out the android.text.StaticLayout class. The methods for the other spans are called there as well. – thehayro Dec 11 '13 at 14:15
  • 4
    If we ever meet I owe you a beer (or other beverage of choice) - this stuff is super undocumented, and finding it takes a crazy amount of time. – secureboot Apr 11 '14 at 22:05
  • +1. Works like a charm. One question: is it possible to vertically center the text? – sonix Sep 03 '14 at 15:24
  • Why does setting FontMetricsInt.leading have no effect? – alexbirkett Aug 07 '15 at 08:40
  • 4
    This works great but if this affects the spacing of the whole paragraph instead of a single line. How would I apply this on a single line within a paragraph ? – Atieh Feb 27 '17 at 04:13
  • Works correctly to be space is Height(57) but, after that value suddenly rising the line height, how to solve this? – ATES May 26 '20 at 16:18