165

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.

Jonathan Soifer
  • 2,715
  • 6
  • 27
  • 50
mertaydin
  • 2,184
  • 5
  • 19
  • 26
  • 1
    Someone should make a library / collection of all these types of input filters. Then everyone can work and test them together. Rather than each person doing their own thing. – Zapnologica Jun 20 '16 at 19:56
  • Use this edit text filter to solve your problem. [filter](https://stackoverflow.com/a/45695392/6667580) – Taras Smakula Aug 15 '17 at 15:05

29 Answers29

311

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.

Pratik Sharma
  • 13,307
  • 5
  • 27
  • 37
  • 1
    @mertaydin sure. give it a try then let me know if you need my help. Thanks. – Pratik Sharma Jan 08 '13 at 10:22
  • 13
    Aah, I need help. I have a problem while writig value between 1930 and 1999. When i write 1 it's controlling the value and 1 isn't between 1930 and 1999 so it doesn't accept it. – mertaydin Jan 08 '13 at 13:15
  • @mertaydin sorry but I am running out of time so will check that tomorrow for sure. Hope you don't mind with that. – Pratik Sharma Jan 08 '13 at 13:34
  • Hi Pratik Sharma, I'm here again. – mertaydin Jan 09 '13 at 07:34
  • 1
    @mertaydin Hey sorry but I lit a bit busy with my work. I just got some time to look into this, I think I need to do modifications in the algorithm applied in the filter class. I will update you once I will done with that. – Pratik Sharma Jan 09 '13 at 15:45
  • 1
    Some characters from `source` will replace some characters in `dest`. I think you should simulate the replacement, and obtain end result, then, validate it. – S.D. May 31 '13 at 11:17
  • @pratik/@mertaydin is there a way to disable numbers on the keyboard? Like if i put the limit 0-23; and once the user enters 2; it disables all 4,5,6,..9 (as they would not fall in range) ? Like in Timely? – Zen Apr 16 '14 at 09:28
  • 4
    @Pratik Sharma , This does not work when i enter range from 1900 to 2000, Can you suggest something – Amarjit Jun 12 '15 at 09:05
  • i want range -100 +100 but edittext doesnot accept (-) – Device Jun 20 '15 at 16:20
  • then range should be a build in feature of edit text – fangzhzh Nov 16 '15 at 08:40
  • 1
    One Hack, if user enters 0 or any other number and move cursor ahead of that and then enter any value then it is allowing to enter any number not in range – GOLDEE Mar 15 '16 at 06:20
  • Usable but not cautious, you should consider copy and paste number in it. – Yugy Jul 08 '16 at 09:11
  • How to use this for decimal Min & Max, I've tried but android prevent user to enter value like 0.001 or entering .001 . – SDG69 Jan 11 '17 at 19:34
  • @PratikSharma does this solution work with edittext with input type "numberDecimal"?? – KJEjava48 Mar 21 '17 at 14:17
  • @PratikSharma If i modify the int parameter of this class to double will it work for decimal numbers?? – KJEjava48 Mar 21 '17 at 14:19
  • @PratikSharma How can i set a maximum value for edittext with decimal values??Please help me... – KJEjava48 Mar 22 '17 at 12:02
  • 1
    doesn't work if user type in the middle of the field =\ – Vasilii Suricov Sep 24 '17 at 14:28
  • 1
    it is the best solution, but still, it is not working, if the range start other than 1. say for eg. min. 20 max 40. Please provide solution for that. – Anish Kumar Jan 31 '18 at 12:56
  • @PratikSharma code has a nice idea but with a lot of bugs. Zac's and Anthony B ( negative numbers solutions) have solve some of them, but Zac’s code still have 3 mayor bugs. Solutions at: https://stackoverflow.com/questions/14212518/is-there-a-way-to-define-a-min-and-max-value-for-edittext-in-android/60824434#60824434 – nnyerges Mar 24 '20 at 03:16
  • This answer don't work as intended. It is ransom that it works for anybody. If you have "91" and insert at the beginning the check will be for "911" instead of "191". This answer should be deleted out of my view. – rekire Nov 26 '20 at 12:56
  • This doesn't allow decimal if you set input-type = numberDecimal. – Prajwal Waingankar Mar 15 '21 at 11:05
101

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 "";
}

