I want to define a min and max value for an EditText.

For example: if any person tries to enter a month value in it, the value must be between 1-12.

I can do it by using TextWatcher but I want to know if there is any other way to do it in layout file or elsewhere.

Edit: I don't want to limit character count. I want to limit the value. For example, if I limit month EditText w characters when I enter 12 it will accept it but if I enter 22 it mustn't accept it while I am entering.

Solution 1

First make this class :

package com.test;

import android.text.InputFilter;
import android.text.Spanned;

public class InputFilterMinMax implements InputFilter {

    private int min, max;

    public InputFilterMinMax(int min, int max) {
        this.min = min;
        this.max = max;
    }

    public InputFilterMinMax(String min, String max) {
        this.min = Integer.parseInt(min);
        this.max = Integer.parseInt(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) { }     
        return "";
    }

    private boolean isInRange(int a, int b, int c) {
        return b > a ? c >= a && c <= b : c >= b && c <= a;
    }
}

Then use this from your Activity :

EditText et = (EditText) findViewById(R.id.myEditText);
et.setFilters(new InputFilter[]{ new InputFilterMinMax("1", "12")});

This will allow user to enter values from 1 to 12 only.

EDIT :

Set your edittext with android:inputType="number".

You can find more details at https://www.techcompose.com/how-to-set-minimum-and-maximum-value-in-edittext-in-android-app-development/.

Thanks.

Solution 2

There is a small error in Pratik's code. For instance, if a value is 10 and you add a 1 at the beginning to make 110, the filter function would treat the new value as 101.

See below for a fix to this:

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    try {
        // Removes string that is to be replaced from destination
        // and adds the new string in.
        String newVal = dest.subSequence(0, dstart)
                // Note that below "toString()" is the only required:
                + source.subSequence(start, end).toString()
                + dest.subSequence(dend, dest.length());
        int input = Integer.parseInt(newVal);
        if (isInRange(min, max, input))
            return null;
    } catch (NumberFormatException nfe) { }
    return "";
}

Solution 3

Kotlin if any one needs it (Use Utilities)

class InputFilterMinMax: InputFilter {
    private var min:Int = 0
    private var max:Int = 0
    constructor(min:Int, max:Int) {
        this.min = min
        this.max = max
    }
    constructor(min:String, max:String) {
        this.min = Integer.parseInt(min)
        this.max = Integer.parseInt(max)
    }
    override fun filter(source:CharSequence, start:Int, end:Int, dest: Spanned, dstart:Int, dend:Int): CharSequence? {
        try
        {
            val input = Integer.parseInt(dest.toString() + source.toString())
            if (isInRange(min, max, input))
                return null
        }
        catch (nfe:NumberFormatException) {}
        return ""
    }
    private fun isInRange(a:Int, b:Int, c:Int):Boolean {
        return if (b > a) c in a..b else c in b..a
    }
}

Then use this from your Kotlin class

percentage_edit_text.filters = arrayOf(Utilities.InputFilterMinMax(1, 100))

This EditText allows from 1 to 100.

Then use this from your XML

android:inputType="number"

Solution 4

Of what i've seen of @Patrik's solution and @Zac's addition, the code provided still has a big problem :

If min==3 then it's impossible to type any number starting with 1 or 2 (ex: 15, 23)
If min>=10 then it's impossible to type anything as every number will have to start with 1,2,3...

In my understanding we cannot achieve the min-max limitation of an EditText's value with simple use of the class InputFilterMinMax, at least not for the min Value, because when user is typing a positive number, the value goes growing and we can easily perform an on-the-fly test to check if it's reached the limit or went outside the range and block entries that do not comply. Testing the min value is a different story as we cannot be sure if the user has finished typing or not and therefore cannot decide if we should block or not.

It's not exactly what OP requested but for validation purposes i've combined in my solution an InputFilter to test max values, with an OnFocusChangeListener to re-test for min value when the EditText loses the focus assuming the user's finished typing and it's something like this :

package test;


import android.text.InputFilter;

import android.text.Spanned;

