HTML Formatted String inserting into TextViews and EditText - android

All,
I have a database that will store an HTML tagged text to retain formatting information from an EditText. I create this string using HTML.toHtml(EditText.getText). I notice this method wraps whatever Spanned Text is put in it with <p> and </p>. The issue with that is when I got to use the method HTML.fromHtml(HTMLFormattedString) and then use the setText method of either a TextView or EditText there are two extra lines at the end of my actual text, which makes sense because that is how the paragraph tag works with HTML.
My question is is there anyway to make the textView or EditText shrink to not display the extra blank lines? What is the simplest way to do this? I have experimented with just removing the last <p> and </p>, but that only works if the user did not enter 3 or more new lines with the return key.

I ended up searching for white space at the end of the spanned text that was created and removed it. This took care of extra spaces due to the <p> </p> and was less time consuming than overriding the mentioned class to achieve the same results.
public SpannableStringBuilder trimTrailingWhitespace(
SpannableStringBuilder spannableString) {
if (spannableString == null)
return new SpannableStringBuilder("");
int i = spannableString.length();
// loop back to the first non-whitespace character
while (--i >= 0 && Character.isWhitespace(spannableString.charAt(i))) {
}
return new SpannableStringBuilder(spannableString.subSequence(0, i + 1));
}

Well this is just a round about approach. I had the same issue. And you are provided with two options,
1)As you said that paragraph tag works the way what you have suspected. What it does , it appends two "\n" values to the end of each <\p> tag. So you can convert the html to string and remove the last two characters which are usually two "\n"s
or
2) You have get into the Html Class itself. That is, you have to override the HTML class and look for handleP(SpannableStringBuilder text) and change its core logic a little bit.
private static void handleP(SpannableStringBuilder text) {
int len = text.length();
if (len >= 1 && text.charAt(len - 1) == '\n') {
if (len >= 2 && text.charAt(len - 2) == '\n') {
return;
}
text.append("\n");
return;
}
if (len != 0) {
text.append("\n\n");
}
}
As you can see here, it appends two "\n" in len!=0 which is were you have to do the change.

Related

Android HtmlCompat.toHtml(Spanned) returns improperly nested HTML tags

