android how an EditText work as AutoComplete - android

I want my EditText should work as AutoComplete, for that I write in XML file
android:inputType="textAutoComplete|textAutoCorrect"
but it's not working.
I am working with API v2.2 and my Activity extends MapActivity, there I put a simple EditText and a button named "Search". so if we type the location name in EditText and press search button means it should go to that location in map.
So I want that EditText to work as a AutoComplete.
How can I do that?

Just use an AutoCompleteTextView instead of normal EditText.
hello-autocomplete will be helpful.
EDIT: The above link looks like has expired. The new page is here: https://developer.android.com/training/keyboard-input/style#AutoComplete

First convert your EditText->AutoCompleteTextView
Then link your XML file to the AutoCompleteTextView using a ArrayAdapter
Assume that the XML string-array you created is named as list_of_countries then it can be linked to your AutoCompleteTextView as follows:
String[] countries = getResources().getStringArray(R.array.list_of_countries);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,countries);
actv.setAdapter(adapter);

I use this code:
1) On AndroidManifest.xml
<uses-permission android:name="android.permission.GET_ACCOUNTS"></uses-permission>
2) On xml layout you must use AutoCompleteTextView instead of EditText.
<AutoCompleteTextView
android:id="#+id/autoCompleteTextView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:text="AutoCompleteTextView" />
3) Use this on Activity file
private ArrayAdapter<String> getEmailAddressAdapter(Context context) {
Account[] accounts = AccountManager.get(context).getAccounts();
String[] addresses = new String[accounts.length];
for (int i = 0; i < accounts.length; i++) {
addresses[i] = accounts[i].name;
}
return new ArrayAdapter<String>(context, android.R.layout.simple_dropdown_item_1line, addresses);
}
4) On onCreate activity:
AutoCompleteTextView autoCompleteTextView1 = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
autoCompleteTextView1.setAdapter(getEmailAddressAdapter(this));

Default ArrayAdapter filters only by the first characters. In case you want to see also words which contain the searching keyword, you need to use a custom ArrayAdapter and override its getView and getFilter methods. Take a look at a complete solution I provided in another StackOverflow question: https://stackoverflow.com/a/37298258/1808829
Some code fragment:
public class AutoSuggestAdapter extends ArrayAdapter
{
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// handle view here
}
#Override
public Filter getFilter()
{
// implement filtering here
}
}

This code for change settings of MultiAutoCompleteTextView
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,codeKeyWords);
MultiAutoCompleteTextView autoCompleteTextView1 = (MultiAutoCompleteTextView) findViewById(R.id.autoCompleteTextView1);
autoCompleteTextView1.setAdapter(adapter);
autoCompleteTextView1.setThreshold(1);
autoCompleteTextView1.setTokenizer(new this.CommaTokenizer());
And below that code for make spliting words by space char and \n charactes.. (Why we need this code? Because Normal multiAutoComplete.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer()); like that and it make spliting the words by ',' character, But our code help you to make that spliting by these characters ' ' and '\n' )
/**
* This simple Tokenizer can be used for lists where the items are
* separated by a comma and one or more spaces.
*/
public static class CommaTokenizer implements Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == '\n') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == '\n') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + "\n");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(),
Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}

Related

After spliting String into ArryList<String> i cannot read the text from any cell

I am an android noobie. What I am trying to do is to make this String an ArrayList. This is done. When i Print it On (with tv.setText) , the result is what i need but in this if i have right below i cannot find the "1".
The result i want to have is to store the text between the noumbers inside another ArrayList but to go there i have to be able to read the strings from the ArrayList.
public class MainActivity extends AppCompatActivity {
String text = "1Hello12People22Paul22Jackie21Anna12Fofo2";
TextView tv;
List<String> chars = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView)findViewById(R.id.tv);
PrinThemNow();
}
public void PrinThemNow(){
chars = Arrays.asList(text.split(""));
tv.setText(toString().valueOf(chars));
for(int i=0;i<chars.size();i++){
if(toString().valueOf(chars.get(i)) == " 1"){
Toast.makeText(this,"I found One",Toast.LENGTH_LONG).show();
//This if is not working while the TV's text shows " 1"
}
}
}
}
First, just a tip, from string to char[] you can use
char[] chars = myString.toCharArray();
because it has no sense to save a char array as a string ArrayList
but now the problem. you have your string and you wanna print the text between the numbers.
It's not really clear what is your goal but lets try.
I will suppose you used the char[] because it's 10 times better and easier
case 1) you wanna print text betweens "1"s
//lets loop the chars
bool firstOneFound = false;
int firstOccurrence = -1;
int secondOccurrence = -1;
int i = 0;
for(char c : chars){
//is it equals to 1?
if(c.equals('1')){
//check if we are already after the first 1
if(firstOneFound){
//if yes, we found the final one
secondOccurrence = i;
break;
}
else{
//this is the first occurrence
firstOccurrence = i;
firstOneFound = true;
}
}
i++;
}
if(firstOccurrence != -1 && secondOccurrence != -1){
String myFinalString = myString.subString(firstOccurrence, secondOccurrence);
}
case 2) you wanna print all text except numbers (maybe with a space instead)
for(char c : chars){
//check if it's a number
if(c >= '0' && c <= '9'){
//replace the number with anything else
c = ' '; //if you wanna have it as a space
}
}
//print the final string
String myFinalString = new String(chars);
NOTE:
You can also use ArrayList of string, just replace ' with "
hope it helps

