Add Bullets with proper formatting in Android - android

I wanted to show bullets in android text. I have added them successfully.
I search over internet and found that you can add bullets. but if text goes more than one line it does not follow proper spacing like html list does.
See Screenshot below.
I have used following code to add bullets.
String longDescription = "Enhanced bass performance.\n" +
"Lightweight headband enhances comfort and adds durability\n" +
"Easy to adjust headband ensures optimum fit and comfort\n" +
"2 metre-long cable";
String arr[] = longDescription.split("\n");
StringBuilder desc = new StringBuilder();
for (String s : arr){
desc.append("<li>"+s+"</li>");
}
String newDesc = "<ul>"+desc+"</ul>";
tvProdDesc.setText(Html.fromHtml(newDesc, null, new UlTagHandler()));
Here is my
UlTagHandler.java
public class UlTagHandler implements Html.TagHandler {
public void handleTag(boolean opening, String tag, Editable output,
XMLReader xmlReader) {
if(tag.equals("ul") && !opening) output.append("\n");
if(tag.equals("li") && opening) output.append("\n•\t");
}
}
But I want text should be properly formatted like word processor does.
I want this type of output
Can we do anything simillar to above image?

Would You be satisfied of this example?
public class MainActivity extends AppCompatActivity {
private TextView tvProdDesc;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvProdDesc = (TextView) findViewById(R.id.text1);
String longDescription = "Enhanced bass performance.\n" +
"Lightweight headband enhances comfort and adds durability\n" +
"Easy to adjust headband ensures optimum fit and comfort\n" +
"2 metre-long cable";
String arr[] = longDescription.split("\n");
int bulletGap = (int) dp(10);
SpannableStringBuilder ssb = new SpannableStringBuilder();
for (int i = 0; i < arr.length; i++) {
String line = arr[i];
SpannableString ss = new SpannableString(line);
ss.setSpan(new BulletSpan(bulletGap), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.append(ss);
//avoid last "\n"
if(i+1<arr.length)
ssb.append("\n");
}
tvProdDesc.setText(ssb);
}
private float dp(int dp) {
return getResources().getDisplayMetrics().density * dp;
}
}
Result:

Related

Making text Bold, Italic and Underscore with Dynamic string

I'm currently trying to figure out how to make text bold, Italic or underline with dynamic string coming from API, the text which has to be bold is coming as * bold *, Italic coming as _ italic_ and underline as #underline# (Same functionality as Stackoverflow).
After successful conversion of text, I want the special chars to be removed as well.
Text from API -
* I am Bold* and love to see _myself and _ others too.
Expected answer - I am Bold and love to see myself and others too.
I have tried some code which does not work if I try to create italic after bold also if I try to remove special chars.
TextView t = findViewById(R.id.viewOne);
String text = "*I am Bold* and _I am Italic_ here *Bold too*";
SpannableStringBuilder b = new SpannableStringBuilder(text);
Matcher matcher = Pattern.compile(Pattern.quote("*") + "(.*?)" + Pattern.quote("*")).matcher(text);
while (matcher.find()){
String name = matcher.group(1);
int index = text.indexOf(name)-1;
b.setSpan(new StyleSpan(Typeface.BOLD), index, index + name.length()+1, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE);
}
t.setText(b);
I don't want to use HTML tags
Edited answer to address the edited question
Try below, you should had to pass typeface instead StyleSpan.
public class SpanTest extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
TextView test = findViewById(R.id.test);
// String text = "*I am Bold* and _I am Italic_ here *Bold too*";
String text = "* I am Bold* and love to see _myself and _ others too";
CharSequence charSequence = updateSpan(text, "*", Typeface.BOLD);
charSequence = updateSpan(charSequence, "_", Typeface.ITALIC);
test.setText(charSequence);
}
private CharSequence updateSpan(CharSequence text, String delim, int typePace) {
Pattern pattern = Pattern.compile(Pattern.quote(delim) + "(.*?)" + Pattern.quote(delim));
SpannableStringBuilder builder = new SpannableStringBuilder(text);
if (pattern != null) {
Matcher matcher = pattern.matcher(text);
int matchesSoFar = 0;
while (matcher.find()) {
int start = matcher.start() - (matchesSoFar * 2);
int end = matcher.end() - (matchesSoFar * 2);
StyleSpan span = new StyleSpan(typePace);
builder.setSpan(span, start + 1, end - 1, 0);
builder.delete(start, start + 1);
builder.delete(end - 2, end - 1);
matchesSoFar++;
}
}
return builder;
}
}
Here is the output.

