EditText not updated after text changed in the TextWatcher - android

I have an EditText and a TextWatcher.
Skeleton of my code:
EditText x;
x.addTextChangedListener(new XyzTextWatcher());
XyzTextWatcher implements TextWatcher() {
public synchronized void afterTextChanged(Editable text) {
formatText(text);
}
}
My formatText() method inserts some hyphens at some positions of the text.
private void formatText(Editable text) {
removeSeparators(text);
if (text.length() >= 3) {
text.insert(3, "-");
}
if (text.length() >= 7) {
text.insert(7, "-");
}
}
private void removeSeparators(Editable text) {
int p = 0;
while (p < text.length()) {
if (text.charAt(p) == '-') {
text.delete(p, p + 1);
} else {
p++;
}
}
}
The problem I have is - what is displayed on my EditText isn't in sync with the Editable. When I debugged the code, I saw that the variable text (Editable) has the expected value, but what's shown on the EditText doesn't always match the Editable.
For example, when I have a text
x = "123-456-789"
I cut the text "456" from x manually.
After formatting, my Editable has the value "123-789-"
However, the value shown on my EditText is "123--789"
They have the same value in most cases though.
I assumed that the EditText IS the Editable and they should always match. Am I missing something?

Ok, you never actually change the EditText just the Editable. Android EditTexts are not children of the Editable class. Strings are subclasses of the Editable class. The onTextChangedListener doesn't receive the EditText as an argument but the Editable/String displayed in the EditText. After you format the Editable with the hyphens you then need to update the EditText. Something like this should work fine:
class MyClass extends Activity{
//I've ommited the onStart(), onPause(), onStop() etc.. methods
EditText x;
x.addTextChangedListener(new XyzTextWatcher());
XyzTextWatcher implements TextWatcher() {
public synchronized void afterTextChanged(Editable text) {
String s = formatText(text);
MyClass.this.x.setText(s);
}
}
}
To prevent the slowdown why not change the formatText method something like this?
private Editable formatText(Editable text) {
int sep1Loc = 3;
int sep2Loc = 7;
if(text.length==sep1Loc)
text.append('-');
if(text.length==sep2Loc)
text.append('-');
return text;
}
Note: I haven't tested this

Related

how to I prevent a value greater than 20 inside the edittext and if the edittext is empty, write 0 inside the edittext

I want to make a quizscore calculating app. I have some problems. How to I prevent entering value greater than 20 inside the edittext and if the edittext is empty, write 0 inside the edittext and calculate the 0 value. When the application runs, it calculates the score, but when the Edittext is entered null, the application closes. I want to if edittext is null, app sets the null value 0 and calculate score.
Here's the image my app:1: https://i.stack.imgur.com/efn9H.png
Here's the code I'm using:
public class MainActivity extends AppCompatActivity {
EditText editText1,editText2,editText3;
private Button calculate;
TextView textView1;
double c=0.0,x;
int a=0,b=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText1=findViewById(R.id.correct_answer);
editText2=findViewById(R.id.false_answer);
editText3=findViewById(R.id.turnet);
textView1=findViewById(R.id.hesap);
calculate=findViewById(R.id.calculate);
calculate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
a=Integer.parseInt(editText1.getText().toString());
b=Integer.parseInt(editText2.getText().toString());
x=a+b;
if (x<=20){
if (editText1.getText().toString().length()==0)
{
a=0;
Toast.makeText(getApplicationContext(),"Correct Answers must be max:20 .", Toast.LENGTH_LONG).show();
}
else{
a=Integer.parseInt(editText1.getText().toString());
}
if (editText2.getText().toString().length()==0)
{
Toast.makeText(getApplicationContext(),"False Answers must be max:20.", Toast.LENGTH_LONG).show();
b=0;
editText2.setText(0);
}
double c = a - (b / 3);
double result=((200.004394)+(c*3.44603333));
editText3.setText(Double.toString(c));
textView1.setText("Your Score: "+ Double.toString(result));
}
else{
Toast.makeText(getApplicationContext(),"All English questions dont greater than 20.", Toast.LENGTH_LONG).show();
}
};});}}
There is a problem with this code :
a=Integer.parseInt(editText1.getText().toString());
b=Integer.parseInt(editText2.getText().toString());
when one of your edittextes is empty, then an error will be thrown because Integer.parseInt cannot accept an empty string.
so use this:
a = 0;
b = 0;
if (editText1.getText().toString() != ""){
a=Integer.parseInt(editText1.getText().toString());
}
if(editText2.getText().toString() != ""){
b=Integer.parseInt(editText2.getText().toString());
}
x = a + b
You can use TextWatcher. Write a TextWatcher which change value to 20 if it is greater than 20 or set it to 0 if EditText's text becomes empty