UPDATE

Kotlin version

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

/**
 * Custom InputFilter that restricts the input to a range of minimum and maximum values, both
 * inclusive. The edit text must have android:inputType="number".
 */
class MinMaxEditTextInputFilter(private val mMin: Int, private val mMax: Int) : InputFilter {

  override fun filter(
    source: CharSequence,
    start: Int,
    end: Int,
    dest: Spanned,
    dstart: Int,
    dend: Int,
  ): CharSequence {
    try {
      val newValueString: String = dest.subSequence(0, dstart).toString() +
        source.subSequence(start, end).toString() +
        dest.subSequence(dend, dest.length)
      val newValueInt = newValueString.toInt()
      if (isInRange(mMin, mMax, newValueInt)) return source
    } catch (e: NumberFormatException) {
      e.printStackTrace()
    }
    return ""
  }

  private fun isInRange(min: Int, max: Int, value: Int): Boolean {
    return if (max > min) {
      value in min..max
    } else {
      value in max..min
    }
  }
}
Damia Fuentes
  • 5,308
  • 6
  • 33
  • 65
Zac
  • 2,325
  • 3
  • 23
  • 33
  • 5
    I think this is clearer _imho_: `String replacement = source.subSequence(start, end).toString(); String newVal = dest.subSequence(0, dstart).toString() + replacement + dest.subSequence(dend, dest.length()).toString();` – Sven Jacobs Apr 30 '14 at 06:38
  • 2
    +1. this solution more correct than accepted. To check it e.g. try to use **InputFilterMinMax** with **selectAllOnFocus** option enabled and compare result. – Sash0k Jun 02 '14 at 09:24
  • 2
    Why not `String newVal= dest.toString().substring(0, dstart) + source.toString().substring(start, end) + dest.toString().substring(dend, dest.toString().length());`, looks more clean and clear. – Shishir Gupta Dec 08 '14 at 13:09
  • 7
    As mentioned by OP @mertaydin in a comment on @Patrick 's answer, this solution still has a major flaw, i wonder why it hasn't been reported: If `min==3` then it's impossible to type any number starting with 1 or 2 (ex: 15, 23) – Guerneen4 Feb 25 '15 at 10:36
  • For Decimal :- use double input = Double.parseDouble(newVal); – iamnaran Jul 17 '18 at 11:14
  • @Guerneen4 Patrik’s code has a nice idea but with a lot of bugs. Zac's and Anthony B ( negative numbers solutions) have solve some of them, but Zac’s code still have 3 mayor bugs. Solution at: https://stackoverflow.com/questions/14212518/is-there-a-way-to-define-a-min-and-max-value-for-edittext-in-android/60824434#60824434 – nnyerges Mar 24 '20 at 03:18
16

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"
Alish
  • 225
  • 2
  • 12
  • 1
    I using return source and work for me ==> if (isInRange(min, max, input)) return source – Muzafferus Feb 14 '20 at 11:45
  • To use with Double where you do not need to restrict to specific decimal value you can just edit the `input` variable to be `val input = kotlin.math.floor((dest.toString() + source.toString()).toDouble()).toInt()` – InvisibleGorilla Jun 30 '22 at 20:15
14

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')});
Guerneen4
  • 708
  • 8
  • 17
  • OnFocusChangeListenerMin is not working , it put all values from zero – Amarjit Jun 12 '15 at 11:45
  • 1
    Would you mind detailing a bit more ? what do you mean _put all values from zero_ ? – Guerneen4 Jun 12 '15 at 12:35
  • from code OnFocusChangeListenerMin must be working above 20 as discussed in problem bu it accept all values less than 20 like 0 , 1, 2 , ------ 19 etc – Amarjit Jun 13 '15 at 05:52
  • did you find any solution? – Amarjit Jun 16 '15 at 07:38
  • I've used OnFoncusChangeListener in a purpose of validation, in my case i display an error on EditText notify the user by a Toast that the value is not good and invite him to enter a new value `if(Integer.valueOf(val) – Guerneen4 Jun 17 '15 at 10:02