How to split up a SpannableStringBuilder while keeping its formating?

I am working on an android project that involves parsing some HTML (parsed by Jsoup) into a SpannableStringBuilder class.
However, I need this SpannableStringBuilder class to be divided up by each new line character into a List once it is done parsing, while keeping its formatting.
Such that a spanned text of
{"I am a spanned text,\n hear me roar"}
would turn into
{
"I am a spanned text,"
"hear me roar"
}
I am fairly new to developing on Android, and could not find anything in the documentation about spitting spans or even getting a listing of all formatting on a spanned class to build my own. So any help is much appreciated.
I managed to figure this out on my own, after looking into the method that pskink suggested.
My solution to this was
#Override
public List<Spanned> parse() {
List<Spanned> spans = new ArrayList<Spanned>();
Spannable unsegmented = (Spannable) Html.fromHtml(base.html(), null, new ReaderTagHandler());
//Set ColorSpan because it defaults to white text color
unsegmented.setSpan(new ForegroundColorSpan(Color.BLACK), 0, unsegmented.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//get locations of '/n'
Stack<Integer> loc = getNewLineLocations(unsegmented);
loc.push(unsegmented.length());
//divides up a span by each new line character position in loc
while (!loc.isEmpty()) {
Integer end = loc.pop();
Integer start = loc.isEmpty() ? 0 : loc.peek();
spans.add(0,(Spanned) unsegmented.subSequence(start, end));
}
return spans;
}
private Stack<Integer> getNewLineLocations(Spanned unsegmented) {
Stack<Integer> loc = new Stack<>();
String string = unsegmented.toString();
int next = string.indexOf('\n');
while (next > 0) {
//avoid chains of newline characters
if (string.charAt(next - 1) != '\n') {
loc.push(next);
next = string.indexOf('\n', loc.peek() + 1);
} else {
next = string.indexOf('\n', next + 1);
}
if (next >= string.length()) next = -1;
}
return loc;
}

Show source code in TextView correctly indented and parsed

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>";
}

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.

Android - How to parse Textview and add Spannables on certain characters?

I am developing a Calculator app and I want to display symbols like + - / * etc on a different color. Im using a TextView as my display.
I was able to do it when the buttons are being pressed with a code like this
coloredOperator = "<font color=#BED505>"+buttonPressed+"</font>";
textView.append(Html.fromHtml(coloredOperator));
However then I implemented a code on text change to order the operations when a new line is created on my textView, which looks something like this:
public void onTextChanged(CharSequence s, int start, int before, int count){
String message = s.toString();
// I have a java class that takes cares of this
int lastPositionOfBreakCharacter = getLastIndexOfRegex(message, "\\-|\\+|\\/|\\*|\\^");
int length = s.length();
int breakPosition = length-lastPositionOfBreakCharacter;
String text_view_text=t.getText().toString();
StringBuffer sb=new StringBuffer(text_view_text);
// So a new line is inserted before the last character +|-|* etc...
sb.insert(breakPosition,"\n");
textView.setText(sb);
}
The problem is that obviously this last functions strips my text view of all Spannables thus loosing style.
Is there any way to parse the text to find for the special characters, add the corresponding Spannables and then use .setText()?
Or do you have any other ideas on how to achieve what I'm after to?
Thanks!!!
How to use SpannableString with Regex in android?
The Correct answer works for this question.
////////////
public class SpanTest extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String dispStr = "This has the string ABCDEF in it \nSo does this :ABCDEF - see!\nAnd again ABCD here";
TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(dispStr);
changeTextinView(tv, "ABC", Color.RED);
}
private void changeTextinView(TextView tv, String target, int colour) {
String vString = (String) tv.getText();
int startSpan = 0, endSpan = 0;
Spannable spanRange = new SpannableString(vString);
while (true) {
startSpan = vString.indexOf(target, endSpan);
ForegroundColorSpan foreColour = new ForegroundColorSpan(colour);
// Need a NEW span object every loop, else it just moves the span
if (startSpan < 0)
break;
endSpan = startSpan + target.length();
spanRange.setSpan(foreColour, startSpan, endSpan,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
tv.setText(spanRange);
}
}

Categories

Resources