public class InputFilterMax implements InputFilter {

private int max;

public InputFilterMax(int max) {
    this.max = max;
}

public InputFilterMax(String max) {
    this.max = Integer.parseInt(max);
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {   
    try {
        String replacement = source.subSequence(start, end).toString(); 

        String newVal = dest.toString().substring(0, dstart) + replacement +dest.toString().substring(dend, dest.toString().length());

        int input = Integer.parseInt(newVal);

        if (input<=max)
            return null;
    } catch (NumberFormatException nfe) { }   
//Maybe notify user that the value is not good      
return "";
}
}

And OnFocusChangeListenerMin

package test;

import android.text.TextUtils;
import android.view.View;
import android.view.View.OnFocusChangeListener;

public class OnFocusChangeListenerMin implements OnFocusChangeListener {

private int min;

public OnFocusChangeListenerMin(int min) {
    this.min = min;
}

public OnFocusChangeListenerMin(String min) {
    this.min = Integer.parseInt(min);
}


@Override
public void onFocusChange(View v, boolean hasFocus) {
    if(!hasFocus) {
        String val = ((EditText)v).getText().toString();
        if(!TextUtils.isEmpty(val)){
            if(Integer.valueOf(val)<min){
                //Notify user that the value is not good
            }

        }
    }
}
}

Then in Activity set the InputFilterMax and theOnFocusChangeListenerMin to EditText note : You can 2 both min and max in onFocusChangeListener.

mQteEditText.setOnFocusChangeListener( new OnFocusChangeListenerMin('20');
mQteEditText.setFilters(new InputFilter[]{new InputFilterMax(getActivity(),'50')});

Solution 5

Extension of Pratik's and Zac's answer. Zac fixed a small bug of Pratik's in his answer. But I notcied that code doesn't support negative values, it will throw a NumberFormatException. To fix that, and allow the MIN to be negative, use the following code.

Add this line (In bold) between the other two lines:

newVal = newVal.substring(0, dstart) + source.toString()+ newVal.substring(dstart, newVal.length());

if(newVal.equalsIgnoreCase("-") && min < 0)return null;

int input = Integer.parseInt(newVal);

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());
        // Add the new string in
        newVal = newVal.substring(0, dstart) + source.toString() + newVal.substring(dstart, newVal.length());
        //****Add this line (below) to allow Negative values***// 
        if(newVal.equalsIgnoreCase("-") && min < 0)return null;
        int input = Integer.parseInt(newVal);
        if (isInRange(min, max, input))
            return null;
    } catch (NumberFormatException nfe) {
        nfe.printStackTrace();
    }
    return "";
}

Solution 6

If you need range with negative numbers like -90:90, you can use this solution.

public class InputFilterMinMax implements InputFilter {

private int min, max;

public InputFilterMinMax(int min, int max) {
    this.min = min;
    this.max = max;
}

public InputFilterMinMax(String min, String max) {
    this.min = Integer.parseInt(min);
    this.max = Integer.parseInt(max);
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    try {
        String stringInput = dest.toString() + source.toString();
        int value;
        if (stringInput.length() == 1 && stringInput.charAt(0) == '-') {
            value = -1;
        } else {
            value = Integer.parseInt(stringInput);
        }
        if (isInRange(min, max, value))
            return null;
    } catch (NumberFormatException nfe) {
    }
    return "";
}

private boolean isInRange(int min, int max, int value) {
    return max > min ? value >= min && value <= max : value >= max && value <= min;
}
}

Solution 7

I extended @Pratik Sharmas code to use BigDecimal objects instead of ints so that it can accept larger numbers, and account for any formatting in the EditText that isn't a number (like currency formatting i.e. spaces, commas and periods)

EDIT: note that this implementation has 2 as the minimum significant figures set on the BigDecimal (see the MIN_SIG_FIG constant) as I used it for currency, so there was always 2 leading numbers before the decimal point. Alter the MIN_SIG_FIG constant as necessary for your own implementation.

public class InputFilterMinMax implements InputFilter {
private static final int MIN_SIG_FIG = 2;
private BigDecimal min, max;

public InputFilterMinMax(BigDecimal min, BigDecimal max) {
    this.min = min;
    this.max = max;
}

public InputFilterMinMax(String min, String max) {
    this.min = new BigDecimal(min);
    this.max = new BigDecimal(max);
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart,
        int dend) {
    try {
        BigDecimal input = formatStringToBigDecimal(dest.toString()
                + source.toString());

        if (isInRange(min, max, input)) {

            return null;
        }
    } catch (NumberFormatException nfe) {

    }
    return "";
}

private boolean isInRange(BigDecimal a, BigDecimal b, BigDecimal c) {
    return b.compareTo(a) > 0 ? c.compareTo(a) >= 0 && c.compareTo(b) <= 0
            : c.compareTo(b) >= 0 && c.compareTo(a) <= 0;
}

public static BigDecimal formatStringToBigDecimal(String n) {

    Number number = null;
    try {
        number = getDefaultNumberFormat().parse(n.replaceAll("[^\\d]", ""));

        BigDecimal parsed = new BigDecimal(number.doubleValue()).divide(new BigDecimal(100), 2,
                BigDecimal.ROUND_UNNECESSARY);
        return parsed;
    } catch (ParseException e) {
        return new BigDecimal(0);
    }
}

private static NumberFormat getDefaultNumberFormat() {
    NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
    nf.setMinimumFractionDigits(MIN_SIG_FIG);
    return nf;
}

Solution 8

I found my own answer. It is very late now but I want to share it with you. I implement this interface:

import android.text.TextWatcher;


public abstract class MinMaxTextWatcher implements TextWatcher {
    int min, max;
    public MinMaxTextWatcher(int min, int max) {
        super();
        this.min = min;
        this.max = max;
    }

}

And then implement it in this way inside your activity:

private void limitEditText(final EditText ed, int min, int max) {
    ed.addTextChangedListener(new MinMaxTextWatcher(min, max) {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {

        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }

        @Override
        public void afterTextChanged(Editable s) {
            String str = s.toString();
            int n = 0;
            try {
                n = Integer.parseInt(str);
                if(n < min) {
                    ed.setText(min);
                    Toast.makeText(getApplicationContext(), "Minimum allowed is " + min, Toast.LENGTH_SHORT).show();
                }
                else if(n > max) {
                    ed.setText("" + max);
                    Toast.makeText(getApplicationContext(), "Maximum allowed is " + max, Toast.LENGTH_SHORT).show();
                }
            }
            catch(NumberFormatException nfe) {
                ed.setText("" + min);
                Toast.makeText(getApplicationContext(), "Bad format for number!" + max, Toast.LENGTH_SHORT).show();
            }
        }
    });
}

This is a very simple answer, if any better please tell me.

Solution 9

There is something wrong in the accepted answer.

int input = Integer.parseInt(dest.toString() + source.toString());

If I move cursor into middle of text, then type something, then the above statement will produce wrong result. For example, type "12" first, then type "0" between 1 and 2, then the statement mentioned above will produce "120" instead of 102. I modified this statement to statements below:

String destString = dest.toString();
  String inputString = destString.substring(0, dstart) + source.toString() + destString.substring(dstart);
  int input = Integer.parseInt(inputString);

Solution 10

I made a simpler way to set a min/max to an Edittext. I use arithmetic keypad and I work with this method:

