Android TextView's subscript being clipped off - android

The Android TextView clips off my text subscripts (see image below) even when I use android:layout_height="wrap_content" for the TextView.
Is there a fix/work-around for this?
P/S: Superscripts work fine
Note: padding doesn't work.
I tried even adding a padding of 50dip but it did not help.
I can use an absolute height such as 50dip but that messes everything up when I need text to wrap around.
Sample Code:
mtTextView.setText(Html.fromHtml("HC0<sub>3</sub>"));

Most answers suggest to add paddings or to use smaller sub/superscripts. These might be serviceable workarounds, but they don't really solve the problem. Ideally, we want Android to take the sub/superscript into account when calculating line height. I think I found how to do it, and I'm sharing it for people googling this issue.
SpannableStringBuilder sb = new SpannableStringBuilder("X2");
sb.setSpan(new SuperscriptSpan(), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(sb, BufferType.SPANNABLE);
The trick is in BufferType.SPANNABLE. Apparently it makes TextView pay more attention to the markup and calculate line heights properly.

This solution worked for me.
Superscripted text is usually made smaller when the browser renders it, that doesn't seem to happen here so you can replicate that (and solve this problem) by doing this:
someTextView.setText(Html.fromHtml("Some text<sup><small>1</small></sup>"));

For subscript a slight variation to the above suggestion is needed, two small tags:
textView.setText(Html.fromHtml(
"HCO<sub><small><small>3</small></small></sub>));

android:lineSpacingExtra="4dp" should solve it
this will add extra line spacing below your text, and keep subscript from getting cutoff. I haven't tried it with superscript so it might now fix that.

I had the same issue, so after reading the posts, I found this to be working.
Example : H2O
simply use :
textView.setText(Html.fromHtml("H<sub>2</sub>O"),BufferType.SPANNABLE);
BufferType.SPANNABLE is important as it will tell textview to consider the superscript span.
If you are using custom tag handler for HTML you can also use it like this:
textView.setText(Html.fromHtml(data, null, new CustomHtmlTagHandler(),BufferType.SPANNABLE);
Hope it helps someone looking for same problem.

I'm displaying fractions and mixed numbers so I'm using both super and subscripting together. The Html.fromHtml didn't work for me, it either clipped the top or the bottom.
Oddly, mixed numbers worked correctly, but fractions by themselves did not.
I ended up using a SpannableString with a SubscriptSpan or a SuperscriptSpan, then setting the font size in a TextAppearanceSpan.
Once I had done that I had to expand the height of the TextView as well.
TextView number = (TextView)findViewById(R.id.number);
String temp = "1 1/2";
SpannableString s = new SpannableString(temp);
// if the string has a fraction in it, superscript the numerator and subscript the denominator
if (temp.indexOf('/') != -1)
{
int len = temp.length();
s.setSpan(new SuperscriptSpan(), len - 3, len - 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new TextAppearanceSpan(null, 0, fractionFontSize, null, null), len - 3, len - 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new TextAppearanceSpan(null, 0, fractionFontSize, null, null), len - 2, len - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new SubscriptSpan(), len - 1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
s.setSpan(new TextAppearanceSpan(null, 0, fractionFontSize, null, null), len - 1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
number.setText(s);
Then I had to expand the height:
RelativeLayout.LayoutParams parms = (RelativeLayout.LayoutParams)number.getLayoutParams();
Rect frame = CalcSize(number.getTextSize(), quantityMaxString);
parms.height = frame.height() + fractionAdjustment;
number.setLayoutParams(parms);
CalcSize returns a bounding rectangle of the largest string in the array of display elements.
fractionAdjustment is an emperically selected value that works for the selected font size adjusted for screen geometry.
Note: This is TextView is inside a ListView, so that might have some impact as well.
// calculate the field dimensions, given the font size and longest string
private static Rect CalcSize(float fontSize, String maxString)
{
Rect bounds = new Rect();
paint.setTypeface(Typeface.DEFAULT);
paint.setTextSize(fontSize);
paint.getTextBounds(maxString, 0, maxString.length(), bounds);
return bounds;
}
Empirical values:
fractionAdjustment = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 14, resources.getDisplayMetrics());
fractionFontSize = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 11, resources.getDisplayMetrics());

I have faced the same issue in ICS and below android versions. I fixed the issue by a simple step
Give a minimum height to the Text View . It will fix the problem.
You can set minimum height through xml .
android:minHeight="30dp"
Or dynamically
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
tv.setMinHeight(52);
}

This worked for me along with the Small tag.
Inside the TextView add
android:paddingBottom="1dp"
Use the small Tag after the subscript
yourTextView.setText(Html.fromHtml("" +" Hey< sub >< small >2< /small > < /sub >"));
Note Please note , step 1 is important , My text was still cutting down in some case,using paddingBottom resolved it.
Don't forget to remove the spaces in sub and small tags that are present in my answer :)

The More number of <small> </small> tags in there, the smaller the subscript will get and you should be able to see it without being clipped.
Eg: H2O
Html.fromHtml("H<sub><small><small><small>2</small></small></small></sub>O");

Related

Duplicate images appear in EditText after insert one ImageSpan in Android 4.x

I have below code slice to insert a bitmap into an EditText widget. With Android 5.x, it works fine, but with Android 4.x, duplicate images will show after insert one bitmap. Dose anyone know how to fix this with Android 4.x?
insertPicIntoEditText(getBitmapSpannable(resized_bm, upload_uri));
private SpannableString getBitmapSpannable(Bitmap pic, String uri_string) {
SpannableString ss = new SpannableString(uri_string);
ImageSpan span = new ImageSpan(this, pic);
ss.setSpan(span, 0, uri_string.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return ss;
}
private void insertPicIntoEditText(SpannableString ss) {
Editable et = mContentEditor.getText();
int start = mContentEditor.getSelectionStart();
et.insert(start, ss);
et.insert(start + ss.length(), "\n");
mContentEditor.setText(et);
mContentEditor.setSelection(start + ss.length() + 1);
}
This was being caused by the height set in setBounds being greater than that of the Bitmap the drawable was created from earlier in the activity. When this occurs there are two things that seem to happen..
First, if the size only slightly (I haven't extensively tested this once I got it working so I'm unsure of exact figures) exceeds the size of the Bitmap then a large blank space is added to the span, this blank space is the same size as the bitmap inserted.
Second, an extra copy of the Drawable is added to the span, directly below the blank space.
The resolution was relatively simple.. Ensure that the Bitmap used to create the Drawable was set to the intended final size before creating the Drawable and calling setBounds.
This may not work in all cases but worked for me and hopefully will be helpful for someone.
I also noticed images are repeated if the spannable string has line breaks.

Add paragraph dynamically at the bottom with iText library in android

I am using below code but this is not working its show only one line in bottom while I want a paragraph in bottom in every page.
public void onEndPage(PdfWriter writer, Document document) {
Rectangle rect = writer.getBoxSize("art");
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_LEFT, new Phrase(ActivityWaTestamentInput.pDFHeaderText,headerFont),
rect.getLeft(), rect.getTop(), 0);
ColumnText.showTextAligned(writer.getDirectContent(),
Element.ALIGN_LEFT, new Paragraph(ActivityWaTestamentInput.pDFFooterText,footerFont),
rect.getLeft() , rect.getBottom() - 18, 0);
As documented, ColumnText.showTextAligned() will only show one line. If you need multiple lines, then you can also use ColumnText, but you need to use it in a different way.
Please download the free ebook The Best iText Questions on StackOverflow. In the section "Absolute positioning of text", you'll find several examples that involve ColumnText.
These are some questions and answers you should check:
How to fit a String inside a rectangle?
How to draw a rectangle around multiline text
iText placement of Phrase within ColumnText
This is a code snippet from one of the answers to these questions:
ColumnText ct = new ColumnText(cb);
ct.setSimpleColumn(120f, 500f, 250f, 780f);
Paragraph p = new Paragraph("This is a long paragraph that doesn't"
+ "fit the width we defined for the simple column of the"
+ "ColumnText object, so it will be distributed over several"
+ "lines (and we don't know in advance how many).");
ct.addElement(p);
ct.go();
I hardcoded the position:
llx = 120;
lly = 500;
urx = 250;
ury = 780;
This is a rectangle with lower left corner (120, 500), a width of 130 and a height of 380. You will need to adapt these values if you want the text to appear at the bottom of the page.

Prevent Android's TextView from breaking links

This question is probably the same as this one, but since none of its answers really solve the problem, I'll ask again.
My app has a TextView that occasionally will display very long URLs. For aesthetic reasons (and since the URLs contain no spaces), the desirable behaviour would be to fill each line completely before jumping to the next one, something like this:
|http://www.domain.com/som|
|ething/otherthing/foobar/|
|helloworld |
What happens instead, is the URL being broke near the bars, as if they were spaces.
|http://www.domain.com/ |
|something/otherthing/ |
|foobar/helloworld |
I tried extending the TextView class and adding a modified version of the breakManually method (found here) to trick the TextView and do what I need, calling it on onSizeChanged (overridden). It works fine, except for the fact that the TextView is inside a ListView. When this custom TextView is hidden by the scrolling and brought back, its content goes back to the original breaking behaviour, due to the view being re-drawn without onSizeChanged being called.
I was able to work-around this problem by calling breakManually inside onDraw. This presents the expected behaviour at all times, but at a high performance cost: since onDraw is called whenever the ListView is scrolled and the breakManually method isn't exactly "lightweight", the scrolling gets unacceptably laggy, even on a high-end quad-core device.
Next step was to crawl through the TextView source code, trying to figure where and how it splits the text, and hopefully override it. This was a complete failure. I (a newbie) spent the whole day fruitlessly looking at code I mostly couldn't understand.
And that brings me here. Can someone please point me the right direction about what I should override (assuming that it's possible)? Or maybe there is a simpler way of achieving what I want?
Here's the breakManually method I mentioned. Due to the use of getWidth(), it only works if called after the view is measured.
private CharSequence breakManually (CharSequence text) {
int width = getWidth() - getPaddingLeft() - getPaddingRight();
// Can't break with a width of 0.
if (width == 0) return text;
Editable editable = new SpannableStringBuilder(text);
//creates an array with the width of each character
float[] widths = new float[editable.length()];
Paint p = getPaint();
p.getTextWidths(editable.toString(), widths);
float currentWidth = 0.0f;
int position = 0;
int insertCount = 0;
int initialLength = editable.length();
while (position < initialLength) {
currentWidth += widths[position];
char curChar = editable.charAt(position + insertCount);
if (curChar == '\n') {
currentWidth = 0.0f;
} else if (currentWidth > width) {
editable.insert(position + insertCount , "\n");
insertCount++;
currentWidth = widths[position];
}
position++;
}
return editable.toString();
}
To everyone who bothered reading this, thanks for your time.
If you're not using a monospace font, then in some cases it's not even possible to align it very well. Since you have no whitespaces in a URL, it's not likely that a Justify-like alignment will solve the problem. I suggest that you use a monospace font for that particular TextView. Then, decide on a fixed number of characters per line and break the string to display with "\n" after those many characters.
This isn't answering your question but it's the smoothest way possible, I guess.

chemistry formula on Android

I'm developing an android project and I want to render some chemistry formula.
I wrote the following code and I got the following result.
I create a custom string and show it in a textview.
But my question is this: Is this the best way to do this? And is there another way to handle that?
str = new SpannableString(Html.fromHtml("2H<sup>+</sup> + So<sub size = 2>4</sub><sup size = 2>2-</sup> --> H<sub size =2>2</sub>So<sub size = 2>4</sub>"));
ss1.setSpan(new RelativeSizeSpan(0.6f), 2,3, 0); // set size
ss1.setSpan(new RelativeSizeSpan(0.6f), 8,11, 0); // set size
ss1.setSpan(new RelativeSizeSpan(0.6f), 17,18, 0); // set size
ss1.setSpan(new RelativeSizeSpan(0.6f), 20,21, 0); // set size
TempF.setText(ss1,TextView.BufferType.SPANNABLE);
I don't think there is a better way.
Unfortunately Html.fromHtml() ignores <font size="n"> tags, so these spans need to be added manually, as you have done.
I had a similar problem to solve. I created a simple library by extending WebView and using JavaScript in the background. https://github.com/RanaRanvijaySingh/EquationView
String strChem = "Chemistry: \\(\\ce{CO2 + C -> 2 CO}\\)";
EquationView equationViewChem = findViewById(R.id.equationViewChem);
equationViewChem.setText(strChem);
In your layout file:
<com.rana.equationview.EquationView
android:id="#+id/equationViewChem"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Hope this helps someone.
I think you have found a solution that fits you.
Perhaps, if there is a rule to know when a symbol will be a subscript or superscript, or the spansize you have to use, you can write a method to parse a given a formula privided as a string and automatically provide the formated html output.
To do this, and assuming there are specific rules, you can use a finite state machine that will "compile" a text string to the html output you need.
If you are looking for something like this let me know and we may be able to work it out.
Another option would be to see if you find (in Internet) a font that meets your needs (Or perhaps build your own). You can import a font putting the font file in the assets folder and setting it from there in the source.

Editable.setSpan() on Gingerbread: start and end parameters ignored

I want to highlight two parts of the text in EditText. Here's how I do it:
EditText etOpenAnswer;
int correctPartEndIndex;
UnderlineSpan correctPartSpan=new UnderlineSpan();
StrikethroughSpan incorrectPartSpan=new StrikethroughSpan();
if (etOpenAnswer.length()>=correctPartEndIndex) {
etOpenAnswer.getText().setSpan(correctPartSpan, 0, correctPartEndIndex, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
etOpenAnswer.getText().setSpan(incorrectPartSpan, correctPartEndIndex, etOpenAnswer.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
}
So first part of the text is from 0 to correctPartEndIndex (it is underlined), second is from correctPartEndIndex to the end (it is striked through).
Everything works as expected on ICS and later, but on 2.2 and 2.3.3 both spans are applied to the whole text (text is underlined and striked through at the same time), which makes me think start and end parameters are just ignored.
What am I doing wrong?

Categories

Resources