My application uses the unicode character \u2192 to display a right arrow within a TextView element. However, the arrow is shown at the very bottom line, but should be centered vertically:
However, if I print the unicode character using the standard output, everything is fine:
public class Main {
public static void main(String[] args) {
System.out.println("A" + Character.toString("\u2192".toCharArray()[0]));
}
}
How I can enforce the right arrow to be centered in the TextView, too? My TextView already uses android:gravity="center_vertical|left"
.
Update: I use Android Studio 3.0.1 and Android SDK 26. The XML code of the TextView:
<TextView
android:id="@+id/my_text_view"
android:layout_width="match_parent"
android:fontFamily="monospace"
android:gravity="center_vertical|left"
android:textSize="26sp" />
Filling the TextView in the code:
TextView textView = findViewById(R.id.my_text_view);
textView.setText("A" + Character.toString("\u2192".toCharArray()[0]) + "B");
Unicode Character “↑” (U+2191)
SetText(String, TextView+BufferType) Sets the text to be displayed using a string resource identifier.
Just add a \n to your text. This can be done directly in your layout file, or in a string resource and will cleanly break the text in your TextView to the next line.
Since Android's Roboto font doesn't seem to contain arrow glyphs, this would cause it to display a character from a fallback font if possible (though I couldn't find the documentation of this behavior). I'm guessing that the "fallback" arrow on your device is lying on the baseline for some reason.
However, there's a trick that might help you: You can include an image of the special character and insert the image into your output text.
First, add a new Drawable resource file (ic_arrow.xml
) in res/drawable
, and copy-paste this into it:
<vector android:alpha="0.78" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M3,12L21.5,12"
android:strokeColor="#000000" android:strokeWidth="1"/>
<path android:fillColor="#FF000000" android:pathData="M21,12L17,8"
android:strokeColor="#000000" android:strokeWidth="1"/>
<path android:fillColor="#FF000000" android:pathData="M21,12L17,16"
android:strokeColor="#000000" android:strokeWidth="1"/>
</vector>
Now, we'll need to embed the arrow image into a SpannableString that you can display in your TextView. I'll walk you through the (surprisingly many) lines of code that we'll need to accomplish this:
Get the Drawable resource so that we can make it the correct size before displaying it.
Drawable arrow = ContextCompat.getDrawable(this, R.drawable.ic_arrow);
Figure out what size the arrow needs to be, based on the font metrics.
Float ascent = textView.getPaint().getFontMetrics().ascent;
This is negative (for reasons), so make it positive.
int h = (int) -ascent;
Finally, we can set the bounds of the Drawable to match the text size.
arrow.setBounds(0,0,h,h);
Next, create a SpannableString initialized with our text. I just used *
for a placeholder here, but it could be any character (or a blank space). If you're planning to concatenate different bits together along with multiple arrow images, use a SpannableStringBuilder
for this.
SpannableString stringWithImage = new SpannableString("A*B");
Now, we can finally insert the image into the span (using a new
ImageSpan with our "arrow" image, aligned with the baseline).
The 1
and 2
are zero-based start and end indices (telling where
to insert the image), and SPAN_EXCLUSIVE_EXCLUSIVE
indicates that
the span doesn't expand to include inserted text.
stringWithImage.setSpan(new ImageSpan(arrow, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Finally, we can display the text (including the custom arrow image):
textView.setText(stringWithImage);
Altogether, the code should look like this.
TextView textView = findViewById(R.id.my_text_view);
Drawable arrow = ContextCompat.getDrawable(this, R.drawable.ic_arrow);
Float ascent = textView.getPaint().getFontMetrics().ascent;
int h = (int) -ascent;
arrow.setBounds(0,0,h,h);
SpannableString stringWithImage = new SpannableString("A*B");
stringWithImage.setSpan(new ImageSpan(arrow, DynamicDrawableSpan.ALIGN_BASELINE), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(stringWithImage);
I wish Android had a better way to do this sort of thing, but at least it can be done somehow. The resulting TextView should look like this:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With