 private int limit(EditText x,int z,int limin,int limax){

    if( x.getText().toString()==null || x.getText().toString().length()==0){
        x.setText(Integer.toString(limin));
        return z=0;
    }
    else{
        z = Integer.parseInt(x.getText().toString());
         if(z <limin || z>limax){
             if(z<10){
                 x.setText(Integer.toString(limin));
                return  z=0;
             }
            else{
                x.setText(Integer.toString(limax));
                return z=limax;
            }

         }
         else
            return z = Integer.parseInt(x.getText().toString());
    }
 }

The method accepts all of your values but if a value of users in not adhere to your limits it will be set automatically to the min/max limit. For ex. limit limin=10, limax =80 if the user sets 8, automatically 10 is saved to a variable and EditText is set to 10.

Solution 11

If you are only concerned about max limit then just add below line in

android:maxLength="10" 

If you need to add min limit then you can do like this way in this case min limit is 7. user is restricted to enter character between min and max limit (in between 8 and 10)

public final static boolean isValidCellPhone(String number){
        if (number.length() < 8 || number.length() >10 ) {
            return false;
        } else {

           return android.util.Patterns.PHONE.matcher(number).matches();
        }
    }

If you also need to restrict user to enter 01 at start then modify if condition like this way

if (!(number.startsWith("01")) || number.length() < 8 || number.length() >10 ) {  
.
.
.
}

At the end call method like

   ....else if (!(Helper.isValidMobilePhone(textMobileNo))){
                        Helper.setEditTextError(etMobileNo,"Invalid Mobile Number");
                    }......

Solution 12

I know there are a million answers to this already, with one accepted. However, there are numerous bugs in the accepted answer and most of the rest simply fix one (or maybe two) of them, without expanding to all possible use cases.

So I basically compiled most of the bug fixes suggested in support answers as well as adding a method to allow continuous input of numbers outside of the range in the direction of 0 (if the range doesn't start at 0), at least until it's certain that it can no longer be in the range. Because to be clear, this is the only time that really causes trouble with many of the other solutions.

Here's the fix:

public class InputFilterIntRange implements InputFilter, View.OnFocusChangeListener {

    private final int min, max;

    public InputFilterIntRange(int min, int max) {
        if (min > max) {
            // Input sanitation for the filter itself
            int mid = max;
            max = min;
            min = mid;
        }
        this.min = min;
        this.max = max;
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        // Determine the final string that will result from the attempted input
        String destString = dest.toString();
        String inputString = destString.substring(0, dstart) + source.toString() + destString.substring(dstart);

        // Don't prevent - sign from being entered first if min is negative
        if (inputString.equalsIgnoreCase("-") && min < 0) return null;

        try {
            int input = Integer.parseInt(inputString);
            if (mightBeInRange(input))
                return null;
        } catch (NumberFormatException nfe) {}

        return "";
    }

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

        // Since we can't actively filter all values
        // (ex: range 25 -> 350, input "15" - could be working on typing "150"),
        // lock values to range after text loses focus
        if (!hasFocus) {
            if (v instanceof EditText) sanitizeValues((EditText) v);
        }
    }

    private boolean mightBeInRange(int value) {
        // Quick "fail"
        if (value >= 0 && value > max) return false;
        if (value >= 0 && value >= min) return true;
        if (value < 0 && value < min) return false;
        if (value < 0 && value <= max) return true;

        boolean negativeInput = value < 0;

        // If min and max have the same number of digits, we can actively filter
        if (numberOfDigits(min) == numberOfDigits(max)) {
            if (!negativeInput) {
                if (numberOfDigits(value) >= numberOfDigits(min) && value < min) return false;
            } else {
                if (numberOfDigits(value) >= numberOfDigits(max) && value > max) return false;
            }
        }

        return true;
    }

    private int numberOfDigits(int n) {
        return String.valueOf(n).replace("-", "").length();
    }

    private void sanitizeValues(EditText valueText) {
        try {
            int value = Integer.parseInt(valueText.getText().toString());
            // If value is outside the range, bring it up/down to the endpoint
            if (value < min) {
                value = min;
                valueText.setText(String.valueOf(value));
            } else if (value > max) {
                value = max;
                valueText.setText(String.valueOf(value));
            }
        } catch (NumberFormatException nfe) {
            valueText.setText("");
        }
    }

}

Note that some input cases are impossible to handle "actively" (i.e., as the user is inputting it), so we must ignore them and handle them after the user is done editing the text.

Here's how you might use it:

EditText myEditText = findViewById(R.id.my_edit_text);
InputFilterIntRange rangeFilter = new InputFilterIntRange(25, 350);
myEditText.setFilters(new InputFilter[]{rangeFilter});

// Following line is only necessary if your range is like [25, 350] or [-350, -25].
// If your range has 0 as an endpoint or allows some negative AND positive numbers, 
// all cases will be handled pre-emptively.
myEditText.setOnFocusChangeListener(rangeFilter);

Now, when the user tries to type in a number closer to 0 than the range allows, one of two things will happen:

  1. If min and max have the same number of digits, they won't be allowed to input it at all once they get to the final digit.

  2. If a number outside of the range is left in the field when the text loses focus, it will automatically be adjusted to the closest boundary.

And of course, the user will never be allowed to input a value farther from 0 than the range allows, nor is it possible for a number like that to "accidentally" be in the text field for this reason.

Known Issue(s?)

  1. This only works if the EditText loses focus when the user is done with it.

The other option is sanitizing when the user hits the "done"/return key, but in many or even most cases, this causes a loss of focus anyways.

However, closing the soft keyboard will not automatically un-focus the element. I'm sure 99.99% of Android developers wish it would (and that focus handling on EditText elements was less of a quagmire in general), but as of yet there is no built-in functionality for it. The easiest method that I've found to get around this, if you need to, is to extend EditText something like this:

public class EditTextCloseEvent extends AppCompatEditText {

    public EditTextCloseEvent(Context context) {
        super(context);
    }

    public EditTextCloseEvent(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EditTextCloseEvent(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            for (InputFilter filter : this.getFilters()) {
                if (filter instanceof InputFilterIntRange)
                    ((InputFilterIntRange) filter).onFocusChange(this, false);
            }
        }
        return super.dispatchKeyEvent(event);
    }
}

This will "trick" the filter into sanitizing the input even though the view hasn't actually lost focus. If the view happens to later lose focus on its own, the input sanitation will trigger again, but nothing will change since it was already fixed.

Closing

Whew. That was a lot. What originally seemed like it would be a pretty trivially easy problem ended up uncovering many little ugly pieces of vanilla Android (at least in Java). And once again, you only need to add the listener and extend EditText if your range does not include 0 in some way. (And realistically, if your range doesn't include 0 but starts at 1 or -1, you also won't run into problems.)

As a last note, this only works for ints. There is certainly a way to implement it to work with decimals (double, float), but since neither I nor the original asker have a need for that, I don't particularly want to get all that deep into it. It would be very easy to simply use the post-completion filtering along with the following lines:

// Quick "fail"
if (value >= 0 && value > max) return false;
if (value >= 0 && value >= min) return true;
if (value < 0 && value < min) return false;
if (value < 0 && value <= max) return true;

You would only have to change from int to float (or double), allow insertion of a single . (or ,, depending upon country?), and parse as one of the decimal types instead of an int.

That handles most of the work anyways, so it would work very similarly.

Solution 13

Very simple example on Kotlin:

import android.text.InputFilter
import android.text.Spanned

class InputFilterRange(private var range: IntRange) : InputFilter {

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int) = try {
        val input = Integer.parseInt(dest.toString() + source.toString())
        if (range.contains(input)) null else ""
    } catch (nfe: NumberFormatException) {
        ""
    }
}

Solution 14

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    try {
        String prefix = dest.toString().substring(0, dstart);
        String insert = source.toString();
        String suffix = dest.toString().substring(dend);
        String input_string = prefix + insert + suffix;
        int input = Integer.parseInt(input_string);

        if (isInRange(min, max, input) || input_string.length() < String.valueOf(min).length())
            return null;
    } catch (NumberFormatException nfe) { }
    return "";
}

private boolean isInRange(int a, int b, int c) {
    return b > a ? c >= a && c <= b : c >= b && c <= a;
}

Solution 15

@Patriks code has a nice idea but with a lot of bugs. @Zac and @Anthony B ( negative numbers solutions) have solve some of them, but @Zacs code still have 3 mayor bugs:

1. If user deletes all entries in the EditText, its impossible to type any number again.. Of course this can be controlled using a EditText changed listener on each field, but it will erase out the beauty of using a common InputFilter class for each EditText in your app.

2. Has @Guernee4 says, if for example min = 3, its impossible to type any number starting with 1.

3. If for example min = 0, you can type has many zeros you wish, that its not elegant the result. Or also, if no matter what is the min value, user can place the cursor in the left size of the first number, an place a bunch of leading zeros to the left, also not elegant.

I came up whit these little changes of @Zacs code to solve this 3 bugs. Regarding bug # 3, I still haven't been able to completely remove all leading zeros at the left; It always can be one, but a 00, 01, 0100 etc in that case, is more elegant an valid that an 000000, 001, 000100, etc.etc. Etc.

Here is the code:

@Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        try {

            // Using @Zac's initial solution
            String lastVal = dest.toString().substring(0, dstart) + dest.toString().substring(dend);
            String newVal = lastVal.substring(0, dstart) + source.toString() + lastVal.substring(dstart);
            int input = Integer.parseInt(newVal);

            // To avoid deleting all numbers and avoid @Guerneen4's case
            if (input < min && lastVal.equals("")) return String.valueOf(min);

            // Normal min, max check
            if (isInRange(min, max, input)) {

                // To avoid more than two leading zeros to the left
                String lastDest = dest.toString();
                String checkStr = lastDest.replaceFirst("^0+(?!$)", "");
                if (checkStr.length() < lastDest.length()) return "";

                return null;
            }
        } catch (NumberFormatException ignored) {}
        return "";
    }

Have a nice day!

Solution 16

Many solutions here are great, but I want to provide a quicker alternative that might be enough if you are just looking for a way to prevent overflows:

android:maxLength="9"

This will make sure that you cannot get an overflow on 32 bit integers for non decimals. So here the min value is -99999999 and max value is 999999999.

Solution 17

In Kotlin:

class InputFilterMinMax(private val min: Float, private val max: Float) : InputFilter {

    override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
        try {
            val input = (dest.subSequence(0, dstart).toString() + source + dest.subSequence(dend, dest.length)).toFloat()
            if (isInRange(min, max, input)) return null
        } catch (e: NumberFormatException) {
            e.printStackTrace()
        }
        return ""
    }

    private fun isInRange(a: Float, b: Float, c: Float): Boolean {
        return if (b > a) c in a..b else c in b..a
    }
}

Solution 18

please check this code

    String pass = EditText.getText().toString(); 
    if(TextUtils.isEmpty(pass) || pass.length < [YOUR MIN LENGTH]) 
    { 
       EditText.setError("You must have x characters in your txt"); 
        return; 
    }

    //continue processing



edittext.setOnFocusChangeListener( new OnFocusChangeListener() {

       @Override
       public void onFocusChange(View v, boolean hasFocus) {
          if(hasFocus) {
           // USE your code here 
  }

USe the below link for more details about edittext and the edittextfilteres with text watcher..

http://www.mobisoftinfotech.com/blog/android/android-edittext-setfilters-example-numeric-text-field-patterns-and-length-restriction/

Solution 19

this is my code max=100, min=0

xml

<TextView
                    android:id="@+id/txt_Mass_smallWork"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:textColor="#000"
                    android:textSize="20sp"
                    android:textStyle="bold" />

java

EditText ed = findViewById(R.id.txt_Mass_smallWork);
    ed.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) {
            if(!charSequence.equals("")) {
                int massValue = Integer.parseInt(charSequence.toString());
                if (massValue > 10) {
                    ed.setFilters(new InputFilter[]{new InputFilter.LengthFilter(2)});
                } else {
                    ed.setFilters(new InputFilter[]{new InputFilter.LengthFilter(3)});
                }
            }
        }

        @Override
        public void afterTextChanged(Editable editable) {

        }
    });

Solution 20

@Pratik Sharma

For support negative numbers, add the following code within the filter method:

package ir.aboy.electronicarsenal;

import android.text.InputFilter;
import android.text.Spanned;

public class InputFilterMinMax implements InputFilter {

  private int min, max;
  int input;

  InputFilterMinMax(int min, int max) {
    this.min = min;
    this.max = max;
  }

  public InputFilterMinMax(String min, String max) {
    this.min = Integer.parseInt(min);
    this.max = Integer.parseInt(max);
  }

  @Override
  public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    try {

      if ((dest.toString() + source.toString()).equals("-")) {
        source = "-1";
      }

      input = Integer.parseInt(dest.toString() + source.toString());
      if (isInRange(min, max, input))
        return null;

    } catch (NumberFormatException ignored) {
    }
    return "";
  }

  private boolean isInRange(int a, int b, int c) {
    return b > a ? c >= a && c <= b : c >= b && c <= a;
  }

}

Then use this from your Activity :

findViewById(R.id.myEditText).setFilters(new InputFilter[]{ new InputFilterMinMax(1, 12)});

Set your edittext with:

android:inputType="number|numberSigned"

Solution 21

First create a method as below

