Making EditText accept a range of values without post validation [duplicate] - java

This question already has answers here:
Is there a way to define a min and max value for EditText in Android?
(29 answers)
Closed 3 years ago.
Is there a way to only allow numbers x through y to be entered in a EditText dynamically?
I.E don't even allow a number below x or above y to be entered?
For example x = 1 y = 12.
Of course i could solve this in Java after the user hit submit with the invalid numbers and prompt them to enter again. It's a time waster though.
Something like android:digits="01234..." but being able to specify n-th fields.
e.g
A range going from 1 to 12
The user should not get confused since they are entering a month number.
I am already using android:maxLength="2" but i need finer control.

It's a bit crude (given that I wrote it quickly) but could do something like this. I would remove the onClick method and create an OnFocusChangeListener and put the code in there.
Also from what it looks like you want something that checks Input as it's being entered and I don't really know if that's possible for a set range let alone that it'll probably cause the app to lag. As suggested by others, validation after Input is the logical route.
public void ValidateInputClick(View view) {
int Min = 1;
int Max = 12;
final TextInputLayout EditTextIP = (TextInputLayout) findViewById(R.id.layoutInput);
String str = EditTextIP.getEditText().getText().toString();
if (str.equals("") || str.contains("\n")) {
EditTextIP.setError("Cannot be blank");
EditTextIP.setErrorEnabled(true);
}
else {
int inputToInt = Integer.parseInt(str);
if (inputToInt >= Min && inputToInt <= Max) {
//Show number
Snackbar.make(view, str, Snackbar.LENGTH_SHORT).show();
EditTextIP.setErrorEnabled(false);
} else {
//Clear text
EditTextIP.getEditText().setText("");
//Show Error
EditTextIP.setError("Number must be between 1-12");
EditTextIP.setErrorEnabled(true);
}
}
}
XML:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/layoutInput">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/editText1"
android:hint="Please enter a number between 1 and 12"
android:maxLength="2"
android:onClick="ValidateInputClick"/>
</android.support.design.widget.TextInputLayout>

No need to do anything in xml.
Find EditText in code:
int minValue = 1;
int maxValue = 12;
EditText input = (EditText) findViewById(R.id.txtInput);
input.setInputType(InputType.TYPE_CLASS_NUMBER);
input.setFilters(new InputFilter[]
{
new InputFilterMinMax(minValue, maxValue),
new InputFilter.LengthFilter(String.valueOf(maxValue).length())
});
input.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
Code for InputFilterMinMax:
public class InputFilterMinMax implements InputFilter {
private int min, max;
public InputFilterMinMax(int min, int max) {
this.min = min;
this.max = max;
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
try {
int input = Integer.parseInt(dest.toString() + source.toString());
if (isInRange(min, max, input))
return null;
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
return "";
}
private boolean isInRange(int a, int b, int c) {
return b > a ? c >= a && c <= b : c >= b && c <= a;
}
}
Hope will help you!

I suggest that you still go for validation after Input. This is simply
logical, if you put yourself in End-User position. The user will try
to tap "99", yet you do not allow second digit to be more than 5. Are
you planning to somehow edit they soft-keyboard to only display
certain numbers in keyboard? That's waaay more confusing and time
consuming. Same would be with writting WARNING / INSTRUCTION messages
uppon user trying to write invalid number (= in this case you are anyway doing validation in background.
Check this SO answer for accessing certain digits in int:
SO: Accessing digits in int
And uppon entry just check for the 2nd digit, hence you allow for the first one to be from 1-9 (which is all).
if(secondDigit > 5){
// Warn the user of incorrect input
}

you can use Textwatcher for this.
please look at below code. In onTextChanged you can get which character is entered and count of Character entered.
TextWatcher textwatcher = getMandatoryTextWatcher(editText);
String text = editText.getText().toString();//At this point String text is not empty.
editText.addTextChangedListener(textwatcher);
public TextWatcher getMandatoryTextWatcher(final EditText editText) {
TextWatcher textwatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//To change body of implemented methods use File | Settings | File Templates.
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//To change body of implemented methods use File | Settings | File Templates.
}
#Override
public void afterTextChanged(Editable s) {
try {
//The first time this is called(the first softkey backspace press),
//text is empty. This causes a weird issue where the character that
//was just removed by pressing the backspace, reappears again.
//This is only the first time.
String text = editText.getText().toString();
if (text.length() <= 0) {
editText.setError(errorMandatory);
} else if (text.length() > 0) {
editText.setError(null);
} else {
editText.setError(errorMelding);
}
} catch (NumberFormatException nfe) {
editText.setError(errorMeldingTijdensParsen);
}
}
};
return textwatcher;
}
you can use something like this.
Hope this help you.

