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.
Related
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();
First I have to say I have read similar questions and answers here on SO and this question is basically a duplicate of this question and many others but the answers given to those questions doesn't work like the way i want it.
The problem:
Setting length filter on my EditText programmatically like this:
editText.setFilters(new InputFilter[]{new LengthFilter(10)} );
The only thing it does is hide the text that go over the limit in the EditText. It still shows the long (unlimited) text in suggestion box and i have to delete (backspace) for each letter that go over before being able to delete what is shown in the EditText.
Suggested Solutions:
Setting InputType to textFilter.
Programmatically I did this:
editText.setInputType( InputType.TYPE_TEXT_VARIATION_FILTER );
It hides suggestions but the unlimited text is still present and i still have to use backspace to delete letters that shouldn't be present.
Setting InputType to textNoSuggestions|textVisiblePassword.
Programmatically I did this (had to add TYPE_CLASS_TEXT too otherwise it wouldn't work):
editText.setInputType( InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD );
This one does work but the problem is it stops "gesture typing" and it changes the font to monospace.
Better Solutions?
As you can see these two methods don't actually work without additional problems. Is there any other way of doing this that I missed. Should I just use a TextWatcher if I want to keep gesture typing and suggestions?
I ended up using a TextWatcher instead. I'm not sure if it is the best way to do this but it does work with suggestions and it doesn't turn off gesture typing or change the font style. Here's how I did it (I'm quite new to android so if this needs improvement feel free to let me know).
I added an example in the comments to clarify what is going on.
Make these global variables:
private boolean mWatcherIsBlocked = false;
private String mBeforeChange;
private String mFilteredString;
private int mCursorPosition = 0;
Then create the TextWatcher and add it to your EditText
final int maxLength = 10; // desired length limit
/**
* lets say our EditText is showing "abcdefgh". We select "cdef" from it and
* paste a new text "ijklmnop" in the middle. What we should get according to
* our maxLength is this:
* (1) "ab" (0th up to the letter from before_change_text we were selecting) +
* (2) "ijklmn" (part of the text we pasted minus the number of letters the whole
* after_change_text goes over the 10 letter limit) +
* (3) "gh" (last part of before_change_text that wasn't selected)
*
* so the new text has to be "abijkmngh"
*/
TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// get before_change_text if textWatcher isn't blocked
if (!mWatcherIsBlocked) mBeforeChange = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!mWatcherIsBlocked){
// get after_change_text if textWatcher isn't blocked
String after = s.toString();
// if after_change_text's length is bigger than the limit
if (after.length() > maxLength) {
// see how much it goes over the limit
int over = after.length() - maxLength;
// add parts (1) and (2) like our example above
String st = mBeforeChange.substring(0, start) + // (1)
after.substring(start, start + count - over); // (2)
// get where the cursor position should be after pasting (
// = after the last letter we could paste = length of (1) + (2) )
mCursorPosition = st.length();
// now add part (3) of our text to the first two
st += mBeforeChange.substring(
mBeforeChange.length() - (maxLength - st.length()),
mBeforeChange.length());
// now assign this new text to a global variable
mFilteredString = st;
} else {
// if after_change_text hasn't gone over the limit assign it
// directly to our global variable
mFilteredString = s.toString();
}
}
}
#Override
public void afterTextChanged(Editable s) {
// if filtered text is not the same as unfiltered text
// or textWatcher is not blocked
if (!mFilteredString.equals(s.toString()) && !mWatcherIsBlocked) {
// block textWatcher to avoid infinite loops created by setText
// (this might not work as I well as I think!)
mWatcherIsBlocked = true;
// set new text to our EditText
editText.setText(mFilteredString);
// set its cursor position
editText.setSelection(mCursorPosition);
// unblock the textWatcher
mWatcherIsBlocked = false;
}
}
};
// add the TextWatcher to our EditText
editText.addTextChangedListener(textWatcher);
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
}
});
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"
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());
}
}