Incorect line wrapping when using bulletspan - android

I'm trying to solve my problem for 2 days now but without any success.
The problem is: when I set BulletSpan to text and then display it in EditText everything works fine until I start typing in another text. When the text is wrapped at the end of the screen, the indentation works but the cursor is pointing off the actual position and also some characters from previous line are added after the cursor. To better illustrate this problem see the attached image.
Also is worth mentioning that this happen only when I type in text, when I'm setting the text in the source and the text is too long to be only on one line the wrapping works fine and no extra characters are added nor the cursor position is wrong.
Also I tried LeadingMarginSpan.Standart and the behaviour was the same.
In code I'm setting the start mark:
private void handleListStart(SpannableStringBuilder text) {
int len = text.length();
text.setSpan(new ListItem(), len, len, Spannable.SPAN_MARK_MARK);
}
Then setting the span:
private void handleListEnd(SpannableStringBuilder text) {
int len = text.length();
Object obj = getLast(text, ListItem.class);
int where = text.getSpanStart(obj);
text.removeSpan(obj);
if (where != len) {
text.setSpan(new BulletSpan(listIndent * 15), where, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
I'm getting the data from xml file.
Thanks for any help
EDIT:
I forget to add that I have tried this on Android 4.1 and 4.4 and both behaved the same

This issue happens when your bulletspan-style characters come to a new line.
You can listen when the lines increase, then you can clear the bulletspan and set a new bulletspan again.
The solutiion above works perfectly for me.

#QuinnChen 's answer worked for me. Let me elaborate it with code for convenience .
This issue happens when the text is automatically wrapped to the next line in BulletSpan and LeadingMargin span .
Solution is to remove the previous span and apply the same span again when the line increases .
first set int line_counter = editorEditText.getLineCount(); when you click the button to apply the span
then in the body of textwatcher write this:
if(line_count > editorEditText.getLineCount()){
LeadingMarginSpan[] leadingMarginSpans = editorEditText.getText().getSpans(0, editorEditText.getSelectionStart(),
LeadingMarginSpan.class);
int s , e;
for(LeadingMarginSpan ss: leadingMarginSpans){
s = editorEditText.getText().getSpanStart(ss);
e = editorEditText.getText().getSpanEnd(ss);
if(s<=editorEditText.getSelectionStart() && editorEditText.getSelectionStart()<=e){
editorEditText.getText().removeSpan(ss);
editorEditText.getText().setSpan(new LeadingMarginSpan.Standard(30), s,e,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
line_count = editorEditText.getLineCount();
}
}
}
This code would execute when the text is automatically wrapped to the next line .
NOTE:
This is the code for LeadingMargin, the solution for bulletSpan goes the same, you just have to change replace LeadingMarginSpan with BulletSpan

Related

TextView with mixed languages

I have a TextView with 2 lines. first line rtl language (let's say hebrew), second line is ltr language (let's say english)
The View result is something like:
אחת שתיים שלוש
one two three
what i want: align rtl in that case
אחת שתיים שלוש
one two three
I've tried using setTextDirection() with TEXT_DIRECTION_FIRST_STRONG
but alas the results were the same. Also tried TEXT_ANY_RTL without success
myTextView.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
if i'm using TEXT_DIRECTION_RTL it's working as expected but this is not really a solution because most of the time the TextView will contain only one language.
Is this solvable?
--- UPDATE ---
How i'm populating the TextView
SpannableStringBuilder ssb = new SpannableStringBuilder(titleText);
int end = titlText.length();
ssb.append("\n").append(otheText);
ssb.setSpan(new AbsoluteSizeSpan(size), end, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(ssb);
Why not just use two TextViews?
I've managed to solve this problem using Character.getDirectionality.
The first char that is a directional char will signify the TextView direction
#TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static int getTextDirection(String text) {
final int length = text.length();
for (int i = 0; i < length; i++) {
final char c = text.charAt(i);
final byte directionality = Character.getDirectionality(c);
if(directionality == Character.DIRECTIONALITY_LEFT_TO_RIGHT){
return View.TEXT_DIRECTION_LTR;
}
else if(directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT){
return View.TEXT_DIRECTION_RTL;
}
}
return View.TEXT_DIRECTION_ANY_RTL;
}
and then:
textView.setTextDirection(textDirection);
I Strongly believe that TEXT_DIRECTION_FIRST_STRONG is supposed to do the exact same thing according to the docs. sadly it's not the case.
I'm not accepting my answer in hope that someone will suggest better solution
What About TEXT_DIRECTION_ANY_RTL
This text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains any strong RTL character, otherwise it is LTR if it contains any strong LTR characters. If there are neither, the paragraph direction is the view's resolved layout direction.

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.

HTML Formatted String inserting into TextViews and EditText

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.

How set selection in spinner of some string if spinner contains the same? [duplicate]

This question already has answers here:
How to set selected item of Spinner by value, not by position?
(25 answers)
Closed 8 years ago.
I posted similar question recently but nobody helped me. So I'll try to explain my problem better than before.
So I read some text from one file (this file may include more words) and then I read text from second file (this file always includes one word which is the same as one word in first file). Text in both files may be different everytime.
So for example:
First string contains: black blue yellow red green
Second string contains: yellow
Then I make spinner from first string, so spinner contains in this example these words (black blue yellow red green), so default option is black (coz it is first in my array), but I need to make third position as default in my spinner in this example, because second string contains yellow and yellow is on the third position in my spinner.
How can I make it without repopulating spinner?
btw. these strings are only example. Files may always includes different words than before.
Solution:
s1.setSelection(getIndex(s1, prefNameCurGOV));
and then:
private int getIndex(Spinner s1, String prefNameCurGOV){
int index = 0;
for (int i=0;i<s1.getCount();i++){
if (s1.getItemAtPosition(i).equals(prefNameCurGOV)){
index = i;
}
}
return index;
Something like:
String secondString = secondSpinner.getSelectedItem();
firstSpinner.setSelection(getIndex(firstSpinner,secondString));
then use
private int getIndex(Spinner spinner,String string){
//Pseudo code because I dont remember the API
int index = 0;
for (int i = 0; i < firstSpinner.size(); i++){
if (firstSpinner.getItemAtPosition(i).equals(string)){
index = i;
}
}
return index;
}

Android: Insert text into EditText at current position

I want to insert a constant string into an EditText by the press of a button. The string should be inserted at the current position in the EditText.
If I use EditText.append the text gets inserted at the end of the EditText.
How can I do that? I couldn't find a suitable method.
Cpt.Ohlund gave me the right hint. I solved it, now, partly with using EditText.getSelectionStart(), but I realized that you can also replace the selected text with the same expression and you don't need String.subString() for that.
int start = Math.max(myEditText.getSelectionStart(), 0);
int end = Math.max(myEditText.getSelectionEnd(), 0);
myEditText.getText().replace(Math.min(start, end), Math.max(start, end),
textToInsert, 0, textToInsert.length());
This works for both, inserting a text at the current position and replacing whatever text is selected by the user. The Math.max() is necessary in the first and second line because, if there is no selection or cursor in the EditText, getSelectionStart() and getSelectionEnd() will both return -1. The Math.min() and Math.max() in the third line is necessary because the user could have selected the text backwards and thus start would have a higher value than end which is not allowed for Editable.replace().
This seems simpler:
yourEditText.getText().insert(yourEditText.getSelectionStart(), "fizzbuzz");
However, Manuel's answer might be better if you want to replace any selected text with the inserted text.
Try using EditText.getSelectionStart() to get the current position of the cursor. Then you can use String.subString to get the text before and after the cursor and insert your text in the middle.
I think this function will help for you :
public void insertConstantStr(String insertStr) {
String oriContent = editText.getText().toString();
int index = editText.getSelectionStart() >= 0 ? editText.getSelectionStart() : 0;
StringBuilder sBuilder = new StringBuilder(oriContent);
sBuilder.insert(index, insertStr);
editText.setText(sBuilder.toString());
editText.setSelection(index + insertStr.length());
}
For Kotlin simply do that:
editText.text.insert(editText.selectionStart, "Your Text Here")
Editable editable = new SpannableStringBuilder("Pass a string here");
yourEditText.text = editable;

Categories

Resources