private InputFilter inRange(int min, int max) {
    return new InputFilter(){
        @Override
        public CharSequence filter(CharSequence source, int start1, int end, Spanned dest, int dstart, int dend) {
            try {
                int input = Integer.parseInt(dest.toString() + source.toString());
                if (input < min || input > max){
                    return "";
                } else {
                    return null;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    };
}

Use it like this:

edittext1.setFilters(new InputFilter[]{inRange(3,60)});
edittext2.setFilters(new InputFilter[]{inRange(1,100)});

You can make more filers & add like this :

edittext1.setFilters(new InputFilter[]{filter1(somevalue),filter2(somevalue)});

Solution 22

//still has some problem but Here you can use min, max at any range (positive or negative)

// in filter calss
 @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
            int input;
            String newVal = dest.toString() + source.toString();
            if (newVal.length() == 1 && newVal.charAt(0) == '-') {
                input = min; //allow
            }
            else {
                newVal = dest.toString().substring(0, dstart) + dest.toString().substring(dend, dest.toString().length());
                // Add the new string in
                newVal = newVal.substring(0, dstart) + source.toString() + newVal.substring(dstart, newVal.length());
                input = Integer.parseInt(newVal);
            }

            //int input = Integer.parseInt(dest.toString() + source.toString());

            if (isInRange(min, max, input))
                return null;
        } catch (NumberFormatException nfe) {
        }
        return "";
    }

//also the filler must set as below: in the edit createview
// to allow enter number and backspace.
et.setFilters(new InputFilter[]{new InputFilterMinMax(min >= 10 ?  "0" : String.valueOf(min), max >-10 ? String.valueOf(max) :"0" )});



//and at same time must check range in the TextWatcher()
et.addTextChangedListener(new
 TextWatcher() {

      @Override
      public void afterTextChanged (Editable editable)
      {
         String tmpstr = et.getText().toString();
         if (!tmpstr.isEmpty() && !tmpstr.equals("-") ) {
             int datavalue = Integer.parseInt(tmpstr);
             if ( datavalue >= min || datavalue <= max) {
               // accept data ...     
             }
         }
      }
 });

Solution 23

To add to Pratik's answer, here is a modified version where user can enter min 2 digits also , for example, 15 to 100:

 import android.text.InputFilter;
 import android.text.Spanned;

public class InputFilterMinMax implements InputFilter {

    private int min, max;

    public InputFilterMinMax(int min, int max) {
        this.min = min;
        this.max = max;
    }

    public InputFilterMinMax(String min, String max) {
        this.min = Integer.parseInt(min);
        this.max = Integer.parseInt(max);
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {

        try {
            if(end==1)
                min=Integer.parseInt(source.toString());
            int input = Integer.parseInt(dest.toString() + source.toString());
            if (isInRange(min, max, input))
                return null;
        } catch (NumberFormatException nfe) {
        }
        return "";
    }

    private boolean isInRange(int a, int b, int c) {

        return b > a ? c >= a && c <= b : c >= b && c <= a;
    }}

added: if(end==1) min=Integer.parseInt(source.toString());

Hope this helps. kindly dont downvote without reasons.

Solution 24

here's the way I've used, it's working for negative number

First, create MinMaxFIlter.java class with the following code :

import android.text.InputFilter;
import android.text.Spanned;
import android.util.Log;

/**
* Created by 21 on 4/5/2016.
*/
public class MinMaxFilter implements InputFilter {

private double mIntMin, mIntMax;

public MinMaxFilter(double minValue, double maxValue) {
    this.mIntMin = minValue;
    this.mIntMax = maxValue;
}

public MinMaxFilter(String minValue, String maxValue) {
    this.mIntMin = Double.parseDouble(minValue);
    this.mIntMax = Double.parseDouble(maxValue);
}

@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
    try {
        Boolean isNeg = false;
        String provi = dest.toString() + source.toString();
        if("-".equals(provi.substring(0,1))){
            if(provi.length()>1) {
                provi = provi.substring(1, provi.length());
                isNeg = true;
            }
            else{
                if("".equals(source)){
                    return null;
                }
                return "-";
            }
        }

        double input = Double.parseDouble(provi); 
        if(isNeg){input = input * (-1);}

        if (isInRange(mIntMin, mIntMax, input)) {
            return null;
        }
    } catch (Exception nfe) {}
    return "";
}

private boolean isInRange(double a, double b, double c) {
    if((c>=a && c<=b)){
        return true;
    }
    else{
        return false;
    }
}
}

Then, create and set your filter to your edittext like this :

EditText edittext = new EditText(context);
editext.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
eInt.setFilters(new InputFilter[]{new MinMaxFilter(min, max)});

Solution 25

You can do this with an an InputFilter. Apparently ther is just this Input Filter Interface you can use. Before you do it the annoying way an create a new Class that extends Input filter, u can use this shortcut with a innerclass Interface instantiation.

Therefore you just do this:

EditText subTargetTime = (EditText) findViewById(R.id.my_time);
subTargetTime.setFilters( new InputFilter[] {
                new InputFilter() {
                    @Override
                    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
                        int t = Integer.parseInt(source.toString());
                        if(t <8) { t = 8; }
                        return t+"";

                    }
                }
        });