If your range is short and you don't need double numbers , I recommend you to use spinner for selecting numbers.

Related

Min & Max Input values on Android Studio [duplicate]

This question already has answers here:
Is there a way to define a min and max value for EditText in Android?
(29 answers)
Closed 11 months ago.
I am creating an app that calculates the the users marks. How do I go forward, I'm trying to limit the user's input value to between min=0 and max=100?
I've got 3 inputs that I want to limit.
EditText a = (EditText) findViewById(R.id.Assignment1);
EditText b = (EditText) findViewById(R.id.Assignment2);
EditText c = (EditText) findViewById(R.id.Assignment3);
Tried using the following but only works when I execute my function.
public void YearMark(View v)
{
EditText a = (EditText) findViewById(R.id.Assignment1);
a.setFilters(new InputFilter[]{ new InputFilterMinMax("0", "100")});
As #JAY_Panchal mentioned, you can use an InputFilter:
public class RangeInputFilter implements InputFilter {
private final int min, max;
public RangeInputFilter(int min, int max) {
boolean rightOrder = min <= max;
this.min = rightOrder ? min : max;
this.max = rightOrder ? max : min;
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dStart, int dEnd) {
try {
String sourceStr = source.toString();
String destStr = dest.toString();
String result = destStr.substring(0, dStart) + sourceStr.substring(start, end) + destStr.substring(dEnd);
int input = Integer.parseInt(result);
if (min <= input && input <= max) return null;
} catch (NumberFormatException ignored) { }
return "";
}
}
When a user changes the content of an EditText, you can use an InputFilter to decide what actually changes in the EditText.
Returning null means that the input is accepted.
Returning something else (in this case "") means that source should be replaced with that (in this case, we want dest to stay as it is and thus have an empty source).
To assign your filter:
RangeInputFilter filter = new RangeInputFilter(0, 100);
InputFilter[] filters = new InputFilter[] {filter};
editText1.setFilters(filters);
editText2.setFilters(filters);
editText3.setFilters(filters);
Add this textwatcher for all three edittext a,b and c:
a.addTextChangedListener(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) {
String strTest = editText1235.getText().toString().trim();
if (!strTest.equals("")) {
float no = Float.parseFloat(strTest);
if (no > 100) {
a.setText(null);
Toast.makeText(this, "enter a value less than 100", Toast.LENGTH_SHORT).show();
}
}
}
});
Also set input type of these text fields as number so that user cannot enter a negative sign,ie, any value less than 0.
android:inputType="number"
While submitting the details also check if the field is not empty like below:
if (a.getText().toString().trim().length() <=0 ||b.getText().toString().trim().length() <=0 ||c.getText().toString().trim().length() <= 0){
Toast.makeText(this, "Value cannot be empty", Toast.LENGTH_SHORT).show();
}
I don't know your usecase, but you might also want to use a SeekBar.
A SeekBar is a slider - you can select a number by sliding. It is also very customizable.
The main advantage is that the user does not need to open the keyboard.

How to limit an integer value entered into an EditText in Android Studio/Java

