Show source code in TextView correctly indented and parsed - android

Is it possible to parse HTML code in a verbatim mode or something similar so that the source code fragments that eventually may appear (enclosed between pre and code HTML tags) can be displayed properly?
What I want to do is show source code in a user-friendly mode (easy to distinguish from the rest of the text, keep indentation, etc.), as Stack Overflow does :)
It seems that Html.fromHtml() supports only a reduced subset of HTML tags.

TextView will never succeed supporting all the html formating and styling you would want it to. Use WebView instead.
TextView is native and more lightweight, but exactly because of its lightweightedness it will not understand some of the directives you describe.

Finally I preparsed by myself the HTML code received, since Html.fromHtml does not support the pre and code tags, y replaced them with my custom format and pre-parsed the code inside those tags replacing "\n" with <br/> and " " with .
Then I send the results to Html.fromHtml, and the result is just fine:
public class HtmlParser {
public static Spanned parse(String text) {
if (text == null) return null;
text = parseSourceCode(text);
Spanned textSpanned = Html.fromHtml(text);
return textSpanned;
}
private static String parseSourceCode(String text) {
if (text.indexOf(ORIGINAL_PATTERN_BEGIN) < 0) return text;
StringBuilder result = new StringBuilder();
int begin;
int end;
int beginIndexToProcess = 0;
while (text.indexOf(ORIGINAL_PATTERN_BEGIN) >= 0) {
begin = text.indexOf(ORIGINAL_PATTERN_BEGIN);
end = text.indexOf(ORIGINAL_PATTERN_END);
String code = parseCodeSegment(text, begin, end);
result.append(text.substring(beginIndexToProcess, begin));
result.append(PARSED_PATTERN_BEGIN);
result.append(code);
result.append(PARSED_PATTERN_END);
//replace in the original text to find the next appearance
text = text.replaceFirst(ORIGINAL_PATTERN_BEGIN, PARSED_PATTERN_BEGIN);
text = text.replaceFirst(ORIGINAL_PATTERN_END, PARSED_PATTERN_END);
//update the string index to process
beginIndexToProcess = text.lastIndexOf(PARSED_PATTERN_END) + PARSED_PATTERN_END.length();
}
//add the rest of the string
result.append(text.substring(beginIndexToProcess, text.length()));
return result.toString();
}
private static String parseCodeSegment(String text, int begin, int end) {
String code = text.substring(begin + ORIGINAL_PATTERN_BEGIN.length(), end);
code = code.replace(" ", " ");
code = code.replace("\n","<br/>");
return code;
}
private static final String ORIGINAL_PATTERN_BEGIN = "<pre><code>";
private static final String ORIGINAL_PATTERN_END = "</code></pre>";
private static final String PARSED_PATTERN_BEGIN = "<font color=\"#888888\"><tt>";
private static final String PARSED_PATTERN_END = "</tt></font>";
}

Related

html in textview android studio

I have textview in which i want to display html which i get from a server so i use like this
Spanned html = Html.fromHtml(content.toString(), Html.FROM_HTML_MODE_COMPACT);
but it did not display well because the span did not remove the white spaces, so i tried this
int i = html.length();
while(--i >= 0) {
char test = html.charAt(i);
if (test != 0 && !Character.isWhitespace(test)){
break;
}
}
and this
CharSequence sub = (html.subSequence(0, descriptionWithOutExtraSpace.length()))
and this
String withSpace = html.toString();
String descriptionWithOutExtraSpace = new String(html.toString()).trim();
but all came to the same part that the spaces is removed but it isn't html any more and for example a url is not linked any more
some one have any recommendation for me?
in the and i did it like this
Spanned html = Html.fromHtml(content.toString(), Html.FROM_HTML_MODE_COMPACT);
String descriptionWithOutExtraSpace = new String(html.toString()).trim();
CharSequence sub = (html.subSequence(0, descriptionWithOutExtraSpace.length()));
holder.binding.contentView.setText(sub);}

Multi html formatting in Snackbar

