What I'm trying to achieve is to have an EditText with the following behaviour:
At first, the text show:
0.00 (en_GB local) or 0,00 (fr_FR local)
When user type the number 4, the Edittext show:
0.04 (en_GB) or 0,04 (fr_FR)
When user type another 6:
0.46 (en_GB) or 0,46 (fr_FR)
Etc.
Currently my code works very fine when the local is set to en_GB, however when the local change to fr_FR, I get an infinite loop.
Here is the code:
EditText layout:
<EditText
android:id="#+id/amount_ET"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="numberDecimal"
android:maxLength="8" />
And the java code:
amountEditText = (EditText) findViewById(R.id.amount_ET);
amountEditText.setText(mValueToString());
// ensure the cursor is always in the beginning of the text.
amountEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (v.hasFocus()) {
int len = amountEditText.getEditableText().toString().trim().length();
if (len > 1) {
amountEditText.setSelection(0);
amountEditText.setCursorVisible(false);
}
}
}
});
// add listener in order to intercept input on the EditText
amountEditText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
// we only update if the value is not what we expect to avoid infinite loop
if (!s.toString().isEmpty() && !s.toString().equals(mValueToString())) {
// the cursor is always at the first position, so new char are in the beginning of the editable
updateAmountTextView(s, s.charAt(0));
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
}
/**
* Update the new value with the given numeral and update the given Editable with the new value.
*
* #param s
* if not null, will be updated with the formatted value.
* #param c
* the char representing the number to insert.
*/
protected void updateAmountTextView(Editable s, char c) {
Integer i = Integer.valueOf(c - 48);
mValue = mValue * 10 + i;
if (s != null) {
s.clear();
s.append(mValueToString()); // when using fr_FR local, the coma is not appended leading to a false value in the editable.
}
amountEditText.setSelection(0);
}
/**
* return the String formatted value.
*/
protected String mValueToString() {
Double d = Double.valueOf(mValue) / 100;
NumberFormat formatter = new DecimalFormat("#0.00");
return formatter.format(d);
}
Has explained in the comments of the java code, the bug is in the method updateAmountTextView() where the Editable refuse to take the coma.
When changing the inputType of the EditText to the value text this bug doesn't occur, except I want to have a softkeyboard accepting numbers only.
I guess I should do something on the Editable object before appending value to it but I can't find anything about this in the documentation.
As pointed in the comments by https://stackoverflow.com/users/427291/devisnik adding the line android:digits="0123456789.," in the Edittext layout solved the issue.
Related
I have an EditText which is supposed to have multi-line while typing and should have an action done when enter is pressed, meaning the soft keyboard should disappear and the cursor made invisible... basically the EditText should lose "focus".
Now this is done and working, but the problem is that the "enter" key leaves a new line in the EditText.
I have tried to remove it by setting the entire text to "", but the EditText is blank with a new line.
I have tried to remove it by replacing '\n' with a 's' and setting the text back, but the text starts in a new line.
String cap = caption.getText().toString().replace('\n', 's').trim();
caption.setText(cap);
Thanks in advance.
I know this is a late answer but maybe it can help someone.
I recently had this problem and I solved it by implementing an addTextChangedListener to my EditText.
Code:
EditText editText = new EditText(this);
layout.addView(editText);
editText.addTextChangedListener(new TextWatcher() {
boolean ignore = false; // This is used to prevent infinite recursion in the afterTextChanged method
#Override
public void afterTextChanged(Editable arg0) {
if (ignore) return;
ignore = true;
String s = arg0.toString();
if (s.length() > 0) {
// The condition checks if the last typed char's ASCII value is equal to 10, which is the new line decimal value
if (((int)(s.charAt(s.length()-1)) == 10)) {
String newStr = s.substring(0, s.length()-1); // Removes the new line character from the string
editText.setText(newStr);
editText.setSelection(editText.length()); // Sets the text cursor to the end of the text
}
}
ignore = false;
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
There might be better ways to get rid of the new line character, but this worked for me and seems simple enough.
Here is my scenario,
I just wanna get only number and a particular special character like $ at the end of the EditText field.
Ex: 234.34$
I don't wanna validate after entering this input, but rather user can only enter decimal number and at the end this special character.
Some one please help me to do this.
you can use the following (progromatically)
ed.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
ed.setKeyListener(DigitsKeyListener.getInstance("0123456789.$"));
you can define whatever characters you want within the DigitsKeyListener.getInstance
or if dont want the user to enter the $ sign and you want to be entered manually after the user finishes editting...
#Override
protected OnFocusChangeListener getOnFocusChangeListener() {
return new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
//Member variable in the class which contains an EditText
CurrencyTextbox.this.hadFocus = true;
} else {
// Thes EditText has lost focus
Log.v(TAG, "Lost focus.");
if (CurrencyTextbox.this.hadFocus) {
// We previously had focus, now we lost it, format the user input!
// Get current value of the Textbox
String value = CurrencyTextbox.this.textbox.getText().toString();
// Formatting the user input
value = String.format(//Doing some formatting);
// Reset the had focus
CurrencyTextbox.this.hadFocus = false;
}
}
}
};
Use this in your EditText android:inputType="numberDecimal"
In the Class file add a TextWatcher to the EditText as following code and in the method afterTextChanged(Editable s) add "$" at the end of the text in EditText.
EditText input = (EditText) findViewById(R.id.search_text_box);
input.addTextChangedListener(onInputTextChanged);
public TextWatcher onInputTextChanged = new TextWatcher()
{
#Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
#Override
public void afterTextChanged(Editable s)
{
// Add "$" at the end of text in EditText
}
};
In the Edit text add the property android:inputType="numberDecimal"
You can accept only numbers and phone number type using java code
EditText number1 = (EditText) layout.findViewById(R.id.edittext);
number1.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
number1.setKeyListener(DigitsKeyListener.getInstance("Replace with your special characters”));
This code will avoid lot of validations after reading input
I have provided an AutoCompleteTextView to show suggestions to the user. Based on the item selected by the user, I am getting the ID of the item and using it in data base side. Now my problem is to force the user to make selection only from AutoCompleteTextView (i.e. user should not enter his own text). It is a client requirement. How to do this?
Here's a pretty straightforward solution:
You can create a variable to store the selected value by setting setOnItemClickListener in your AutoCompleteTextView. Then you can null that value whenever a user types in the field by adding a TextWatcher to it. Finally, you can validate your variable is not null before continuing.
String my_var; //keep track!
AutoCompleteTextView tv = (AutoCompleteTextView) layout.findViewById(R.id.tv);
tv.setAdapter(my_adapter);
tv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
my_var = my_adapter.getItem(position).toString();
}
});
/**
* Unset the var whenever the user types. Validation will
* then fail. This is how we enforce selecting from the list.
*/
tv.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
my_var = null;
}
#Override
public void afterTextChanged(Editable s) {}
});
I happened to need such a requirement for a project I am working on, and I though I'll share you guys the way I implemented the required.
I added a on focus change listener for the auto-complete text view and checked when the user has focus changed focus from the auto-complete, and handled the situation straight forward.
autoTextViewCountry.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, boolean b) {
if(!b) {
// on focus off
String str = autoTextViewCountry.getText().toString();
ListAdapter listAdapter = autoTextViewCountry.getAdapter();
for(int i = 0; i < listAdapter.getCount(); i++) {
String temp = listAdapter.getItem(i).toString();
if(str.compareTo(temp) == 0) {
return;
}
}
autoTextViewCountry.setText("");
}
}
});
So my implementation is: if the typed text doesn't exist in the array adapter then on focus changed empty the text view, and later on when continuing to next stage of say registration, check if this text view is empty or not.
Hope this approach helps somebody.
Happy coding.
NiceAutoCompleteTextView will give you the ability to check whether a selection was made from the drop-down popup, by invoking isSelectionFromPopup()
Just add this property to your AutoCompleteTextView.
android:focusable="false"
My Code looks like:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/menu"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Mode">
<AutoCompleteTextView
android:id="#+id/mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
/>
</com.google.android.material.textfield.TextInputLayout>
On Java Side:
AutoCompleteTextView mode = findViewById(R.id.mode);
final List<String> modeList = new ArrayList();
modeList.add("YEARLY");
modeList.add("HALF-YEARLY");
modeList.add("QUARTER-YEARLY");
modeList.add("MONTHLY");
mode.setAdapter(new ArrayAdapter(getApplicationContext(),R.layout.list_item,modeList));
To get the Text of AutoCompleteTextView:
mode.getText().toString()
Ok I assume you would like to limit the input of the user to the texts contained in the list of items listed in the suggest box.
For instance if you have:
One
Two
Three
then the user could only type for the first character "O" and "T".
And so on according to the text entered before.
To achieve this you can utilize the setFilters method of TextView:
editBox = (TextView) findViewById(R.id.editBox);
editBox.setFilters(getFilters());
editBox.addTextChangedListener(this);
editBox.setOnFocusChangeListener(this);
Additionally you would probably need the text change listener and focus listener to react and update the filtered list when a new character is entered ... plus to update the filter.
Here is an example of decimal number filter I have used on a project:
protected InputFilter[] getFilters()
{
InputFilter[] filters = new InputFilter[1];
filters[0] = new InputFilter()
{
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
{
// limit input to digits and decimal / thousand separator only
// in case thousand separator is pressed, change it to decimal
// separator instead
String output = "";
if (number.isFocused())
{
for (int i = start; i < end; i++)
{
char c = source.charAt(i);
if (isDecimalOrThousandSeparator(c))
{
output = output + Character.toString(decimalSeparator);
}
else if (Character.isDigit(c))
{
output = output + Character.toString(c);
}
}
return output == "" ? null : output;
}
return null;
}
};
return filters;
}
A simple solution would be to just check if the current input is one of the items in the adapter. You can do it like this:
val AutoCompleteTextView.isValid: Boolean
get() {
for (i in 0 until adapter.count) {
if (adapter.getItem(i) == text.toString()) {
return true
}
}
return false
}
here is the another solution for this proAutoCompleteTextView.Validator to ensure the valid values. Validator is invoked when edit text looses focus.
autoCompleteTextView.validator = object : AutoCompleteTextView.Validator {
override fun isValid(text: CharSequence?): Boolean {
return optionsList.contains(text.toString())
}
override fun fixText(invalidText: CharSequence?): CharSequence {
return ""
}
}
Where optionsList is list of valid values.
I was having the same requirement, so here is my implementation:
autoCompleteTextView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
autoCompleteTextView.showDropDown();
}
});
In the xml set focusable=false and focusableInTouchMode=false.
Happy coding
In order to have a non editable variation of the AutoCompleteTextView, you should disable user input in the AutoCompleteTextView. That can be achieved by setting android:inputType="none" on the AutoCompleteTextView.
In Android, I create a password field like this:
EditText text = new EditText(context);
text.setTransformationMethod(PasswordTransformationMethod.getInstance());
Or like this, which seems to do the same thing:
EditText text = new EditText(context);
text.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
I get a nice password field except for the last character typed by the user. It's visible on the screen for a few seconds before beeing masked with a dot.
Here is a screenshot:
Do you know how to fix this behaviour please?
This is expected behavior. With the soft keyboards on most devices, it is valuable feedback that they are typing the password correctly.
For a list of all of the different inputTypes you can specify and how they change the EditText,
see android inputTypes .
Also, it is possible to change this behavior by implementing your own TransformationMethod and setting it via setTransformationMethod(), but I would not recommend doing that. Users will expect the behavior you are seeing and by changing your app, you'll be providing an inconsistent user experience.
also check this android article
Implementation of TransformationMethod to hide last char typed in password:
public class LoginActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// example of usage
((TextView) findViewById(R.id.password)).setTransformationMethod(new HiddenPassTransformationMethod());
}
private class HiddenPassTransformationMethod implements TransformationMethod {
private char DOT = '\u2022';
#Override
public CharSequence getTransformation(final CharSequence charSequence, final View view) {
return new PassCharSequence(charSequence);
}
#Override
public void onFocusChanged(final View view, final CharSequence charSequence, final boolean b, final int i,
final Rect rect) {
//nothing to do here
}
private class PassCharSequence implements CharSequence {
private final CharSequence charSequence;
public PassCharSequence(final CharSequence charSequence) {
this.charSequence = charSequence;
}
#Override
public char charAt(final int index) {
return DOT;
}
#Override
public int length() {
return charSequence.length();
}
#Override
public CharSequence subSequence(final int start, final int end) {
return new PassCharSequence(charSequence.subSequence(start, end));
}
}
}
}
I came across this problem after needing to implement a pin-code type user interface. After the user entered 1 number (1:1 relationship between EditTexts and pin numbers for a total of 4 EditTexts) the password would remain "in the open". My solution was to implement a TextWatcher that replaced the input with bullets (•'s).
See the full answer here
I was also facing same issues, in my case there was 3 edit text each edit text can have maximum 4 digit number for Aadhaar number. I used numberPassword input type.
When user type Aadhaar number very fast first edit text last text 4th was not hiding because I am changing focus when 4 digit completed. I have added text watcher.
I got solution at the end of last edit text when 4 digit completed I requestFocus for all 3 edit text one by one.
Solution :
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (getBinding().edtFirstFourNumber.getText().toString().length() == 4 && getBinding().edtMiddleFourNumber.getText().toString().length() == 4 && getBinding().edtLastFourNumber.getText().toString().length() == 4) {
// getBinding().homeProceed.setAlpha(1f);
getBinding().homeProceed.setTextColor(ContextCompat.getColor(mContext, R.color.color_ffffff));
getBinding().homeProceed.setEnabled(true);
} else {
// getBinding().homeProceed.setAlpha(0.5f);
getBinding().homeProceed.setTextColor(ContextCompat.getColor(mContext, R.color.color_4090BE));
getBinding().homeProceed.setEnabled(false);
}
if (getBinding().edtFirstFourNumber.length() == 4) {
getBinding().edtMiddleFourNumber.requestFocus();
}
if (getBinding().edtMiddleFourNumber.length() == 4) {
getBinding().edtLastFourNumber.requestFocus();
}
if (getBinding().edtLastFourNumber.length() == 4) {
getBinding().edtFirstFourNumber.requestFocus();
getBinding().edtMiddleFourNumber.requestFocus();
getBinding().edtLastFourNumber.requestFocus();
CommonUtils.hideSoftKeyboard(getActivity());
}
}
I have a TextWatcher set on an EditText that changes the input type after a user types a number followed by a space.
If the user types two numbers the input type switches and accepts the next character, but if the user types only one number and presses space the input type still changes but it won't accept the first character the user tries to input.
I've tested this on Froyo and 1.6, it only happens on Froyo, 1.6 works like it should.
Here's the code:
TextWatcher watcher = new TextWatcher() {
#Override
public void afterTextChanged (Editable s) {
}
#Override
public void beforeTextChanged (CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged (CharSequence s, int start, int before, int count) {
// Parsed text holder is a class that just parses the EditText and pulls out various parts.
ParsedTextHolder th = parseTextHolder(s);
String newText = "";
Boolean setTextKeyListener = false;
String tGetTextString = mQuery.getText().toString();
if (!th.pFullMatch.equals("")) {
if (th.pFullMatch.length() == 2) {
mQuery.setKeyListener(new
TextKeyListener(TextKeyListener.Capitalize.SENTENCES, true));
newText = tGetTextString + " for ";
setTextKeyListener = true;
}
}
if (setTextKeyListener) {
Log.i("setTextKeyListener", "true");
if (mQuery.getKeyListener().getClass() != TextKeyListener.class) {
mQuery.setKeyListener(new TextKeyListener(TextKeyListener.Capitalize.SENTENCES, true));
} else {
Log.d("setTextKeyListener", "skipped. already was text.");
}
if (!newText.equals("")) {
int position = newText.length();
String ttext = newText;
newText = "";
mQuery.setText(ttext, TextView.BufferType.EDITABLE);
mQuery.setText(ttext);
Editable text = mQuery.getEditableText();
Log.w("setting selectiont to text: ", text.toString());
Log.w("setting selectiont to position: ", Integer.toString(position));
Selection.setSelection(text, position);
mQuery.setKeyListener(new TextKeyListener(TextKeyListener.Capitalize.SENTENCES, true));
}
}
}
};
Also, here's an APK if you want to see what the bug is like: http://endlesswhileloop.com/files/KeyboardBug.apk
Is mQuery the editText that is being watched? According to the javadocs, you shouldn't be making any changes to the text in your EditText in onTextChanged. All such changes should be made in afterTextChanged.
Generally, I've ended up examining the change in onTextChanged and then doing the work that results form the change in afterTextChanged. You might try that.