Last active
March 15, 2023 05:43
-
-
Save ok3141/3a3d142adc2e30a203a8211adfc65bd8 to your computer and use it in GitHub Desktop.
MonospaceSpan detects the widest char of given text and draws others with the same width
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
WARNING: This class breaks when handling CharSequences which include unicode Emojis, because those are represented using TWO CharSequence elements together. Displaying them one by one breaks the display (so instead of the emoji, you'll get two question marks).
To display the emoji correctly, line 70 needs to be "canvas.drawText(text, start + i, start + i + 2, x + max * i + p, y, paint);", but, only where it's needed. I don't have the solution, but I wanted to report this bug :')
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example
MainActivity.java
res/layout/activity_main.xml