In this example I check if the value of the EditText is greater than 8. If not it shall be set to 8. So apaprently you need to com up with the min max or whatever filter logic by yourself. But at least u can write the filter logic pretty neat and short directly into the EditText.

Hope this helps

Solution 26

To define the minimum value of the EditText, I used this:

if (message.trim().length() >= 1 && message.trim().length() <= 12) {
  // do stuf
} else {
  // Too short or too long
}

Solution 27

I stumbled upon this problem when I was making a pet project. I've read some of the answers here and I've probably adopted on or two of them in my code.

BAD NEWS: I managed to do this by using a very dirty way (you'll see why). There are still some bugs that I haven't bothered to address (I was writing this at like 2 am), like if the min value is 10, you won't be able to input a number to begin with.

GOOD NEWS: I've managed to get rid of the leading zeros bug mentioned by @nnyerges using solely the InputFilter down to only one 0, that is if the min value is 0. However, the limit of my implementation of the InputFilter comes when user deletes the first number(s) that's followed by zero(s), e.g. if at first user inputs 1000 but then deletes 1, it will become 000. That's ugly, and that's where my dirty ugly use of TextChangedListener / TextWatcher comes in. (I know OP already said that he can do it using TextWatcher, but whatever.)
Another limitation (or maybe MY limitation?) using the InputFilter is when the inputType is numberDecimal, which means user can input a decimal separator. Example case: range is 0 - 100, user inputs 99.99, user then deletes the separator, we'd have 9999. We don't want that, do we?
I've also made it to accommodate negative value.

Some features in my code, whether you like it or not, include trimming insignificant 0s, e.g. if user deletes 1 from 10032, as long as it's within the defined range, it will trim the leading 0s, so the final result will be 32. Second, when user tries to delete the negative (-) notation or the decimal separator (.), it will check whether the resulting number after deletion is still in range. If not, then it will revert back to last value. In other words, user isn't allowed to do that kind of removal. But, if you prefer to set the new values to either min or max values when that happens, you can do it, too.

NOTE: I'm too lazy to even bother with localization, so people who use comma as decimal separator will have to manually change it themselves.
SECOND NOTE: The code is very messy and probably have some or a lot of redundant checks, so be aware. Also, if you have suggestion, feel free to comment, as I want to improve it, too. I may need to use it in the future. Who knows?

Anyway, here it goes.

import android.text.InputFilter;
import android.text.Spanned;
import android.util.Log;

public class InputFilterMinMax implements InputFilter {
    
    private double min, max;
    
    public InputFilterMinMax(double min, double max) {
        this.min = min;
        this.max = max;
    }
    
    public InputFilterMinMax(String min, String max) {
        this.min = Double.parseDouble(min);
        this.max = Double.parseDouble(max);
    }
    
    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        try {
            String lastVal = dest.toString();
            String newVal = lastVal.substring(0, dstart) + source.toString() + lastVal.substring(dstart);
            String strInput = source.toString();
            double input;
    
            if (strInput.equals("-") && (lastVal.length() == 0 || lastVal.equals("0"))) {
                return null;
            } else {
                input = Double.parseDouble(newVal);
            }
            
            if (isInRange(min, max, input)) {
                try {
                    if (lastVal.equals("0") && strInput.equals("0") && !strInput.equals(".")) {
                        Log.d("Checkpoint 1", "Can't put 0 again.");
                        return "";
                    } else if (strInput.equals("0")) {
                        if (dstart == 0) {
                            if (lastVal.substring(0, 1).equals("0")) {
                                Log.d("Checkpoint 2", "Can't put 0 again.");
                                return "";
                            } else if (!lastVal.substring(0, 1).equals(".")) {
                                Log.d("Checkpoint 3", "Can't put 0 in front of them.");
                                return "";
                            }
                        } else {
                            if (lastVal.substring(0, 1).equals("0") && dstart == 1) {
                                Log.d("Checkpoint 4", "Can't put 0 again.");
                                return "";
                            } else if (lastVal.substring(0, 1).equals("-")) {
                                if (Double.parseDouble(lastVal) == 0) {
                                    if (!lastVal.contains(".")) {
                                        Log.d("Checkpoint 5", "Can't put 0 here.");
                                        return "";
                                    } else {
                                        if (dstart <= lastVal.indexOf(".")) {
                                            Log.d("Checkpoint 6", "Can't put 0 here.");
                                            return "";
                                        }
                                    }
                                } else {
                                    if (lastVal.indexOf("0") == 1 && (dstart == 1 || dstart == 2)) {
                                        Log.d("Checkpoint 7", "Can't put 0 here.");
                                        return "";
                                    } else if ((!lastVal.substring(1, 2).equals("0") && !lastVal.substring(1, 2).equals(".")) && dstart == 1) {
                                        Log.d("Checkpoint 8", "Can't put 0 here.");
                                        return "";
                                    }
                                }
                            }
                        }
                    }
    
                    /**
                     * If last value is a negative that equals min value,
                     * and user tries to input a decimal separator at the
                     * very end, ignore it, because they won't be able to
                     * input anything except 0 after that anyway.
                     */
                    if (strInput.equals(".") && lastVal.substring(0,1).equals("-")
                            && Double.parseDouble(lastVal) == min && dstart == lastVal.length()) {
                        return "";
                    }
                } catch (Exception e) {
                }
                return null;
            }
        } catch (Exception ignored) {
            ignored.printStackTrace();
        }
        return "";
    }
    
    private boolean isInRange(double a, double b, double c) {
        return b > a ? c >= a && c <= b : c >= b && c <= a;
    }
}

