20

I am developing an app that displays information such as ascii arts which are very sensitive to font width. I've been using monospace font, but it does not work well because there are wide chars (such as Chinese and Japanese characters) in the information and the textview does not make the wide chars exactly twice wide as regular chars. Therefore I am trying to see if it's possible to alter the width of fonts in a textview, or if there's better way to solve this problem? Is Installing another monospace font to my app a good idea? Any input is greatly appreciated. Thank you.

Kevin

Kevin
  • 201
  • 1
  • 2
  • 3

3 Answers3

45

You can try

textView.setTextScaleX(1.5f);

textView.setTextSize(20);

textView.setTypeface(Typeface.MONOSPACE);    //all characters the same width

With these three methods I hope you can set the font to a desirable appearance.

Lumis
  • 21,517
  • 8
  • 63
  • 67
  • Thank you for your reply. It seems setTextScaleX adjusts the font width according to its original width. Therefore the width is proportional to the original width instead of making all font the same width. Is there a way to work around this? Thank you. – Kevin May 21 '11 at 07:23
  • The monospaced typeface works for making regular char equal width. It seems that it doesn't work for wide char. In order to correctly display the ascii art, the wide chars need to be twice wide as the regular chars. For example, aaaaaaaa aaaa These two strings should look with the same length on a terminal. Wikipedia says the monospace typeface doesn't support wide char languages such as Chinese and Japanese. http://en.wikipedia.org/wiki/Droid_(font) Therefore it couldn't correctly render the width of wide chars, so I am hoping to find a way to set the width of the wide chars. Thanks. – Kevin May 21 '11 at 13:11
  • 1
    as a style item: monospace – larham1 Jan 23 '13 at 19:10
  • Note that if you set fontFamily it might not work well. Example of a problematic value is "sans-serif-light" – android developer Jun 15 '17 at 11:41
14

for those who wants to set monospace using xml, try adding 'android:typeface = "monospace"'

        <TextView
            android:id="@+id/tv_output"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"  
            android:typeface="monospace" />

before adding monospace

enter image description here

after adding monospace enter image description here

Angel Koh
  • 12,479
  • 7
  • 64
  • 91
9

This question is too old, but I got similar issue and I eventually found a good solution.

I have font, which has no monospace variant. I need to display some hexadecimal value in few lines within TextView, but I don't want to use any other font.

Android documentation says:

Spans are powerful markup objects that you can use to style text at a character or paragraph level. By attaching spans to text objects, you can change text in a variety of ways, including adding color, making the text clickable, scaling the text size, and drawing text in a customized way. Spans can also change TextPaint properties, draw on a Canvas, and even change text layout.

So, I create custom MonospaceSpan implementation which derives from ReplacementSpan. This span detects the widest char of given text and draws others with the same width.

Here's result:

MonospaceSpan result

GitHub

MonospaceSpan.java

    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.support.annotation.NonNull;
    import android.support.annotation.Nullable;
    import android.text.style.ReplacementSpan;

    public class MonospaceSpan extends ReplacementSpan {
        private boolean ignoreFullText;

        public void setIgnoreFullText(boolean ignoreFullText) {
            this.ignoreFullText = ignoreFullText;
        }

        private int getMaxCharWidth(@NonNull Paint paint, @NonNull CharSequence text, int start, int end, float[] widths) {
            if (widths == null) {
                widths = new float[end - start];
            }

            paint.getTextWidths(text, start, end, widths);

            float max = 0;

            for (float w : widths) {
                if (max < w) {
                    max = w;
                }
            }

            return Math.round(max);
        }

        @Override
        public int getSize(@NonNull Paint paint, @NonNull CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
            if (fm != null) {
                paint.getFontMetricsInt(fm);
            }

            int count = end - start;

            if (text.charAt(start) == '\n') {
                count -= 1;
            }

            if (text.charAt(end - 1) == '\n') {
                count -= 1;
            }

            if (count < 0) {
                count = 0;
            }

            if (ignoreFullText) {
                return getMaxCharWidth(paint, text, start, end, null) * count;
            } else {
                return getMaxCharWidth(paint, text, 0, text.length(), null) * count;
            }
        }

        @Override
        public void draw(@NonNull Canvas canvas, @NonNull CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
            float[] widths = new float[end - start];
            int max = getMaxCharWidth(paint, text, start, end, widths);

            if (!ignoreFullText) {
                max = getMaxCharWidth(paint, text, 0, text.length(), null);
            }

            for (int i = 0, n = end - start; i < n; ++i) {
                float p = (max - widths[i]) / 2;
                canvas.drawText(text, start + i, start + i + 1, x + max * i + p, y, paint);
            }
        }
    }

Example of usage:

MainActivity.java

    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.text.SpannableString;
    import android.widget.TextView;

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            setContentView(R.layout.activity_main);

            String text = "Lorem ipsum\ndolor sit amet\n0123456789";
            SpannableString textMono = new SpannableString(text);
            textMono.setSpan(new MonospaceSpan(), 0, textMono.length(), 0);

            TextView textView1 = findViewById(android.R.id.text1);
            TextView textView2 = findViewById(android.R.id.text2);

            textView1.setText(text);
            textView2.setText(textMono);
        }
    }

res/layout/activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_horizontal"
        android:orientation="vertical"
        android:padding="16dp">

        <TextView
            android:id="@android:id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:background="#fa0"
            android:fontFamily="@font/fredoka_one" />

        <TextView
            android:id="@android:id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="4dp"
            android:background="#0af"
            android:fontFamily="@font/fredoka_one" />
    </LinearLayout>
Oleksii K.
  • 5,359
  • 6
  • 44
  • 72