I have an editText that is set so that a user can only enter an integer value. The value is then sent to an API. The problem is that the server get's overloaded if the value is too high (Heroku problems).
I was wondering if there was a way to actually limit the value that was able to be entered into the editText. For example the user can only enter a value between 1-800.
Obvioulsy I cant limit the value by number of figures, for example 3 figures, as that would allow for a value of up to 999.
There is another question open for this, however the answers in that Q dont seem to come up with one elegant solution, instead an amalgamation of different pieces of buggy code.
Here is the current MainActivity and xml for the editText.
activity_main.xml
<EditText
android:id="#+id/editText2"
android:layout_width="274dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="278dp"
android:ems="10"
android:hint="Enter number of tweets..."
android:inputType="number" />
mainActivity.Java
EditText editText2 = findViewById(R.id.editText2);
String tweetNo = editText2.getText().toString();
With an onclicklistener for the button that draws the data from the editText as well.
You just need to have this condition to check whether the value entered is between 1 to 800.
EditText editText2 = findViewById(R.id.editText2);
String tweetNo = editText2.getText().toString();
int result = Integer.parseInt(tweetNo); // convert String to int
if (result >= 1 && result <= 800){
// do whatever you want
}
You can use an InputFilter to limit the value.
editText2.setFilters(new InputFilter[]{new InputFilter() {
#Override
public CharSequence filter(CharSequence charSequence, int i, int i1, Spanned spanned, int i2, int i3) {
try{
int val=Integer.parseInt(spanned.toString()+charSequence.toString());
if(MIN<=val&&val<=MAX)
return null;
}catch (NumberFormatException e){
}
return "";
}
}});
You can set a custom InputFilter for your EditText. InputFilter defines what value is allowed inside the EditText, in your case we need to define a filter that accepts only values within the range of 0 to 800. Define the filter class like this:
public class CustomRangeInputFilter implements InputFilter {
private double minValue;
private double maxValue;
public CustomRangeInputFilter(double minVal, double maxVal) {
this.minValue = minVal;
this.maxValue = maxVal;
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dStart, int dEnd) {
try {
// Remove the string out of destination that is to be replaced
String newVal = dest.toString().substring(0, dStart) + dest.toString().substring(dEnd, dest.toString().length());
newVal = newVal.substring(0, dStart) + source.toString() + newVal.substring(dStart, newVal.length());
double input = Double.parseDouble(newVal);
if (isInRange(minValue, maxValue, input)) {
return null;
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
return "";
}
private boolean isInRange(double a, double b, double c) {
return b > a ? c >= a && c <= b : c >= b && c <= a;
}
}
Then you set the filter on your EditText on the onCreate method:
editText.setFilters(new InputFilter[]{new CustomRangeInputFilter(0f, 800.0f)});

Remove span when text inside the span is changed in Android

Let´s say I make a comment like this:
Hi Andrea check this out..... In that comment I want to highlight Andrea, but whenever I change the value of Andrea or when I delete one character of the word the span changes, the problem is that I´m using spannableString.setSpan(new StyleSpan(Typeface.BOLD), indexOfAt, highlightName.length() + indexOfAt, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE), which Spanned.SPAN_EXCLUSIVE_EXCLUSIVE accepts words in the middle and deletion of words, how can I remove the span when the user changes the word or delete a character of the word?
You need to watch for Text change on the edit text.
Assuming the EditText, you are using is named as commentEditText
commentEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String comment = editable.toString();
if (comment.indexOf('#') != -1) {
//Asumming the name string
String name = "Andrea";
int atIndex = comment.indexOf('#');
int endIndex = atIndex + name.length() + 1;
if (endIndex == -1 || endIndex > editable.length()) {
endIndex = editable.length();
}
if (comment.toLowerCase().contains(name.toLowerCase())) {
editable.setSpan(new StyleSpan(Typeface.BOLD), atIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
StyleSpan[] spannable = editable.getSpans(atIndex, endIndex, StyleSpan.class);
if (spannable != null && spannable.length > 0) {
for (int i = 0; i < spannable.length; i++) {
editable.removeSpan(spannable[i]);
}
}
}
}
}
});
If you are using the SpannableString you have to recreate the whole thing on every change.
You can remove spans but you cannot change the source (the comment) because SpannableString source text is immutable.
I suggest on every change of the comment you create the SpannableString, look for names and tag them, then if the comment changes you repeat the same thing with a new SpannableString. There will be no performance problems because the comments are small in size.
If you want to have a mutable object you can use the SpannableStringBuilder but it's more complicated and there is no need for it.
You can try TextWatcher for this, simply add TextWatcher to the textview you want to achieve above and in on
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//Do some magic here
}
Now whenever user types or deletes any character you will get a callback in this method upon receiving that you can reset your span.

Android - How to only allow a certain number of decimal places

