I have been having issues with some odd behaviours from the EditText .setSelection that I am hoping you can all help with!
The app I am working on has a search field and there is the need to have it behave very similar to a browser search bar. For example, if the user types "fo", we would want the EditText to autocomplete to "foobar" with the autocompleted "obar" text highlighted so it can be easily replaced by the user incase the autocomplete does not match what the user was intending to type.
To accomplish this, I have an EditText field with a TextWatcher setup to try to autocomplete the text afterTextChanged. The following is my code:
editText.addTextChangedListener(new TextWatcher() {
int lastCount = 0;
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable editable) {
editText.removeTextChangedListener(this);
String searchString = editable.toString();
if (editable.length() > lastCount) {
lastCount = editable.length();
int oldLength = searchString.length();
String autoFillResult = completeAutofill(searchString);
if (!autoFillResult.equals("")) {
searchString = autoFillResult;
editable.clear();
editable.append(autoFillResult);
editText.setSelection(oldLength, autoFillResult.length());
}
} else lastCount = editable.length();
editText.addTextChangedListener(this);
}
});
My issue is as follows. Using the previous "Foobar" case:
The user types "F", the EditText autofills "oobar" and highlights it.
Then the user types the first "o"
The EditText field is momentarily cleared (i.e. afterTextChanged receives an Editable with the empty string)
The EditText correctly autofills to user supplied "Fo" followed by autocompleted "obar" which is highlighted.
The issue is the EditText is being cleared then repopulated when the user types the next character, which creates a noticeable disturbance in the EditText field. Interestingly, I have singled out the editText.setSelection(oldLength, autoFillResult.length()); as the culprit (i.e. commenting out the line gets rid of the issue, but obviously its the wrong functionality).
After completing some Google research and my own debugging I am still unsure why this is happening. The issue does not appear excessively common as I could not find it on Google and I could not figure out the reason for this issue in my own experimentation.
Thank you in advance!
the if condition should possibly be:
autoFillResult != null && !autoFillResult.equals("")
while you might be looking for (or attempting to recreate) an AutoCompleteTextView.
Try removing editable.clear();
Related
This question already has answers here:
Put constant text inside EditText which should be non-editable - Android
(18 answers)
Closed 2 years ago.
I'm quite new to android development and when working on my company project, I encounter a problem.
I need an Edittext with fixed suffix as part of the Edittext so that when user type in, the suffix will wrap to new line just like normal content of the Edittext, but user can not edit and select this suffix part.
I have search around and can't find a good solution for this problem. Some suggest to add a drawable contains text to right of edittext, but this solution will not make the suffix part wrap to new line when people type in.
Another possible solution is to handle text change for the edittext but this will lead to very complicated handle for the cursor of edittext(like handle text-hint and user selection gesture, ...)
So my question is: Is there anyone have implement something like this or can someone point me some directions to easily implement this feature for edittext.
Here is dirty solution to your problem.
Lets assume you have an EditText called mEditText, a String called SUFFIX, and a boolean addedSuffix:
boolean addedSuffix = false;
String SUFFIX = " my suffix";
EditText mEditText = (EditText) findViewById(R.id.my_edit_text);
attach a textWatcher to your EditText
mEditText.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) {
// if the only text is the suffix
if(s.toString().equals(SUFFIX)){
mEditText.setText(""); // clear the text
return;
}
// If there is text append on SUFFIX as long as it is not there
// move cursor back before the suffix
if(s.length() > 0 && !s.toString().contains(SUFFIX) && !s.toString().equals(SUFFIX)){
String text = s.toString().concat(SUFFIX);
mEditText.setText(text);
mEditText.setSelection(text.length() - SUFFIX.length());
addedSuffix = true; // flip the addedSuffix flag to true
}
}
#Override
public void afterTextChanged(Editable s) {
if(s.length() == 0){
addedSuffix = false; // reset the addedSuffix flag
}
}
});
Like i said this is a quick and dirty solution, so this only adds the suffix when the user actually types into the EditText field. If you need it to be added before the user starts typing you can modify the logic to do so on your own.
Good luck and Happy Coding!
Please try this code on your edittext's onfocuschangedlistener.
your suffix value added when your focuschangelistener not focused or focusing on other fields.
//sdur is my edittext name
sdur.setOnFocusChangeListener(View.OnFocusChangeListener { v, hasFocus ->
if (!hasFocus) {
if((!sdur.text.toString().endsWith("mins")&&(sdur.text.toString().isNotEmpty()))){
var jj=sdur.text.toString()
sdur.setText("$jj mins")
}
// code to execute when EditText loses focus
}
});
Hi I am learning Android programming and have run into an issue that I couldn't get a clear answer to through researching.
I have a TextView which serves as a label for my EditText. I have a method which checks if the EditText is an empty String. If the string is empty I want to be able to get a reference to the TextView that corresponds to that EditText in order to make a toast saying something like "please enter a value for ".
I've looked into getLabelFor/setLabelFor but is there a way to do this in the layout XML?
What is best practice for this type of functionality.
You're describing a functionally that is build in to EditText. There is a special field you can define in xml called hint, which is the recommended way to label an EditText rather than a nearby TextView. Additionally, EditText has a method called setError() (link). If the user attempts to hit a submit button, for example, you can check to see if the EditText is empty and if so, call setError().
I wonder if the following is the thing that you need
TextWatcher inputTextWatcher = new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().equals("")) {
textView.setText("please enter a value for ..");
} else {
textView.setText("<the textedit is not empty>");
}
}
public void afterTextChanged(Editable s) {
}
};
editText.addTextChangedListener(inputTextWatcher);
Intro:
I am currently trying to implement an input method for an EditText for my Crossword Puzzle where the user sees something like "____" in the EditText. The underscores mark missing letters, the first char entered will fill the first underscore.
Of course other cells in the puzzle might be solved already, so the EditText text could be "ST_CKOV_RF_OW". I had all this functionality already in my own input view, a subclass of view with an overridden onDraw(). This worked pretty well, except that the view won't appear on some lower Android versions and the Back key slipped through my input routine and wasn't accessible.
So I thought I'd do it with EditText, implement a TextWatcher and be fine, but I can't get it to work properly. What I have right now is working, I can use the keyboard to enter letters, but again the Backspace isn't working, and of course if the user touches into the EditText the position gets messed up.
public void beforeTextChanged(CharSequence s,int start,int count, int after){
et.removeTextChangedListener(textWatcher);
int position = text.indexOf("_");
if(position==-1) onAnswerEntered(et.getText().toString().replace("_", "")); //finished
else {
et.setSelection(et.getText().toString().indexOf("_"));
et.addTextChangedListener(textWatcher);
}
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
et.removeTextChangedListener(textWatcher);
try {
String currentKey = s.toString().substring(start, start+1);
Logger.log("Current Key", currentKey);
int position = text.indexOf("_");
Logger.log("Current Position _ ", position+"");
//replace _ with key
String sbefore=text.substring(0, position);
String safter=text.substring(position+1, text.length());
text=sbefore+currentKey+safter;
int positionNext = text.indexOf("_");
Logger.log("Next Position _ ", positionNext+"");
if(positionNext==-1) onAnswerEntered(et.getText().toString().replace("_","")); //finished
else {
et.setText(text);
et.setSelection(et.getText().toString().indexOf("_"));
et.addTextChangedListener(textWatcher);
}
} catch(IndexOutOfBoundsException ioobe) {
ioobe.printStackTrace();
}
}
I also tried to set an OnKeyListener, but it won't work on EditText (I can get backspace event, nothing else)
So maybe I am totally on the wrong track, but please help me and give me a clue to how I can accomplish my goal. Thanks.
I gave up on it and implemented a simple but working kind of hack. I receive input in my (hidden) EditText now, the output goes to the visible TextView, a function in between fills the "_" with the input from the EditText.
Ex.
hint = "A_A_A_A"
edittext input = "BBB"
textview shows "ABABABA"
Moving an app from 2.2 to 3.x, one of my EditText's that I was using a TextWatcher on for validation is behaving badly. In short, when a user clicks on the EditText and the entire word goes into 'suggestions mode' (where it is underlined), it effectively gets removed from the EditText from the TextWatcher's perspective, triggering my text validation check that I do for an empty EditText. The code:
mText = (EditText) findViewById(R.id.inpt_title);
mText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable editable) {
final String title = editable.toString();
Log.d(LOG_TAG, "addTextChangedListener(): title: " + title + ", length: " + title.length());
if (title.length() == 0) {
// empty title
mText.setError(getString(R.string.error_note_title_empty));
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mText.setTextColor(getResources().getColor(R.color.black));
}
#Override
public void onTextChanged(CharSequence s, int start, int count, int after) {}
});
I'd like to keep suggestions working, but there seems to be some weird interaction here.
Any way to either a) keep the EditText from being empty when the entire word is in 'suggestion mode', or at least checking to see if the EditText is in the 'suggestion' state to determine if the EditText is truly empty, or b) turing off suggestions? I've tried android:inputType="text|textCapWords|textNoSuggestions" for the EditText in question, as well as setting it via mText.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); in the code above but suggestions keep happening on a Lenovo 3.1 tablet.
Update:
I see API 14 added a isInputMethodTarget() method to the EditText, which I could use to check for active suggestions and disable the validation... but I am running against API 12. Perhaps I could check the IME directly to see if the suggestions are active?
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.