Now, the really dirty part:

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity implements TextWatcher {
    
    private EditText editInput;
    
    /**
     * Var to store old value in case the new value is either
     * out of range or invalid somehow. This was because I
     * needed a double value for my app, which means I can
     * enter a dot (.), and that could mean trouble if I decided
     * to delete that dot, e.g. assume the range is 0 - 100.
     * At first I enter 99.99, the InputFilter would allow that,
     * but what if somewhere down the line I decided to delete
     * the dot/decimal separator for "fun"?
     * Wow, now I have 9999.
     * Also, when I delete negative notation, it can produce
     * the same problem.
     */
    private String oldVal;
    
    private int min, max;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        editInput = findViewById(R.id.edt_input);
        editInput.addTextChangedListener(this);
        
        min = -1600;
        max = 1500;
        
        editInput.setFilters(new InputFilter[]{new InputFilterMinMax(min, max)});
    }
    
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        oldVal = saveOldValue(s, start);
    }
    
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    
    }
    
    @Override
    public void afterTextChanged(Editable s) {
            validateChange(editInput, oldVal);
    }
    
    private String saveOldValue(CharSequence s, int start) {
        String oldVal = s.toString();
        if (oldVal.contains(".") && start == oldVal.indexOf(".") && start != oldVal.length() - 1) {
            return oldVal;
        } else if (oldVal.contains("-") && start == oldVal.indexOf("-") && start != oldVal.length() - 1) {
            return oldVal;
        }
        return null;
    }
    
    private void validateChange(EditText editText, String oldVal) {
        String strNewVal = editText.getText().toString().trim();
        boolean isChanged = false;

        if (strNewVal.indexOf("0") == 0 || (strNewVal.indexOf("-") == 0 && strNewVal.indexOf("0") == 1)) {
            if (strNewVal.contains(".")) {
                while ((strNewVal.indexOf("0") == 0 && strNewVal.indexOf(".") != 1 && strNewVal.length() > 2) ||
                        (strNewVal.indexOf("0") == 1 && strNewVal.indexOf(".") != 2 && strNewVal.length() > 3)) {
                    Log.d("Trimming 0", "");
                    strNewVal = strNewVal.replaceFirst("0", "");
                    isChanged = true;
                }
            } else if (!strNewVal.contains(".")) {
                while (strNewVal.indexOf("0") == 0 && strNewVal.length() > 1) {
                    Log.d("Trimming 0", "");
                    strNewVal = strNewVal.replaceFirst("0", "");
                    isChanged = true;
                }

                if (Double.parseDouble(strNewVal) > max) {
                    editText.setText(oldVal); // Or, you can set it to max values here.
                    return;
                }
            }
        }
        
        if (strNewVal.indexOf(".") == 0) {
            strNewVal = "0" + strNewVal;
            isChanged = true;
        }

        try {
            double newVal = Double.parseDouble(strNewVal);
            Log.d("NewVal: ", String.valueOf(newVal));
            if (newVal > max || newVal < min) {
                Log.d("Over Limit", "Let's Reset");
                editText.setText(oldVal); // Or, you can set it to min or max values here.
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        if (isChanged) {
            editText.setText(strNewVal);
        }
    }
}

Solution 28

Here's my take on Pratik Sharma's answer for Kotlin and Double if any one needs it

class InputFilterMinMax : InputFilter {

private var min: Double = MIN_LIMIT
private var max: Double = MIN_LIMIT

constructor(min: Int, max: Int) {
    this.min = min.toDouble()
    this.max = max.toDouble()
}

constructor(min: String, max: String) {
    this.min = min.toDouble()
    this.max = max.toDouble()
}

constructor(min: Double, max: Double) {
    this.min = min
    this.max = max
}

override fun filter(
    source: CharSequence,
    start: Int,
    end: Int,
    dest: Spanned,
    dstart: Int,
    dend: Int
): CharSequence? {
    try {
        val input = (dest.toString() + source.toString()).toDouble()
        if (isInRange(min, max, input))
            return null
    } catch (nfe: NumberFormatException) {
        Timber.e(nfe)
    }

    return ""
}

private fun isInRange(a: Double, b: Double, c: Double): Boolean {
    return if (b > a) c in a..b else c in b..a
}
}