Android: Autocomplete TextView Similar To The Facebook App

I have an AutoCompleteTextView in my app. The app makes use of the Facebook SDK. I followed the code from this question on SO: https://stackoverflow.com/a/12363961/450534 to the dot and have a functioning search (filtering) activity.
Now, how do I get an inline Autocomplete like the Facebook App after the # symbol is typed and still hold the other text typed by the user? The activity in question is for a Status Update and can contain the user's Friends along with other text. The AutoCompleteTextView or the EditText will naturally be a multi-line one. For a status update you see.
I know for the lack of any code in this post, I risk getting downvoted or perhaps even having the question closed. But it's really a standard boiler plate list filtering code.
EDIT: The FriendCaster app for Android also does the same.
First make your EditText into a MultiAutoCompleteTextView. A MultiAutoCompleteTextView allows you to replace certain parts of the text, for example text after '#'.
The you can do something like this:
final MultiAutoCompleteTextView inputEditText = (MultiAutoCompleteTextView) dialog.findViewById(R.id.MyEditText);
String[] COUNTRIES = new String[] { "Belgium", "France", "Italy", "Germany", "Spain" };
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, COUNTRIES);
inputEditText.setAdapter(adapter);
inputEditText.setThreshold(1); //Set number of characters before the dropdown should be shown
//Create a new Tokenizer which will get text after '#' and terminate on ' '
inputEditText.setTokenizer(new Tokenizer() {
#Override
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
if (text instanceof Spanned) {
SpannableString sp = new SpannableString(text + " ");
TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0);
return sp;
} else {
return text + " ";
}
}
}
#Override
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != '#') {
i--;
}
//Check if token really started with #, else we don't have a valid token
if (i < 1 || text.charAt(i - 1) != '#') {
return cursor;
}
return i;
}
#Override
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
});
One "problem" with this is that the popup will appear under the EditText view. To move it up and place it under the text that is currently written you can do something like this:
inputEditText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Layout layout = inputEditText.getLayout();
int pos = inputEditText.getSelectionStart();
int line = layout.getLineForOffset(pos);
int baseline = layout.getLineBaseline(line);
int bottom = inputEditText.getHeight();
inputEditText.setDropDownVerticalOffset(baseline - bottom);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
Note: This does not currently take care of the dropdown position in the case that there are more lines in the edittext than the edittext can show.
https://github.com/dhaval001/linkable_text_view.git
Simple way to create link text, such as #username or #hashtag, in Android TextView and EditText with multiautocomplete textview. link facebook and instagram
Features
Match single strings or regex pattern to set links
Change the color of the linked text
Set the style of the linked text: BOLD, ITALIC, or BOLD_ITALIC
Set the underlined of the linked text
Specify click actions of a specific word
OnTextChangedListener listener for LinkableEditText

Android App- Using a loop for setText procedure to populate EditText