text view in android can't be changed

I have TextView in my code. I want to test if an EditText is empty then fill the TextView with "some thing" or take the text from the EditText; but it doesn't change the text. Here is the code(in onCreate() method):
if ((textCity.length())==0){
cityText.setText("something");
}
else
cityText.setText(textCity);
where textCity
textCity=editText1.getText();
and cityText is the TextView
You could do something like this:
if (textCity.getText().toString().trim().length() == 0) {
cityText.setText("something");
}
else
cityText.setText(textCity);
Try this
if ((textCity.length() < 0)){
cityText.setText("something");
}
textCity=editText1.getText().toString(); //add this when you're grabbing the text from the
//textview
I assume that you haven't invoked it in the right place. You need to place a TextWatcher on your EditText. Try this out:
editText1.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
cityText.setText(String.valueOf(s));
}
});

Permanent hint in EditText

I have a situation where I would like the user to complete a sentence for me. For example, consider a EditText with a hint of "The last time I ". Normally, when a user clicks an EditText, the hint disappears, but I would like it to stay. Additionally, I would like the text to be permanent, so that it cannot be erased... leaving the user with only one option... complete the sentence.
The first part is fairly simple, just use the setText() method of EditText to place the hint. The difficult part is the latter. How can I have text in an EditText that the user cannot erase?
Well couldn't you do it in code? Some algorithim like, if the text is less than 16 characters (length of "The last time I ") then set the text to that. Therefore whenever they clicked it, if they tried to erase it, it would just go back to the default text.
Also, another idea..why don't you just make a TextView thats right edge aligns with the left edge of the EditText box, the user would never know that it was another box. This is acutally the best solution, if you don't want the text ever to be edited, just make it a TextView
Described problem can be solved using android.text.TextWatcher.
public class CompleteSentenceWathcher implements TextWatcher {
private final String initialText;
private int start;
private int after;
private int count;
public CompleteSentenceWathcher(String initialText) {
this.initialText = initialText;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
this.start = start;
this.count = count;
this.after = after;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if(start < initialText.length()) {
if(s.toString().startsWith(initialText)) {
return;
}
if(count >= 1 && after == 0) {
if(start+count+1 <= initialText.length()) {
s.replace(start, start+count, initialText.substring(start, start+count+1));
} else {
s.replace(start, start, initialText.substring(start, start+1));
}
} else if(count == 0 && after >= 1) {
s.delete(start, start+after);
}
}
}
}
Create an instance of EditText and add the TextWatcher.
EditText editText = new EditText(this);
editText.setText("I love");
editText.addTextChangedListener(new CompleteSentenceWathcher(editText.getText().toString()));
I've implemented this with an InputFilter, where _PERMANENT_HINT_TEXT is the text at the end of the EditText that I don't want the user to be able to modify. I recommend adding a color span to it, so that it is grayed out to hopefully look like a hint/disabled section of text. This should hopefully improve the UX as they should automatically assume it is unmodifiable, and not just wonder why some part of the EditText (that they usually can completely change) isn't "working". This approach allowed the text to be set after
the InputFilter was set on the EditText, which was a requirement for me since I used this on an EditTextPreference.
To be clear, I needed the permanent text to exist at the end of the EditText, instead of the beginning, but that should be symmetrical to my implementation.
new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int source_start, int source_end,
Spanned destination, int destination_start, int destination_end) {
final int protected_text_start = (TextUtils.isEmpty(destination)? source.length() : destination.length()) - _PERMANENT_HINT_TEXT.length();
// Allows input into unprotected region
if (source_start + destination_start - source_end < protected_text_start)
return null;
// Prevents deletion of protected region
else if (TextUtils.isEmpty(source))
return destination.subSequence(destination_start, destination_end);
// Ignores insertion into protected region
else
return "";
}
}
use EditText.setFilters(new InputFilters[] { /* InputFilter goes here */ }; to add it to the desired EditText.
Just checking for the length wouldn't be adequate... I could type "This is a really long text I put into the box" and it would accept it even though it doesn't begin with "The last time I" string.
Personally, I would probably go for the prevention method suggested of using a TextView over that of a check on the way out. But if you're going to validate it afterwards, you'd actually need to check the beginning of the returned string.

Problem with android password field, not hiding the last character typed

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());
}
}

Keyboard doesn't accept first character when changing inputs

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.

Categories

Resources