I have an Edittext (binding.text using view binding) that contains text styled with StyleSpans, i.e. bold and italics. To save the formatted text, I use HtmlCompat.toHtml(spannable) in Kotlin to convert it into HTML.
var htmlString = HtmlCompat.toHtml(SpannableString(binding.text.text), HtmlCompat.FROM_HTML_MODE_LEGACY)
However, the HTML returned is improperly nested if the text has bold and italics applied to it at the same time.
Hello World: outputs <p dir="ltr><b><i>Hello world</b></i></p>
As you can see, the tags applied are <b><i> </b></i> instead of <b><i> </i></b>.
When the text is not formatted, or either in bold or italics, then proper HTML is returned:
Hello World: outputs <p dir="ltr>Hello world</p>
Hello World: outputs <p dir="ltr><b>Hello world</b></p>
I suppose the function likes to put <b> and </b> first as opposed to <i> and </i>, but this causes weird results as shown.
So the question: How do I get the function to return correctly formatted HTML?
You are right about the ordering, although the HTML should still display correctly in a browser. The offending code is in Html.java. Here is where the translations occur for <b></b> and <i></i>:
...
for (int j = 0; j < style.length; j++) {
...
if (style[j] instanceof StyleSpan) {
int s = ((StyleSpan) style[j]).getStyle();
if ((s & Typeface.BOLD) != 0) {
out.append("<b>");
}
if ((s & Typeface.ITALIC) != 0) {
out.append("<i>");
}
}
...
for (int j = style.length - 1; j >= 0; j--) {
,,,
if (style[j] instanceof StyleSpan) {
int s = ((StyleSpan) style[j]).getStyle();
if ((s & Typeface.BOLD) != 0) {
out.append("</b>");
}
if ((s & Typeface.ITALIC) != 0) {
out.append("</i>");
}
}
style is an array of spans. This code scans forward through the spans to place the opening tags and backward to close the tags. So, it looks like if your bold span and italic spans are different spans spanning the same text, then this code will produce properly nested tags.
However, if one StyleSpan specifies both italic and bold, then the tag order will be as you report <b><i></b></i>. The code that outputs the closing tags should place <i> before b. This looks like a bug and should be reported.
I assume that you have one StyleSpan that is a bolded-italics span. The fix would be to split that span into two separate spans - a bold span and an italics span. The code should then work as expected.

How to get length of first line in Edittext android?

I have a multi lines Edittext with a text (don't content "\n"), a font size (sp)
and the length of text > Edittext.width().
I want to get length of the first line in EditText.
How can I do it?
You can see the photo
One option could be to read the text and then get the index of the newline character, which is essentially the length of the string prior to it:
int firstLineLength = myEditText.getText().toString().indexOf("\n");
As an alternative, if you ever need to do this with other lines you can simply split the whole string based on the newline character:
String[] lines = myEditText.getText().toString().split("\n");
EDIT
Keep in mind that indexOf() will return -1 if an occurrence is not found. So if your EditText has one and only one string, you'll get a -1 line length so be prepared to check against that:
int lineEndIndex = myEditText.getText().toString().indexOf("\n");
int firstLineLength;
if(lineEndIndex == -1) {
firstLineLength = myEditText.getText().toString().length();
} else {
firstLineLength = lineEndIndex;
}

Determine if content in editText is a "." ONLY

I have written a calculator type app. My mates found that entering single decimal points only into the editText's makes the app crash. Decimal numbers and integers work fine, but I get a number format exception when .'s are entered.
I want to check if a single . has been placed in an editText, in order for me to display a toast telling the user to stop trying to crash the app.
My issue is that a . doesn't have a numerical value...
You can wrap it in a try/catch which should be done anyway when parsing text. So something like
try
{
int someInt = Integer.parseInt(et.getText().toString());
// other code
}
catch (NumberFormatException e)
{
// notify user with Toast, alert, etc...
}
This way it will protect against any number format exception and will make the code more reusable later on.
You can treat .1 as 0.1 by the following.
String text = et.getText().toString();
int len = text.length();
// Do noting if edit text just contains a "." without numbers
if(len==0 || (len==1 && text.charAt(0).equals(".")))
return;
if(text.charAt(0).equals(".") && text.length() > 1) {
text = "0" + text;
}
// Do your parsing and calculations

Format Android EditText to specific pattern

I need the user to enter text in an EditText according to this specfic pattern:
123.456-7890-123.456
The user can input any number of integers, so they could as well enter 123.456-7
I do not want the user to enter . or - just the numbers, like an input mask.
Also the numeric keyboard should only show.
I've searched StackOverflow extensively and have seen examples that use InputFilter, ChangedListener, TextWatcher but have not found anything simlar to what I'm trying to do. I've tried in various implementations of what I've found, but I'm inexperienced in using these so I may have overlooked something.
Any suggestions would be welcome.
You're going to have to use a TextWatcher and a regular expression pattern matcher to accomplish what you're trying to do.
This answer should be helpful: Android AutoCompleteTextView with Regular Expression?
You can create your own class that implements InputFilter. Then you would apply it as follows:
MyInputFilter filter = new MyInputFilter(...);
editText.setFilters(new InputFilter[]{filter});
Refer to the docs for how InputFilter is intended to work, then refer to the source code for some of the InputFilters used in Android for some ideas how to implement them.
After many failed attempts to implement InputFilter or Regular Expressions I opted for something a little more straight forward:
public void onTextChanged(CharSequence s, int start, int before, int count) {
String a = "";
String str = id.getText().toString();
String replaced = str.replaceAll(Pattern.quote("."),"");
replaced = replaced.replaceAll(Pattern.quote("-"),"");
char[] id_char = replaced.toCharArray();
int id_len = replaced.length();
for(int i = 0; i < id_len; i++) {
if(i == 2 || i == 12) {
a += id_char[i] + ".";
}
else if (i == 5 || i == 9) {
a += id_char[i] + "-";
}
else a += id_char[i];
}
id.removeTextChangedListener(this);
id.setText(a);
if(before > 0) id.setSelection(start);
else id.setSelection(a.length());
id.addTextChangedListener(this);
}
I don't know if this is the best approach but it does work. One problem I still haven't solved is how to handle cursor placement after the user deletes or inserts a number. If the user inserts the cursor somewhere in the EditText and enters a new number the cursor jumps to the end of the EditText. I would like the cursor to stay where it is at. Another problem if the user inserts the cursor within the EditText number and backspaces to delete a number, then the first key entry doesn't work and on the second key the number is entered. I can only guess this has to do with focus?
Use this: https://github.com/alobov/SimpleMaskWatcher.
Just set your mask for this watcher (###.###-####-###.###). It will add special symbols automatically and wont check your input string for being complete.
But showing the numeric keyboard you must handle by your own using android:inputType="number" tag for your EditText.

Remove extra line breaks after Html.fromHtml()

I am trying to place html into a TextView. Everything works perfectly, this is my code.
String htmlTxt = "<p>Hellllo</p>"; // the html is form an API
Spanned html = Html.fromHtml(htmlTxt);
myTextView.setText(html);
This sets my TextView with the correct html. But my problem is, having a tag in the html, the result text that goes into the TextView has a "\n" at the end, so it pushes my TextView's height higher than it should be.
Since its a Spanned variable, I can't apply regex replace to remove the "\n", and if I was to convert it into a string, then apply regex, I lose the functionality of having html anchors to work properly.
Does anyone know any solutions to remove the ending linebreak(s) from a "Spanned" variable?
Nice answer #Christine. I wrote a similar function to remove trailing whitespace from a CharSequence this afternoon:
/** Trims trailing whitespace. Removes any of these characters:
* 0009, HORIZONTAL TABULATION
* 000A, LINE FEED
* 000B, VERTICAL TABULATION
* 000C, FORM FEED
* 000D, CARRIAGE RETURN
* 001C, FILE SEPARATOR
* 001D, GROUP SEPARATOR
* 001E, RECORD SEPARATOR
* 001F, UNIT SEPARATOR
* #return "" if source is null, otherwise string with all trailing whitespace removed
*/
public static CharSequence trimTrailingWhitespace(CharSequence source) {
if(source == null)
return "";
int i = source.length();
// loop back to the first non-whitespace character
while(--i >= 0 && Character.isWhitespace(source.charAt(i))) {
}
return source.subSequence(0, i+1);
}
The spannable is a CharSequence, which you can manipulate.
This works:
myTextView.setText(noTrailingwhiteLines(html));
private CharSequence noTrailingwhiteLines(CharSequence text) {
while (text.charAt(text.length() - 1) == '\n') {
text = text.subSequence(0, text.length() - 1);
}
return text;
}
You can try this:
Spanned htmlDescription = Html.fromHtml(textWithHtml);
String descriptionWithOutExtraSpace = new String(htmlDescription.toString()).trim();
textView.setText(htmlDescription.subSequence(0, descriptionWithOutExtraSpace.length()));
you can use this lines ... totally works ;)
i know your problem solved but maybe some one find this useful .
try{
string= replceLast(string,"<p dir=\"ltr\">", "");
string=replceLast(string,"</p>", "");
}catch (Exception e) {}
and here is replaceLast ...
public String replceLast(String yourString, String frist,String second)
{
StringBuilder b = new StringBuilder(yourString);
b.replace(yourString.lastIndexOf(frist), yourString.lastIndexOf(frist)+frist.length(),second );
return b.toString();
}

Categories

Resources