i create a form with an EditText that takes in input numberDecimal (i set android:inputType="numberDecimal" in related XML file) and i write the following activity that prevent inserting numbers with more than 2 decimal places (i used .setFilters()). In addition i set a suffix in the same EditText and i would avoid that users can delete this suffix, neither add some input after it. I mean that if users go at the end of the EditText and tries to press backspace button the cursor goes at the begin of the suffix " m" (including space).
How to do that?
public class InputForm extends Activity {
EditText inputField;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.input_form);
inputField = (EditText) findViewById(R.id.editInput);
inputField.setText(" m");
inputField.setSelection(0);
inputField.setFilters(new InputFilter[] {new DecimalDigitsInputFilter(2)});
}
public class DecimalDigitsInputFilter implements InputFilter {
Pattern mPattern;
public DecimalDigitsInputFilter(int digitsAfterZero) {
mPattern = Pattern.compile("[0-9]+((\\.[0-9]{0," + (digitsAfterZero-1) + "})?)||(\\.)?");
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
int input_length = dest.length() - 2; // lenght of the input without " m"
Matcher matcher = mPattern.matcher(dest.subSequence(0, input_length));
if(!matcher.matches())
return "";
return null;
}
}
}
One way that you can manage keeping the right side of your EditText would be to arrange the layout such that it is composed of 2 EditText views. One with a layout attribute to the right and another with a layout to the left of the suffix EditText filling up the remainder of the layout window.
Then make the second EditText unselectable. If you don't put borders around the EditText and force the text in the window on the left to type from right to left, then you will get the appearance you desire.
You could also keep track of the cursor and then after input you could change the position of the cursor if it is in the wrong location, or check the input for having the suffix that you are expecting and replace it as they type. This will have very little visually noticeable change. But I don't believe there is a built-in way to handle the approach you are requesting.
Related
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);
I tried many combinations of inputType, singleLine and maxLines, but I just can't get the behavior that I want.
Basically, I want the EditText to be a single string with no new lines allowed, but also to expand vertically if the string is longer than it's initial size. (all text needs to be displayed on screen)
textMultiLine expands when the string is long, but it does not prevent the usage of the new line character. singleLine has no effect in this case and maxLines simply changes the size of the box while you can still create new lines.
Anything other than textMultiLine does not expand vertically and the text will simply scroll horizontally if it's too long to fit.
As I already mentioned in my comment, you listen for changes using TextWatcher. Unfortunately, there's (as of writing this answer) no other ways of doing this. As per your second comment though, here's an optimization suggestion.
Listeners can be classes, and they can be standalone classes. It doesn't have to be the current class or an anonymous inner class. For an instance, you can create a class like this:
public class SingleLineET implements TextWatcher {
EditText et;
public SingleLineET(EditText et){
this.et = et;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String converted = s.toString();
if(converted.contains("\n")){
//There are occurences of the newline character
converted = converted.replace("\n", "");
et.setText(converted);
}
}
#Override
public void afterTextChanged(Editable s) {
}
}
And whereever you want to have a listener, you simply do:
EditText theET = ...;
theET.addTextChangedListener(new SingleLineET(theET));
And like that you cut down the amount of places you need the same boilerplate code.
There may be some alternative in a later release of Android (or someone creates a view that automates this) but until then, using a class instead of manually creating code for it every time at least cuts down some lines
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"
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.