Do you know of any method to make sure users can only enter figures with a maximum number of decimals.
I'm not sure how to address this problem. In the MS SQL database I'm going to send data from my app I've got columns with this type decimal(8,3)
Now considering the data type of the column that's finally going to store the value I want to validate in Android, I've considered these two cases:
If the user enters a number with no decimals, the maximum number of digits must be 8
If the user enters a number with decimals, the maximum number of digits must be 8 (including the digits to the right of the decimal point)
Now I'm sure about the first case, but not so much about the second. Is it right to keep the number of maximum digits fixed(for example, always 8)? Or should I consider allowing a maximum of 8 digits to the left and 3 to the right of the decimal point?
Either way this is what I've been trying in Android:
mQuantityEditText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
String str = mQuantityEditText.getText().toString();
DecimalFormat format = (DecimalFormat) DecimalFormat
.getInstance();
DecimalFormatSymbols symbols = format.getDecimalFormatSymbols();
char sep = symbols.getDecimalSeparator();
int indexOFdec = str.indexOf(sep);
if (indexOFdec >= 0) {
if (str.substring(indexOFdec, str.length() - 1).length() > 3) {
s.replace(0, s.length(),
str.substring(0, str.length() - 1));
}
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
Even though, the above code handles the maximum number of decimal places. It does not limit the total number of digits allowed in the EditText.
Do you think you could help me improve my code so that it handles both the maximum number of decimal places and the total number of digits allowed in a EditText (considering both numbers to the left and to the right of the decimal point)
EDIT
Well, now I'm trying what João Sousa suggested and here's what I've tried:
1) I defined a class that implements InputFilter
public class NumberInputFilter implements InputFilter {
private Pattern mPattern;
public NumberInputFilter(int precision, int scale) {
String pattern="^\\-?(\\d{0," + (precision-scale) + "}|\\d{0," + (precision-scale) + "}\\.\\d{0," + scale + "})$";
this.mPattern=Pattern.compile(pattern);
}
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned destination, int destinationStart, int destinationEnd) {
if (end > start) {
// adding: filter
// build the resulting text
String destinationString = destination.toString();
String resultingTxt = destinationString.substring(0, destinationStart) + source.subSequence(start, end) + destinationString.substring(destinationEnd);
// return null to accept the input or empty to reject it
return resultingTxt.matches(this.mPattern.toString()) ? null : "";
}
// removing: always accept
return null;
}
}
2) Tried to use the class like this :
mQuantityEditText.setFilters(new InputFilter[] { new NumberInputFilter(8,3)} );
I would go for a filter in the edit text itself with the power of regex. First the regex expression:
^\-?(\d{0,5}|\d{0,5}\.\d{0,3})$
Maybe there are multiple ways to improve this expression, but this does trick.
And now just set an input filter in the edittext, like this:
final String regex = "^\-?(\d{0,5}|\d{0,5}\.\d{0,3})$";
((EditText)rootView.findViewById(R.id.editText1)).setFilters(new InputFilter[] {
new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned destination, int destinationStart, int destinationEnd) {
if (end > start) {
// adding: filter
// build the resulting text
String destinationString = destination.toString();
String resultingTxt = destinationString.substring(0, destinationStart) + source.subSequence(start, end) + destinationString.substring(destinationEnd);
// return null to accept the input or empty to reject it
return resultingTxt.matches(regex) ? null : "";
}
// removing: always accept
return null;
}
}
});
Btw, I just tested this code and what it does is:
The user can enter a maximum of 8 digits;
As soon as the user enters a '.', the maximum decimal digits allowed are 8.
Did I correctly understand the problem you described?
-- EDIT
Ok, I was almost there. From what I understand, decimal(8,3) means at most 8 digits including digits to the left or right of the decimal point, ranging from -99999.999 to 99999.999.
At least that's what I understand from this sentence Standard SQL requires that DECIMAL(5,2) be able to store any value with five digits and two decimals, so values that can be stored in the salary column range from -999.99 to 999.99. Even though it's from the MySQL documentation the MSSQL docs seem to do the same.
I have answser for you, me also suffered lot in this kind of situation.:D :P
I have implemented this for maximum of 4 digits to the left and 2 to the right of the decimal point ex: 4444.99
so small changes need to implement what i did:
Need to do following changes
1) copy CustomTextWatcher.java to track input of editText.
import java.text.NumberFormat;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;
public class CustomTextWatcher implements TextWatcher {
private NumberFormat nf = NumberFormat.getNumberInstance();
private EditText et;
private String tmp = "";
private int moveCaretTo;
private static final int INTEGER_CONSTRAINT = 4;
private static final int FRACTION_CONSTRAINT = 2;
private static final int MAX_LENGTH = INTEGER_CONSTRAINT
+ FRACTION_CONSTRAINT + 1;
public CustomTextWatcher(EditText et) {
this.et = et;
nf.setMaximumIntegerDigits(INTEGER_CONSTRAINT);
nf.setMaximumFractionDigits(FRACTION_CONSTRAINT);
nf.setGroupingUsed(false);
}
public int countOccurrences(String str, char c) {
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) == c) {
count++;
}
}
return count;
}
#Override
public void afterTextChanged(Editable s) {
et.removeTextChangedListener(this); // remove to prevent stackoverflow
String ss = s.toString();
int len = ss.length();
int dots = countOccurrences(ss, '.');
boolean shouldParse = dots <= 1
&& (dots == 0 ? len != (INTEGER_CONSTRAINT + 1)
: len < (MAX_LENGTH + 1));
if (shouldParse) {
if (len > 1 && ss.lastIndexOf(".") != len - 1) {
try {
if (ss.contains(".")) {
String[] integerFractionStrings = ss.split("\\.");
Log.v("Check SS ", ss);
Log.v("second string", "Found"
+ integerFractionStrings.length);
if (integerFractionStrings.length > 1) {
Log.v("integerFractionStrings",
integerFractionStrings[1]);
if (integerFractionStrings[1].length() == 1
&& integerFractionStrings[1].charAt(0) == '0') {
et.setText(ss);
Log.v("second string", "size 1");
} else {
Log.v("second string", "> 1");
Double d = Double.parseDouble(ss);
if (d != null) {
et.setText(nf.format(d));
}
}
}
} else {
Log.v("First string", "No dot");
Double d = Double.parseDouble(ss);
if (d != null) {
et.setText(nf.format(d));
}
}
} catch (NumberFormatException e) {
}
}
} else {
Log.v("second string", "size 1");
et.setText(tmp);
}
et.addTextChangedListener(this); // reset listener
// tried to fix caret positioning after key type:
if (et.getText().toString().length() > 0) {
if (dots == 0 && len >= INTEGER_CONSTRAINT
&& moveCaretTo > INTEGER_CONSTRAINT) {
moveCaretTo = INTEGER_CONSTRAINT;
} else if (dots > 0 && len >= (MAX_LENGTH)
&& moveCaretTo > (MAX_LENGTH)) {
moveCaretTo = MAX_LENGTH;
}
try {
et.setSelection(et.getText().toString().length());
// et.setSelection(moveCaretTo); <- almost had it :))
} catch (Exception e) {
}
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
moveCaretTo = et.getSelectionEnd();
tmp = s.toString();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int length = et.getText().toString().length();
if (length > 0) {
moveCaretTo = start + count - before;
}
}
}
2) set this class to check your editText by following.
EditText review_food_Price;
review_food_Price = (EditText) findViewById(R.id.food_Price);
review_food_Price.setRawInputType(InputType.TYPE_CLASS_NUMBER
| InputType.TYPE_NUMBER_FLAG_DECIMAL);
review_food_Price.addTextChangedListener(new CustomTextWatcher(
review_food_Price));
Hope you can convert my code according to your need.
The problem that you describe is precisely what a Masked EditText is meant to be used for. :)

