I have an ImageSpan
inside of a piece of text. What I've noticed is that the surrounding text is always drawn at the bottom of the text line -- to be more precise, the size of the text line grows with the image but the baseline of the text does not shift upward. When the image is noticeably larger than the text size, the effect is rather unsightly.
Here is a sample, the outline shows bounds of the TextView
:
I am trying to have the surrounding text be centered vertically with respect to the image being displayed. Here is the same sample with blue text showing the desired location:
Here are the constraints that I'm bound by:
- I cannot use compound drawables. The images must be able to be shown between words.
- The text may be multiline depending on the content. I have no control over this.
- My images are larger than the surrounding text and I cannot reduce their size. While the sample image above is larger than the actual images (to demonstrate the current behavior), the actual images are still large enough that this problem is noticeable.
I've tried using the android:gravity="center_vertical"
attribute on the TextView, but this does not have any effect. I believe this just vertically centers the text lines, but within the text line the text is still drawn at the bottom.
My current train of thought is to create a custom span that shifts the baseline of the text based on the height of the line and the current text size. This span would encompass the entire text, and I would have to compute the intersection with the ImageSpan
s so I can avoid shifting the images as well. This sounds rather daunting and I'm hoping someone can suggest another approach.
Any and all help is appreciated!
After reading the source code of TextView, I think we can use the baseLine of eache text line which is "y". And it will work even if you set lineSpaceExtra.
My improved version: drawable font metrics zoomed relative to text font metrics. So that line spacing will be calculate correctly.
It might be a bit late but I've found a way to do it, no matter the image size. You need to create a class extending ImageSpan and override the methods
getSize()
andgetCachedDrawable()
(we don't need to change the last one, but this method fromDynamicDrawableSpan
is private and cannot be accessed in another way from the child class). IngetSize(...)
, you can then redefined the wayDynamicDrawableSpan
set the ascent/top/descent/bottom of the line and achieve what you want to do.Here's my class example:
Let me know if you have any trouble with that class!
I got a working solution by creating a class that inherits from ImageSpan.
Then modified draw implementation from DynamicDrawableSpan. At least this implementation works when my image height is less than font height. Not sure how this works for bigger images like yours.
Also had to reuse implementation from DynamicDrawableSpan as it was private.
And this is how I use it as static method that inserts image in front of the text.
Maybe not a good practice there considering localization, but works for me. To set images in the middle of the text, you'd naturally need to replace tokens in text with spans.
My answer tweaks the first answer. Actually I have tried both two methods above, and I don't think they are really center vertical. It would make the drawable more center if it's placed in between
ascent
anddescent
, rather thantop
andbottom
. So as to the second answer, it aligns the center of the drawable to the baseline of the text, rather than the center of that text. Here's my solution:I also rewrite
getSize
to keep the FontMetrics of drawable the same as other text, otherwise the parent view won't wrap the content correctly.