Trying to use a loop to set the text of 12 checkboxes from a db query. Would like to substitute "add1" with an array value and loop through all 12 instead of spelling out each one. Any ideas of how to do this?
Here is the code I am trying to modify:
add1Text= (CheckBox) findViewById(R.id.add1);
if (cursor.getString(cursor.getColumnIndex("add1")) == null) {
add1Text.setVisibility(View.GONE);
}
else {
add1Text.setText(cursor.getString(cursor.getColumnIndex("add1")));
}
Please note: everything below is off the top of my head, I can't test it right now. I'll test it later when I get a chance.
I think you'll need to keep track of which column to associate with each CheckBox... I'm presuming it's something like this:
Column: add1 => Checkbox: add1Text
Column: add2 => Checkbox: add2Text
and so on and so forth.
In this circumstance, you'll need to manually keep track of them, possibly in an array. I'd suggest making a Pair class that you can use. I've altered the class from this post [ A Java collection of value pairs? (tuples?) ]
public class Pair<L,R> {
private final L left;
private final R right;
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public R getRight() { return right; }
#Override
public int hashCode() { return left.hashCode() ^ right.hashCode(); }
#Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof Pair)) return false;
Pair pairo = (Pair) o;
return this.left.equals(pairo.getLeft()) &&
this.right.equals(pairo.getRight());
}
}
Now, you'll need to make a List (or similar) containing the pairs that you want.
List<Pair<CheckBox, String>> list = new ArrayList<Pair<CheckBox, String>>;
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add1), "add1");
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add2), "add2");
list.add(new Pair<CheckBox, String>((CheckBox) findViewById(R.id.add3), "add3");
and so on and so forth
Then you can iterate through the List using something like
foreach (Pair<CheckBox, String> item in list)
{
if (cursor.getString(cursor.getColumnIndex(item.getLeft()) == null)
{
item.getRight().setVisibility(View.GONE);
}
else
{
item.getRight().setText(cursor.getString(cursor.getColumnIndex(item.getLeft()));
}
}
Got it! Forgot that I was dealing with objects and also realized I needed a third array. Here is what I came up with.
cList contains column names
fList are the objects (in this case CheckBoxes)
pList are the names of the objects I am selecting from the layout.
Object fList[]={add1Text,add2Text,add3Text};
int pList[]={R.id.add1,R.id.add2,R.id.add3};
cList = cursor.getColumnNames();
for (int i =0; i < fList.length; i++){
fList[i] = (CheckBox) findViewById(pList[i]);
if (cursor.getString(cursor.getColumnIndex(cList[i])) == null) {
((TextView) fList[i]).setVisibility(View.GONE);
}
else {
((TextView) fList[i]).setText(cList[i] + " - " + cursor.getString(cursor.getColumnIndex( cList[i])));
}
}
Sets the CheckBox text to ( Column name - Value )

What's the best way to limit text length of EditText in Android

What's the best way to limit the text length of an EditText in Android?
Is there a way to do this via xml?
Documentation
Example
android:maxLength="10"
use an input filter to limit the max length of a text view.
TextView editEntryView = new TextView(...);
InputFilter[] filterArray = new InputFilter[1];
filterArray[0] = new InputFilter.LengthFilter(8);
editEntryView.setFilters(filterArray);
EditText editText = new EditText(this);
int maxLength = 3;
editText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(maxLength)});
A note to people who are already using a custom input filter and also want to limit the max length:
When you assign input filters in code all previously set input filters are cleared, including one set with android:maxLength. I found this out when attempting to use a custom input filter to prevent the use of some characters that we don't allow in a password field. After setting that filter with setFilters the maxLength was no longer observed. The solution was to set maxLength and my custom filter together programmatically. Something like this:
myEditText.setFilters(new InputFilter[] {
new PasswordCharFilter(), new InputFilter.LengthFilter(20)
});
I have had this problem and I consider we are missing a well explained way of doing this programmatically without losing the already set filters.
Setting the length in XML:
As the accepted answer states correctly, if you want to define a fixed length to an EditText which you won't change further in the future just define in your EditText XML:
android:maxLength="10"
Setting the length programmatically
To set the length programmatically you'll need to set it through an InputFilter. But if you create a new InputFilter and set it to the EditText you will lose all the other already defined filters (e.g. maxLines, inputType, etc) which you might have added either through XML or programatically.
So this is WRONG:
editText.setFilters(new InputFilter[] {new InputFilter.LengthFilter(maxLength)});
To avoid losing previously added filters you need to get those filters, add the new one (maxLength in this case), and set the filters back to the EditText as follow:
Java
InputFilter[] editFilters = editText.getFilters();
InputFilter[] newFilters = new InputFilter[editFilters.length + 1];
System.arraycopy(editFilters, 0, newFilters, 0, editFilters.length);
newFilters[editFilters.length] = new InputFilter.LengthFilter(maxLength);
editText.setFilters(newFilters);
Kotlin however made it easier for everyone, you also need to add the filter to the already existing ones but you can achieve that with a simple:
editText.filters += InputFilter.LengthFilter(maxLength)
TextView tv = new TextView(this);
tv.setFilters(new InputFilter[]{ new InputFilter.LengthFilter(250) });
For anyone else wondering how to achieve this, here is my extended EditText class EditTextNumeric.
.setMaxLength(int) - sets maximum number of digits
.setMaxValue(int) - limit maximum integer value
.setMin(int) - limit minimum integer value
.getValue() - get integer value
import android.content.Context;
import android.text.InputFilter;
import android.text.InputType;
import android.widget.EditText;
public class EditTextNumeric extends EditText {
protected int max_value = Integer.MAX_VALUE;
protected int min_value = Integer.MIN_VALUE;
// constructor
public EditTextNumeric(Context context) {
super(context);
this.setInputType(InputType.TYPE_CLASS_NUMBER);
}
// checks whether the limits are set and corrects them if not within limits
#Override
protected void onTextChanged(CharSequence text, int start, int before, int after) {
if (max_value != Integer.MAX_VALUE) {
try {
if (Integer.parseInt(this.getText().toString()) > max_value) {
// change value and keep cursor position
int selection = this.getSelectionStart();
this.setText(String.valueOf(max_value));
if (selection >= this.getText().toString().length()) {
selection = this.getText().toString().length();
}
this.setSelection(selection);
}
} catch (NumberFormatException exception) {
super.onTextChanged(text, start, before, after);
}
}
if (min_value != Integer.MIN_VALUE) {
try {
if (Integer.parseInt(this.getText().toString()) < min_value) {
// change value and keep cursor position
int selection = this.getSelectionStart();
this.setText(String.valueOf(min_value));
if (selection >= this.getText().toString().length()) {
selection = this.getText().toString().length();
}
this.setSelection(selection);
}
} catch (NumberFormatException exception) {
super.onTextChanged(text, start, before, after);
}
}
super.onTextChanged(text, start, before, after);
}
// set the max number of digits the user can enter
public void setMaxLength(int length) {
InputFilter[] FilterArray = new InputFilter[1];
FilterArray[0] = new InputFilter.LengthFilter(length);
this.setFilters(FilterArray);
}
// set the maximum integer value the user can enter.
// if exeeded, input value will become equal to the set limit
public void setMaxValue(int value) {
max_value = value;
}
// set the minimum integer value the user can enter.
// if entered value is inferior, input value will become equal to the set limit
public void setMinValue(int value) {
min_value = value;
}
// returns integer value or 0 if errorous value
public int getValue() {
try {
return Integer.parseInt(this.getText().toString());
} catch (NumberFormatException exception) {
return 0;
}
}
}
Example usage:
final EditTextNumeric input = new EditTextNumeric(this);
input.setMaxLength(5);
input.setMaxValue(total_pages);
input.setMinValue(1);
All other methods and attributes that apply to EditText, of course work too.
Xml
android:maxLength="10"
Java:
InputFilter[] editFilters = editText.getFilters();
InputFilter[] newFilters = new InputFilter[editFilters.length + 1];
System.arraycopy(editFilters, 0, newFilters, 0, editFilters.length);
newFilters[editFilters.length] = new InputFilter.LengthFilter(maxLength);
editText.setFilters(newFilters);
Kotlin:
editText.filters += InputFilter.LengthFilter(maxLength)
Due to goto10's observation, I put together the following code to protected against loosing other filters with setting the max length:
/**
* This sets the maximum length in characters of an EditText view. Since the
* max length must be done with a filter, this method gets the current
* filters. If there is already a length filter in the view, it will replace
* it, otherwise, it will add the max length filter preserving the other
*
* #param view
* #param length
*/
public static void setMaxLength(EditText view, int length) {
InputFilter curFilters[];
InputFilter.LengthFilter lengthFilter;
int idx;
lengthFilter = new InputFilter.LengthFilter(length);
curFilters = view.getFilters();
if (curFilters != null) {
for (idx = 0; idx < curFilters.length; idx++) {
if (curFilters[idx] instanceof InputFilter.LengthFilter) {
curFilters[idx] = lengthFilter;
return;
}
}
// since the length filter was not part of the list, but
// there are filters, then add the length filter
InputFilter newFilters[] = new InputFilter[curFilters.length + 1];
System.arraycopy(curFilters, 0, newFilters, 0, curFilters.length);
newFilters[curFilters.length] = lengthFilter;
view.setFilters(newFilters);
} else {
view.setFilters(new InputFilter[] { lengthFilter });
}
}
//Set Length filter. Restricting to 10 characters only
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(MAX_LENGTH)});
//Allowing only upper case characters
editText.setFilters(new InputFilter[]{new InputFilter.AllCaps()});
//Attaching multiple filters
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(MAX_LENGTH), new InputFilter.AllCaps()});
Another way you can achieve this is by adding the following definition to the XML file:
<EditText
android:id="#+id/input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="number"
android:maxLength="6"
android:hint="#string/hint_gov"
android:layout_weight="1"/>
This will limit the maximum length of the EditText widget to 6 characters.
XML
android:maxLength="10"
Programmatically:
int maxLength = 10;
InputFilter[] filters = new InputFilter[1];
filters[0] = new InputFilter.LengthFilter(maxLength);
yourEditText.setFilters(filters);
Note: internally, EditText & TextView parse the value of android:maxLength in XML and use InputFilter.LengthFilter() to apply it.
See: TextView.java#L1564
From material.io, you can use TextInputEditText combined with TextInputLayout:
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterEnabled="true"
app:counterMaxLength="1000"
app:passwordToggleEnabled="false">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/edit_text"
android:hint="#string/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLength="1000"
android:gravity="top|start"
android:inputType="textMultiLine|textNoSuggestions"/>
</com.google.android.material.textfield.TextInputLayout>
You can configure a password EditText with drawable:
Or you can limit text length with/without a counter:
Dependency:
implementation 'com.google.android.material:material:1.1.0-alpha02'
Kotlin:
edit_text.filters += InputFilter.LengthFilter(10)
ZTE Blade A520 has strange effect. When you type more than 10 symbols (for instance, 15), EditText shows first 10, but other 5 are not visible and not accessible. But when you delete symbols with Backspace, it first deletes right 5 symbols and then removes remaining 10. To overcome this behaviour use a solution:
android:inputType="textNoSuggestions|textVisiblePassword"
android:maxLength="10"
or this:
android:inputType="textNoSuggestions"
or this, if you want to have suggestions:
private class EditTextWatcher(private val view: EditText) : TextWatcher {
private var position = 0
private var oldText = ""
override fun afterTextChanged(s: Editable?) = Unit
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
oldText = s?.toString() ?: ""
position = view.selectionStart
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
val newText = s?.toString() ?: ""
if (newText.length > 10) {
with(view) {
setText(oldText)
position = if (start > 0 && count > 2) {
// Text paste in nonempty field.
start
} else {
if (position in 1..10 + 1) {
// Symbol paste in the beginning or middle of the field.
position - 1
} else {
if (start > 0) {
// Adding symbol to the end of the field.
start - 1
} else {
// Text paste in the empty field.
0
}
}
}
setSelection(position)
}
}
}
}
// Usage:
editTextWatcher = EditTextWatcher(view.edit_text)
view.edit_text.addTextChangedListener(editTextWatcher)
This is a custom EditText Class that allow Length filter to live along with other filters.
Thanks to Tim Gallagher's Answer (below)
import android.content.Context;
import android.text.InputFilter;
import android.util.AttributeSet;
import android.widget.EditText;
public class EditTextMultiFiltering extends EditText{
public EditTextMultiFiltering(Context context) {
super(context);
}
public EditTextMultiFiltering(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextMultiFiltering(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setMaxLength(int length) {
InputFilter curFilters[];
InputFilter.LengthFilter lengthFilter;
int idx;
lengthFilter = new InputFilter.LengthFilter(length);
curFilters = this.getFilters();
if (curFilters != null) {
for (idx = 0; idx < curFilters.length; idx++) {
if (curFilters[idx] instanceof InputFilter.LengthFilter) {
curFilters[idx] = lengthFilter;
return;
}
}
// since the length filter was not part of the list, but
// there are filters, then add the length filter
InputFilter newFilters[] = new InputFilter[curFilters.length + 1];
System.arraycopy(curFilters, 0, newFilters, 0, curFilters.length);
newFilters[curFilters.length] = lengthFilter;
this.setFilters(newFilters);
} else {
this.setFilters(new InputFilter[] { lengthFilter });
}
}
}
it simple way in xml:
android:maxLength="4"
if u require to set 4 character in xml edit-text so,use this
<EditText
android:id="#+id/edtUserCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLength="4"
android:hint="Enter user code" />
Try this for Java programmatically:
myEditText(new InputFilter[] {new InputFilter.LengthFilter(CUSTOM_MAX_LEN)});
it simple way in xml:
android:maxLength="#{length}"
for setting it programmatically you can use the following function
public static void setMaxLengthOfEditText(EditText editText, int length) {
InputFilter[] filters = editText.getFilters();
List arrayList = new ArrayList();
int i2 = 0;
if (filters != null && filters.length > 0) {
int filtersSize = filters.length;
int i3 = 0;
while (i2 < filtersSize) {
Object obj = filters[i2];
if (obj instanceof LengthFilter) {
arrayList.add(new LengthFilter(length));
i3 = 1;
} else {
arrayList.add(obj);
}
i2++;
}
i2 = i3;
}
if (i2 == 0) {
arrayList.add(new LengthFilter(length));
}
if (!arrayList.isEmpty()) {
editText.setFilters((InputFilter[]) arrayList.toArray(new InputFilter[arrayList.size()]));
}
}
This works fine...
android:maxLength="10"
this will accept only 10 characters.
I had saw a lot of good solutions, but I'd like to give a what I think as more complete and user-friendly solution, which include:
1, Limit length.
2, If input more, give a callback to trigger your toast.
3, Cursor can be at middle or tail.
4, User can input by paste a string.
5, Always discard overflow input and keep origin.
public class LimitTextWatcher implements TextWatcher {
public interface IF_callback{
void callback(int left);
}
public IF_callback if_callback;
EditText editText;
int maxLength;
int cursorPositionLast;
String textLast;
boolean bypass;
public LimitTextWatcher(EditText editText, int maxLength, IF_callback if_callback) {
this.editText = editText;
this.maxLength = maxLength;
this.if_callback = if_callback;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (bypass) {
bypass = false;
} else {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(s);
textLast = stringBuilder.toString();
this.cursorPositionLast = editText.getSelectionStart();
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if (s.toString().length() > maxLength) {
int left = maxLength - s.toString().length();
bypass = true;
s.clear();
bypass = true;
s.append(textLast);
editText.setSelection(this.cursorPositionLast);
if (if_callback != null) {
if_callback.callback(left);
}
}
}
}
edit_text.addTextChangedListener(new LimitTextWatcher(edit_text, MAX_LENGTH, new LimitTextWatcher.IF_callback() {
#Override
public void callback(int left) {
if(left <= 0) {
Toast.makeText(MainActivity.this, "input is full", Toast.LENGTH_SHORT).show();
}
}
}));
What I failed to do is, if user highlight a part of the current input and try to paste an very long string, I don't know how to restore the highlight.
Such as, max length is set to 10, user inputed '12345678', and mark '345' as highlight, and try to paste a string of '0000' which will exceed limitation.
When I try to use edit_text.setSelection(start=2, end=4) to restore origin status, the result is, it just insert 2 space as '12 345 678', not the origin highlight. I'd like someone solve that.
You can use android:maxLength="10" in the EditText.(Here the limit is upto 10 characters)
I was using "maxLength" but it didn't work for me. So, based on my experience, I tried a few things.
And I detected the problem.
I must declare the id.
Then:
android:id="#+id/editTextTest"
android:maxLength="10"

Html List tag not working in android textview. What can I do?

Html List tag not working in android TextView. This is my string content:
String str="A dressy take on classic gingham in a soft, textured weave of stripes that resembles twill. Take a closer look at this one.<ul><li>Trim, tailored fit for a bespoke feel</li><li>Medium spread collar, one-button mitered barrel cuffs</li><li>Applied placket with genuine mother-of-pearl buttons</li><li>;Split back yoke, rear side pleats</li><li>Made in the U.S.A. of 100% imported cotton.</li></ul>";
I loaded it in a text view like this:
textview.setText(Html.fromHtml(str));
The output looks like a paragraph. What can I do? Is there any solution for it?
Edit:
webview.loadData(str,"text/html","utf-8");
As you can see in the Html class source code, Html.fromHtml(String) does not support all HTML tags. In this very case, <ul> and <li> are not supported.
From the source code I have built a list of allowed HTML tags:
br
p
div
em
b
strong
cite
dfn
i
big
small
font
blockquote
tt
monospace
a
u
sup
sub
So you better use WebView and its loadDataWithBaseURL method. Try something like this:
String str="<html><body>A dressy take on classic gingham in a soft, textured weave of stripes that resembles twill. Take a closer look at this one.<ul><li>Trim, tailored fit for a bespoke feel</li><li>Medium spread collar, one-button mitered barrel cuffs</li><li>Applied placket with genuine mother-of-pearl buttons</li><li>;Split back yoke, rear side pleats</li><li>Made in the U.S.A. of 100% imported cotton.</li></ul></body></html>";
webView.loadDataWithBaseURL(null, str, "text/html", "utf-8", null);
I was having the same problem, and what I did is overriding the default TagHandler. This one worked for me.
public class MyTagHandler implements TagHandler {
boolean first = true;
String parent = null;
int index = 1;
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equals("ul")) {
parent = "ul";
} else if (tag.equals("ol")) {
parent = "ol";
}
if (tag.equals("li")) {
if (parent.equals("ul")) {
if (first) {
output.append("\n\t•");
first = false;
} else {
first = true;
}
} else{
if (first) {
output.append("\n\t"+index+". ");
first = false;
index++;
} else {
first = true;
}
}
}
}
}
and for displaying the text...
myTextView.setText(Html.fromHtml("<ul><li>I am an Android developer</li><li>Another Item</li></ul>", null, new MyTagHandler()));
[Edit]
Kuitsi has also posted an really good library that does the same, got it from this SO link.
Full sample project is located at https://bitbucket.org/Kuitsi/android-textview-html-list.
Sample picture is available at https://kuitsi.bitbucket.io/stackoverflow3150400_screen.png
This solution is closest to masha's answer. Some code is also taken from inner class android.text.Html.HtmlToSpannedConverter. It supports nested ordered and unordered lists but too long texts in ordered lists are still aligned with item number rather than text. Mixed lists (ol and ul) needs some work too. Sample project contains implementation of Html.TagHandler which is passed to Html.fromHtml(String, ImageGetter, TagHandler).
Edit: For wider HTML tag support, https://github.com/NightWhistler/HtmlSpanner might also be worth trying.
A small fix to Aman Guatam code. The function above has problem of rendering newline character. For example: if before <li> tag is a <p> tag, 2 newline characters are rendered. Here is upgraded code:
import org.xml.sax.XMLReader;
import android.text.Editable;
import android.text.Html.TagHandler;
public class ListTagHandler implements TagHandler {
boolean first = true;
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
// TODO Auto-generated method stub
if (tag.equals("li")) {
char lastChar = 0;
if (output.length() > 0)
lastChar = output.charAt(output.length() - 1);
if (first) {
if (lastChar == '\n')
output.append("\t• ");
else
output.append("\n\t• ");
first = false;
} else {
first = true;
}
}
}
}
WARNING
As of Android 7 android.text.Html actually supports li and ul tags and uses a basic BulletSpan(), which means in the latest versions of Android the Html.TagHandlersolutions posted here will be ignored
Make sure your code handles this change. In case you want a BulletSpan with a larger gap than the default, you can can replace it with another span:
val html = SpannableStringBuilder(HtmlCompat.fromHtml(source, HtmlCompat.FROM_HTML_MODE_COMPACT))
val bulletSpans = html.getSpans<BulletSpan>(0, html.length)
bulletSpans.forEach {
val spanStart = html.getSpanStart(it)
val spanEnd = html.getSpanEnd(it)
html.removeSpan(it)
val bulletSpan = BulletSpan(gapWidthInDp, context.getColor(R.color.textColorBlack))
html.setSpan(bulletSpan, spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
Different solution using LeadingMarginSpan. Handles ordered and unordered lists as well as nesting.
public class ListTagHandler implements TagHandler
{
private int m_index = 0;
private List< String > m_parents = new ArrayList< String >( );
#Override
public void handleTag( final boolean opening, final String tag, Editable output, final XMLReader xmlReader )
{
if( tag.equals( "ul" ) || tag.equals( "ol" ) || tag.equals( "dd" ) )
{
if( opening )
{
m_parents.add( tag );
}
else m_parents.remove( tag );
m_index = 0;
}
else if( tag.equals( "li" ) && !opening ) handleListTag( output );
}
private void handleListTag( Editable output )
{
if( m_parents.get(m_parents.size()-1 ).equals( "ul" ) )
{
output.append( "\n" );
String[ ] split = output.toString( ).split( "\n" );
int lastIndex = split.length - 1;
int start = output.length( ) - split[ lastIndex ].length( ) - 1;
output.setSpan( new BulletSpan( 15 * m_parents.size( ) ), start, output.length( ), 0 );
}
else if( m_parents.get(m_parents.size()-1).equals( "ol" ) )
{
m_index++ ;
output.append( "\n" );
String[ ] split = output.toString( ).split( "\n" );
int lastIndex = split.length - 1;
int start = output.length( ) - split[ lastIndex ].length( ) - 1;
output.insert( start, m_index + ". " );
output.setSpan( new LeadingMarginSpan.Standard( 15 * m_parents.size( ) ), start, output.length( ), 0 );
}
}
}
If you only need to format a list, keep it simple and copy/paste a unicode character in your TextView to achieve the same result.
• Unicode Character 'BULLET' (U+2022)
I came here looking for TagHandler implementations. Both Truong Nguyen and Aman Guatam answers are very nice, but I needed a mixed version of both: I needed my solution not to overformat it and to be able to ressolve <ol> tags, since I'm parsing something like <h3>title</h3><ol><li>item</li><li>item</li><li>item</li></ol>.
Here's my solution.
import org.xml.sax.XMLReader;
import android.text.Editable;
import android.text.Html.TagHandler;
public class MyTagHandler implements TagHandler {
boolean first = true;
String parent = null;
int index = 1;
public void handleTag(final boolean opening, final String tag,
final Editable output, final XMLReader xmlReader) {
if (tag.equals("ul")) {
parent = "ul";
index = 1;
} else if (tag.equals("ol")) {
parent = "ol";
index = 1;
}
if (tag.equals("li")) {
char lastChar = 0;
if (output.length() > 0) {
lastChar = output.charAt(output.length() - 1);
}
if (parent.equals("ul")) {
if (first) {
if (lastChar == '\n') {
output.append("\t• ");
} else {
output.append("\n\t• ");
}
first = false;
} else {
first = true;
}
} else {
if (first) {
if (lastChar == '\n') {
output.append("\t" + index + ". ");
} else {
output.append("\n\t" + index + ". ");
}
first = false;
index++;
} else {
first = true;
}
}
}
}
}
Note that, since we are resetting the index value whenever a new list starts, it WILL NOT work if you nest lists like in <ol><li>1<ol><li>1.1</li><li>1.2</li></ol><li>2</li></ol>11.11.22
With that code, you would get 1, 1, 2, 3 instead of 1, 1, 2, 2.
You can simply replace the "li" with unicodes
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equalsIgnoreCase("li")) {
if (opening) {
output.append("\u2022 ");
} else {
output.append("\n");
}
}
}
Sure, there ise a way of showing bullets in Android TextView. You can replace <li> tags with • (which is HTML code for bullet).
If you want to try other list icons, use the preferred one from the table is this link;
http://www.ascii-code.com/
You can use Html.TagHandler. Below can be used for kotlin
class UlTagHandler : Html.TagHandler {
override fun handleTag(
opening: Boolean, tag: String, output: Editable,
xmlReader: XMLReader
) {
if (tag == "ul" && !opening) output.append("\n")
if (tag == "li" && opening) output.append("\n\t•")
}
}
and
textView.setText(Html.fromHtml(myHtmlText, null, UlTagHandler()));
Lord Voldermort's answer is a good starting point. However I required ol tag to display ordered list 1. 2. 3. .... instead of bullets. Also, nested tags need special handling to work properly.
In my code, I have maintained stack(parentList) to keep track of opened and closed ul and ol tags and also to know the current open tag.
Also, a levelWiseCounter is used to maintain different counts in case of nested ol tags.
myTextView.setText(Html.fromHtml("your string", null, new CustomTagHandler()));
.
.
.
private static class CustomTagHandler implements TagHandler
{
int level = 0;
private LinkedList<Tag> parentList = new LinkedList<DetailFragment.CustomTagHandler.Tag>();
private HashMap<Integer, Integer> levelWiseCounter = new HashMap<Integer, Integer>();
#Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader)
{
if (tag.equalsIgnoreCase("ul") || tag.equalsIgnoreCase("ol"))
{
if (opening)
{
if (tag.equalsIgnoreCase("ul"))
{
parentList.push(Tag.UL);
}
else
{
parentList.push(Tag.OL);
}
level++;
}
else
{
if (!parentList.isEmpty())
{
parentList.pop();
//remove counter at that level, in any present.
levelWiseCounter.remove(level);
}
level--;
if (level < 0)
{
level = 0;
}
}
}
else if (tag.equalsIgnoreCase("li"))
{
if (opening && level > 0)
{
//new line check
int length = output.toString().length();
if (length > 0 && (output.toString().charAt(length - 1) == '\n'))
{
}
else
{
output.append("\n");
}
//add tabs as per current level of li
for (int i = 0; i < level; i++)
{
output.append("\t");
}
// append dot or numbers based on parent tag
if (Tag.UL == parentList.peek())
{
output.append("•");
}
else
{
//parent is OL. Check current level and retreive counter from levelWiseCounter
int counter = 1;
if (levelWiseCounter.get(level) == null)
{
levelWiseCounter.put(level, 1);
}
else
{
counter = levelWiseCounter.get(level) + 1;
levelWiseCounter.put(level, counter);
}
output.append(padInt(counter) + ".");
}
//trailing tab
output.append("\t");
}
}
}
/**
* Add padding so that all numbers are aligned properly. Currently supports padding from 1-99.
*
* #param num
* #return
*/
private static String padInt(int num)
{
if (num < 10)
{
return " " + num;
}
return "" + num;
}
private enum Tag
{
UL, OL
}
}
How about the next code (based on this link) :
public class TextViewHtmlTagHandler implements TagHandler
{
/**
* Keeps track of lists (ol, ul). On bottom of Stack is the outermost list
* and on top of Stack is the most nested list
*/
Stack<String> lists =new Stack<String>();
/**
* Tracks indexes of ordered lists so that after a nested list ends
* we can continue with correct index of outer list
*/
Stack<Integer> olNextIndex =new Stack<Integer>();
/**
* List indentation in pixels. Nested lists use multiple of this.
*/
private static final int indent =10;
private static final int listItemIndent =indent*2;
private static final BulletSpan bullet =new BulletSpan(indent);
#Override
public void handleTag(final boolean opening,final String tag,final Editable output,final XMLReader xmlReader)
{
if(tag.equalsIgnoreCase("ul"))
{
if(opening)
lists.push(tag);
else lists.pop();
}
else if(tag.equalsIgnoreCase("ol"))
{
if(opening)
{
lists.push(tag);
olNextIndex.push(Integer.valueOf(1)).toString();// TODO: add support for lists starting other index than 1
}
else
{
lists.pop();
olNextIndex.pop().toString();
}
}
else if(tag.equalsIgnoreCase("li"))
{
if(opening)
{
if(output.length()>0&&output.charAt(output.length()-1)!='\n')
output.append("\n");
final String parentList=lists.peek();
if(parentList.equalsIgnoreCase("ol"))
{
start(output,new Ol());
output.append(olNextIndex.peek().toString()+". ");
olNextIndex.push(Integer.valueOf(olNextIndex.pop().intValue()+1));
}
else if(parentList.equalsIgnoreCase("ul"))
start(output,new Ul());
}
else if(lists.peek().equalsIgnoreCase("ul"))
{
if(output.charAt(output.length()-1)!='\n')
output.append("\n");
// Nested BulletSpans increases distance between bullet and text, so we must prevent it.
int bulletMargin=indent;
if(lists.size()>1)
{
bulletMargin=indent-bullet.getLeadingMargin(true);
if(lists.size()>2)
// This get's more complicated when we add a LeadingMarginSpan into the same line:
// we have also counter it's effect to BulletSpan
bulletMargin-=(lists.size()-2)*listItemIndent;
}
final BulletSpan newBullet=new BulletSpan(bulletMargin);
end(output,Ul.class,new LeadingMarginSpan.Standard(listItemIndent*(lists.size()-1)),newBullet);
}
else if(lists.peek().equalsIgnoreCase("ol"))
{
if(output.charAt(output.length()-1)!='\n')
output.append("\n");
int numberMargin=listItemIndent*(lists.size()-1);
if(lists.size()>2)
// Same as in ordered lists: counter the effect of nested Spans
numberMargin-=(lists.size()-2)*listItemIndent;
end(output,Ol.class,new LeadingMarginSpan.Standard(numberMargin));
}
}
else if(opening)
Log.d("TagHandler","Found an unsupported tag "+tag);
}
private static void start(final Editable text,final Object mark)
{
final int len=text.length();
text.setSpan(mark,len,len,Spanned.SPAN_MARK_MARK);
}
private static void end(final Editable text,final Class<?> kind,final Object... replaces)
{
final int len=text.length();
final Object obj=getLast(text,kind);
final int where=text.getSpanStart(obj);
text.removeSpan(obj);
if(where!=len)
for(final Object replace : replaces)
text.setSpan(replace,where,len,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
return;
}
private static Object getLast(final Spanned text,final Class<?> kind)
{
/*
* This knows that the last returned object from getSpans()
* will be the most recently added.
*/
final Object[] objs=text.getSpans(0,text.length(),kind);
if(objs.length==0)
return null;
return objs[objs.length-1];
}
private static class Ul
{
}
private static class Ol
{
}
}
I had the problem, that I always got an empty line after a list with #Kuitsis solution. I added a few lines in handleTag() and now the empty lines are gone:
#Override
public void handleTag(final boolean opening, final String tag, final Editable output, final XMLReader xmlReader) {
if (UL_TAG.equalsIgnoreCase(tag)) {
if (opening) { // handle <ul>
lists.push(new Ul());
} else { // handle </ul>
lists.pop();
if (output.length() > 0 && output.charAt(output.length() - 1) == '\n') {
output.delete(output.length() - 1, output.length());
}
}
} else if (OL_TAG.equalsIgnoreCase(tag)) {
if (opening) { // handle <ol>
lists.push(new Ol()); // use default start index of 1
} else { // handle </ol>
lists.pop();
if (output.length() > 0 && output.charAt(output.length() - 1) == '\n') {
output.delete(output.length() - 1, output.length());
}
}
} else if (LI_TAG.equalsIgnoreCase(tag)) {
if (opening) { // handle <li>
lists.peek().openItem(output);
} else { // handle </li>
lists.peek().closeItem(output, lists.size());
}
} else {
Log.d("TagHandler", "Found an unsupported tag " + tag);
}
}
this is a confirmation to what kassim has stated. there is fragmentation. i found how to resolve this. i have to rename <li> and ul to a custom tag. so:
myHTML.replaceAll("</ul>","</customTag>").replaceAll("<ul>","<customTag>");
//likewise for li
then in my handler i can look for that customTag (which does nothing) and make it do something.
//now my handler can handle the customtags. it was ignoring them after nougat.
public class UlTagHandler implements Html.TagHandler {
//for ul in nougat and up this tagHandler is completely ignored
#Override
public void handleTag(boolean opening, String tag, Editable output,
XMLReader xmlReader) {
if (tag.equals("customtag2") && opening)
output.append("\n\t\u25CF\t");
if (tag.equals("customtag2") && !opening)
output.append("\n");
}
}
this should make it work for all versions of android.

Categories

Resources