Calling Zip Code -- Android

I have both the phone number and address (including the zipcode) in the same TextView. I want the phone number to be clickable to call; however, this is making the zip code also clickable to make a phone call. How do I make it no longer clickable without creating another TextView? Thanks!
tvInfo.setText(Html.fromHtml("John Smith<br>123 Fake Street<br>Faketown, FK 12345<br><b>(804) 932-3300</b><br>"));
Linkify.addLinks(tvInfo, Linkify.PHONE_NUMBERS);
android will regard digits with count >=5 as phone numbers. so I think there will be at least 2 solutions:
1)a simple work around : if you are sure the length of phone numbers is more than 5, for example, at least 6 digits, you could make some work around:
private final static int MY_PHONE_NUMBER_MINIMUM_DIGITS = 6;
Linkify.addLinks(main, Patterns.PHONE, "tel:", new Linkify.MatchFilter() {
public final boolean acceptMatch(CharSequence s, int start, int end) {
int digitCount = 0;
for (int i = start; i < end; i++) {
if (Character.isDigit(s.charAt(i))) {
digitCount++;
if (digitCount >= MY_PHONE_NUMBER_MINIMUM_DIGITS ) {
return true;
}
}
}
return false;
}
}, Linkify.sPhoneNumberTransformFilter);
this workaround is based on android source code of Linkify, in Linkify, the method:
gatherLinks(links, text, Patterns.PHONE,
new String[] { "tel:" },
sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);
will be called, while sPhoneNumberMatchFilter will filter the digits less than 5:
public static final MatchFilter sPhoneNumberMatchFilter = new MatchFilter() {
public final boolean acceptMatch(CharSequence s, int start, int end) {
int digitCount = 0;
for (int i = start; i < end; i++) {
if (Character.isDigit(s.charAt(i))) {
digitCount++;
if (digitCount >= PHONE_NUMBER_MINIMUM_DIGITS/*=5*/) {
return true;
}
}
}
return false;
}
};
so we just replace the "PHONE_NUMBER_MINIMUM_DIGITS" with 6
2)a more complicated solution is, if your phone numbers are in a more specific format for example, must be something like "(xxx)xxx-xxxx", you could use your own pattern to replace the Patterns.PHONE, to extract and apply links of phone numbers more accurately

Categories