Im trying to add multiple html foreground formattings of a snackbar text programmatically.
In my strings.xml:
<string name="html_test">The html entries %1$s and %2$s are looking different.</string>
How i try to format them:
public static Spanned getString(Context p_Context, int p_iResID, int p_iColor, String... p_Items) {
int l_iColor = ContextCompat.getColor(p_Context, p_iColor);
String l_HexColor = Integer.toHexString(l_iColor);
String l_Before = "<font color=" + l_HexColor + ">";
String l_After = "</font>";
Object[] l_Items = new String[p_Items.length];
for(int i = 0; i < p_Items.length; i++) {
l_Items[i] = l_Before + p_Items[i] + l_After;
}
return Html.fromHtml(p_Context.getString(p_iResID, l_Items));
}
How i call the function:
getString(getContext(), R.string.html_test, R.color.blue, "Test1", "Test2");
Then i create a snackbar and pass the html formated text.
Snackbar l_SnackBar = Snackbar.make(p_Root, p_Text, p_iSnackBarLenght);
l_SnackBar.getView().setBackgroundColor(p_iBGColor);
return l_SnackBar;
The problem is that there is no html formatting of the two entries i passed into getString().
I don't want to use ![CDATA... because i read that there are some problems with the formatting.
I didn't found a solution to format the foreground parameters of getString() with html. Now im doing it with SpannableString which works for me. My code looks like this now:
public static SpannableString getString(Context p_Context, int p_iResID, int p_iColor, Object... p_Items) {
String l_Feedback = p_Context.getString(p_iResID, p_Items);
SpannableString l_SpannableFeedback = new SpannableString(l_Feedback);
int l_iItemColor = ContextCompat.getColor(p_Context, p_iColor);
for(Object l_Object : p_Items) {
String l_Item = (String) l_Object;
int l_iStartIndex = l_Feedback.indexOf(l_Item);
int l_iEndIndex = l_iStartIndex + l_Item.length();
l_SpannableFeedback.setSpan(new ForegroundColorSpan(l_iItemColor), l_iStartIndex, l_iEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
l_SpannableFeedback.setSpan(new StyleSpan(Typeface.BOLD), l_iStartIndex, l_iEndIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return l_SpannableFeedback;
}

Prevent line-break in TextView

In my Android app I have a text view that displays text containing special characters. The TextView somehow automatically breaks strings at the characters '/' and '-'.
For example, the string "aaaaaaa/bbb-ccccc/ddd" is displayed as
aaaaaaa/
bbb-
ccccc/
ddd
However, I would like to display it without any linebreaks except the one at the boundaries of the view, i.e., like this:
aaaaaaa/bb
bb-ccccc/d
dd
Is there any way to deactivate the automatic line-breaks or to escape these characters? I already tried escaping with \uFEFF without success.
Keep your textview attribute
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Define Your string in string.xml
<string name="Username"> aaaaaaa\/bb\nbb\-ccccc\/d\ndd</string>
Maybe this is a solution: https://stackoverflow.com/a/22337074/3472905
I've added the slash as mentioned:
public class WordBreakTransformationMethod extends ReplacementTransformationMethod {
private static WordBreakTransformationMethod instance;
private WordBreakTransformationMethod() {}
public static WordBreakTransformationMethod getInstance() {
if (instance == null) {
instance = new WordBreakTransformationMethod();
}
return instance;
}
private static char[] dash = new char[]{'-', '\u2011'};
private static char[] space = new char[]{' ', '\u00A0'};
private static char[] slash = new char[]{'/', '\u2215'};
private static char[] original = new char[]{dash[0], space[0], slash[0]};
private static char[] replacement = new char[]{dash[1], space[1], slash[1]};
#Override
protected char[] getOriginal() {
return original;
}
#Override
protected char[] getReplacement() {
return replacement;
}
}
Its is a new thing in Android 6.
Try adding this to your TextView xml layout
android:hyphenationFrequency="none"
Android TextView follows the standard Unicode line break algorithm: http://www.unicode.org/reports/tr14/tr14-45.html
Excerpt: / Prevent a break before, and allow a break after
You can work around this by placing the 'word joiner' character (U+2060) after the slashes.
Example from strings.xml:
aaaaaaa/\u2060bbb-ccccc/\u2060ddd
You can also try using android:breakStrategy="balanced" to keep the lines roughly the same length.
this is work's for me in kotlin
object WordBreakTransformationMethod : ReplacementTransformationMethod() {
private val dash = charArrayOf('-', '\u2011')
private val space = charArrayOf(' ', '\u00A0')
private val slash = charArrayOf('/', '\u2215')
private val original = charArrayOf(dash[0], space[0], slash[0])
private val replacement = charArrayOf(dash[1], space[1], slash[1])
override fun getOriginal() = original
override fun getReplacement() = replacement
}
//tv_text is TextView
tv_text.apply {
transformationMethod = WordBreakTransformationMethod
text = item.text
}
There no ready solution and no such thing as "wrap text by letters in TextView" the only way to do it in a good way is to extend TextView and modify Paint's breakText(String text, boolean measureForwards, float maxWidth, float[] measuredWidth) function.
Also, you can calculate TextView size in pixels, calculate width of one letter in pixels, then find number of letters (X) that will fit in one line and then insert linebreak after each X letters
you probably can use the Lines attribute or its counter-part method setLines(int)
I have tested the following code. You can even convert it into a function:
String specialString = "a/b/-c/d-d";
String[] specialArray = specialString.split("/");
String str = "";
for(int i = 0; i < specialArray.length - 1; i++){
str = str + specialArray[i] + Character.toString((char) 47);
}
str = str + specialArray[specialArray.length - 1];
specialArray = str.split("-");
str = "";
for(int i = 0; i < specialArray.length - 1; i++){
str = str + specialArray[i] + Character.toString((char) 45);
}
str = str + specialArray[specialArray.length - 1];
textView.setText(str);
Now the text does not escape
You can calculate the size of a text this way:
String text = "This is my text";
Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
textPaint.setTextSize(14.0f);
Rect bounds = new Rect();
textPaint.getTextBounds(text, 0, text.length(), bounds);
bounds.width() // width in pixels
bounds.height() // height in pixels
Based on these values you could break up the text in pieces and insert newline characters.

Anything better than JSoup for Android?

What I want to do...
I have a webview in my android app. I get a huge html content from the server as a string and a search string from the application user(the android phone user). Now I break the search string and create a regex out of it. I want all the html content that matches my regex to be highlighted when I display it into my WebView.
What I tried...
Since it is html, I just want to wrap the regex matched words into a pair of tags with yellow background.
Simple regex and replaceAll on the html Content that i get. Very wrong because it screws and replaces even what is inside the '<' and '>'.
I tried using Matcher and Pattern combo. It is difficult to omit what is inside the tags.
I used JSOUP Parser and it worked!
I traverse the html using NodeTraversor class. I used Matcher and Pattern classes to find and replace matched words with tags as i wanted to do.
But it is very slow. And I basically want to use it on Android and the size of it is like 284kB. I removed some unwanted classes and it is now 201kB but it is still too much for an android device. Additionally, the html content can be really large. I looked into JSoup source as well. It kind of iterates over every single character when it parses. I do not know whether all the parsers do the same but it is definitely slow for large html documents.
Here is my code -
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Highlighter {
private String regex;
private String htmlContent;
Pattern pat;
Matcher mat;
public Highlighter(String searchString, String htmlString) {
regex = buildRegexFromQuery(searchString);
htmlContent = htmlString;
pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
}
public String getHighlightedHtml() {
Document doc = Jsoup.parse(htmlContent);
final List<TextNode> nodesToChange = new ArrayList<TextNode>();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
mat = pat.matcher(text);
if(mat.find()) {
nodesToChange.add(textNode);
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
for (TextNode textNode : nodesToChange) {
Node newNode = buildElementForText(textNode);
textNode.replaceWith(newNode);
}
return doc.toString();
}
private static String buildRegexFromQuery(String queryString) {
String regex = "";
String queryToConvert = queryString;
/* Clean up query */
queryToConvert = queryToConvert.replaceAll("[\\p{Punct}]*", " ");
queryToConvert = queryToConvert.replaceAll("[\\s]*", " ");
String[] regexArray = queryString.split(" ");
regex = "(";
for(int i = 0; i < regexArray.length - 1; i++) {
String item = regexArray[i];
regex += "(\\b)" + item + "(\\b)|";
}
regex += "(\\b)" + regexArray[regexArray.length - 1] + "[a-zA-Z0-9]*?(\\b))";
return regex;
}
private Node buildElementForText(TextNode textNode) {
String text = textNode.getWholeText().trim();
ArrayList<MatchedWord> matchedWordSet = new ArrayList<MatchedWord>();
mat = pat.matcher(text);
while(mat.find()) {
matchedWordSet.add(new MatchedWord(mat.start(), mat.end()));
}
StringBuffer newText = new StringBuffer(text);
for(int i = matchedWordSet.size() - 1; i >= 0; i-- ) {
String wordToReplace = newText.substring(matchedWordSet.get(i).start, matchedWordSet.get(i).end);
wordToReplace = "<b>" + wordToReplace+ "</b>";
newText = newText.replace(matchedWordSet.get(i).start, matchedWordSet.get(i).end, wordToReplace);
}
return new DataNode(newText.toString(), textNode.baseUri());
}
class MatchedWord {
public int start;
public int end;
public MatchedWord(int start, int end) {
this.start = start;
this.end = end;
}
}
}
Here is how I call it -
htmlString = getHtmlFromServer();
Highlighter hl = new Highlighter("Hello World!", htmlString);
new htmlString = hl.getHighlightedHTML();
I am sure what i'm doing is not the most optimal way. But I can't seem to think of anything else.
I want to
- reduce the time it takes to highlight it.
- reduce the size of library
Any suggestions?
How about highlighting them using javascript?
You know, everybody love javascript, and you can find example like this blog.
JTidy and HTMLCleaner are aloso among the best Java HTML Parser.
see
Comparison between different Java HTML Parser
and
What are the pros and cons of the leading Java HTML parsers?

Android: Convert first letter of string to lower case

I'm looking for a way to convert the first letter of a string to a lower case letter. The code I'm using pulls a random String from an array, displays the string in a text view, and then uses it to display an image. All of the strings in the array have their first letter capitalized but image files stored in the app cannot have capital letters, of course.
String source = "drawable/"
//monb is randomly selected from an array, not hardcoded as it is here
String monb = "Picture";
//I need code here that will take monb and convert it from "Picture" to "picture"
String uri = source + monb;
int imageResource = getResources().getIdentifier(uri, null, getPackageName());
ImageView imageView = (ImageView) findViewById(R.id.monpic);
Drawable image = getResources().getDrawable(imageResource);
imageView.setImageDrawable(image);
Thanks!
if (monb.length() <= 1) {
monb = monb.toLowerCase();
} else {
monb = monb.substring(0, 1).toLowerCase() + monb.substring(1);
}
public static String uncapitalize(String s) {
if (s!=null && s.length() > 0) {
return s.substring(0, 1).toLowerCase() + s.substring(1);
}
else
return s;
}
Google Guava is a java library with lot of utilities and reusable components. This requires the library guava-10.0.jar to be in classpath. The following example shows using various CaseFormat conversions.
import com.google.common.base.CaseFormat;
public class CaseFormatTest {
/**
* #param args
*/
public static void main(String[] args) {
String str = CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, "studentName");
System.out.println(str); //STUDENT_NAME
str = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "STUDENT_NAME");
System.out.println(str); //studentName
str = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, "student-name");
System.out.println(str); //StudentName
str = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_HYPHEN, "StudentName");
System.out.println(str); //student-name
}
}
Output Like:
STUDENT_NAME
studentName
StudentName
student-name

Categories

Resources