9

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 "";
}
Anthony B
  • 91
  • 1
  • 2
5

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;
}
AndroidNoob
  • 2,771
  • 2
  • 40
  • 53
5

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;
}
}
Denys Vasylenko
  • 2,135
  • 18
  • 20
5

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.

Nash Makineta
  • 79
  • 1
  • 4
4

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);
谭春茂
  • 41
  • 2
4

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.

Martin Braun
  • 10,906
  • 9
  • 64
  • 105
4

In Kotlin:

Transcribe InputFilterMinMax to a Kotlin, it takes two float values as constructor parameters min and max, which are used to define a range of valid values for user input.

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
    }
}
Codelaby
  • 2,604
  • 1
  • 25
  • 25
3

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.

Argyris
  • 31
  • 3
3

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");
                    }......
Fakhar
  • 3,946
  • 39
  • 35
3

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.

VerumCH
  • 2,995
  • 2
  • 15
  • 22
3

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) {
        ""
    }
}
Andrey
  • 675
  • 7
  • 22
3

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)});
ʀᴀʜɪʟ
  • 235
  • 1
  • 4
  • 8
2
@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;
}
jet27
  • 21
  • 1
2

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

1. If user deletes all entries in the EditText, it’s 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, it’s impossible to type any number starting with 1.

3. If for example min = 0, you can type has many zeros you wish, that it’s 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 @Zac’s 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!

nnyerges
  • 563
  • 4
  • 15
  • This is wrong. If you type something and delete it, it won't allow typing more. Also, automatically adding the miniumum is a terrible interface. What if the minimum is 100 and I am trying to input 1500? As soon as I type 1 it is going to autocomplete to 100, then I have to delete the trailing zeros, etc. etc. – Aykhan Hagverdili Aug 04 '21 at 11:58
1

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/

itsrajesh4uguys
  • 4,610
  • 3
  • 20
  • 31
  • OP wants to validate it when the value is being entered. I think you have provided solution for the scenario after value is entered – MysticMagicϡ Jan 08 '13 at 10:40
  • I am not the questioner. I had just doubt regarding your answer, so I was clarifying it. – MysticMagicϡ Jan 08 '13 at 10:48
  • I don't want to check character count. I think you are trying to check the count in this code : if(TextUtils.isEmpty(pass) || pass.length < [YOUR MIN LENGTH]) I just limit for value for example for month value the user must write the value between 1-12 not 13,14, or etc. So, 12,13,14, until 99 is 2 character length. – mertaydin Jan 08 '13 at 10:55
  • hi @mertaydin did you tried with the link i have provided you.. see that example.. may be thats what you want. it will watch the text while you are entering.. – itsrajesh4uguys Jan 08 '13 at 11:09
  • Ok thanks, I will try your answer and @Pratik Sharma's answer. – mertaydin Jan 08 '13 at 11:25
1

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) {

        }
    });
Hoàng
  • 11
  • 1
1

@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"
gadolf
  • 1,035
  • 11
  • 19
0

//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 ...     
             }
         }
      }
 });
CHTsai
  • 1
  • 1
  • 1
0

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.

sanjeeb
  • 87
  • 1
  • 12
0

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)});
maloromano
  • 51
  • 1
  • 5
0

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

Christopher
  • 300
  • 2
  • 9
0

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
}
0

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);
        }
    }
}
LuckMan
  • 85
  • 1
  • 5
0

This can now be achieved using XML with android:maxLength property:

android:maxLength="your text length"
oriohac
  • 187
  • 1
  • 8
-1

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
}
}
Varun Barve
  • 323
  • 2
  • 8
  • 18
  • This input filter is wrong, it ignores the start, end, dstart, dend indices of the text, so if you change the edit text by inserting a character in the middle it will not work correctly – Radu Nov 22 '19 at 09:45