简体   繁体   中英

IP address textbox in Android?

I'm totally new to Android.

I would like to put in a textbox where user can enter an IP address ... but how do I limit the user to only enter numbers? ... and how do I validate?

Is there a ready-made-ip-address-textbox "out there" I can use?

Thanks!

Mojo

What I've found that works is to set an EditText to use android:inputType="phone" , so the input is restricted to digits, period, and a handful of other characters. However, this will only let you input IPV4 addresses, since it's digits only. For validation, you'll have to get the input text and parse it by hand.

As far as ready-made input widgets, I haven't come across one.

In addition to what Erich said, you can use android:digits="0123456789." to disallow anything but digits and a decimal point.

You can use :

EditText ipAddress = (EditText)findViewById(R.id.ip_address);
InputFilter[] filters = new InputFilter[1];
    filters[0] = new InputFilter() {
        @Override
        public CharSequence filter(CharSequence source, int start, int end,
                android.text.Spanned dest, int dstart, int dend) {
            if (end > start) {
                String destTxt = dest.toString();
                String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
                if (!resultingTxt.matches ("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) { 
                    return "";
                } else {
                    String[] splits = resultingTxt.split("\\.");
                    for (int i=0; i<splits.length; i++) {
                        if (Integer.valueOf(splits[i]) > 255) {
                            return "";
                        }
                    }
                }
            }
            return null;
        }

    };
    ipAddress.setFilters(filters);

For validation, regular-expressions.info has a good regex string you could use for testing for an IP in a valid (0-255) range:

\\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b

This is, I think, the most complete of the existing solutions to this (at least that I've found). The only improvement I can imagine is to implement a new KeyListener to restrain the input better but I'm not convinced it is practically possible, given how IMEs work with layouts and stuff.

public class IPAddressText extends EditText {

    public IPAddressText(Context context) {
        super(context);

        setInputType(InputType.TYPE_CLASS_PHONE);
        setFilters(new InputFilter[] { new InputFilter(){
            @Override
            public CharSequence filter(CharSequence source, int start, int end, android.text.Spanned dest, int dstart, int dend) {
                if (end > start) {
                    String destTxt = dest.toString();
                    String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
                    if (!resultingTxt.matches("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) {
                        return "";
                    }
                    else {
                        String[] splits = resultingTxt.split("\\.");
                        for (int i = 0; i < splits.length; i++) {
                            if (Integer.valueOf(splits[i]) > 255) {
                                return "";
                            }
                        }
                    }
                }
                return null;
            }
        }
        });

        addTextChangedListener(new TextWatcher(){
            boolean deleting = false;
            int lastCount = 0;

            @Override
                public void afterTextChanged(Editable s) {
                    if (!deleting) {
                        String working = s.toString();
                        String[] split = working.split("\\.");
                        String string = split[split.length - 1];
                        if (string.length() == 3 || string.equalsIgnoreCase("0")
                            || (string.length() == 2 && Character.getNumericValue(string.charAt(0)) > 1)) {
                            s.append('.');
                            return;
                        }
                    }
                }

            @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (lastCount < count) {
                        deleting = false;
                    }
                    else {
                        deleting = true;
                    }
                }

            @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    // Nothing happens here
                }
        });
    }
}

And because it is what I actually ended up using, here is an EditTextPreference version:

public class IPAddressPreference extends EditTextPreference {

    public IPAddressPreference(Context context) {
        super(context);

        getEditText().setInputType(InputType.TYPE_CLASS_PHONE);
        getEditText().setFilters(new InputFilter[] { new InputFilter(){
            @Override
            public CharSequence filter(CharSequence source, int start, int end, android.text.Spanned dest, int dstart, int dend) {
                if (end > start) {
                    String destTxt = dest.toString();
                    String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
                    if (!resultingTxt.matches("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) {
                        return "";
                    }
                    else {
                        String[] splits = resultingTxt.split("\\.");
                        for (int i = 0; i < splits.length; i++) {
                            if (Integer.valueOf(splits[i]) > 255) {
                                return "";
                            }
                        }
                    }
                }
                return null;
            }
        }
        });

        getEditText().addTextChangedListener(new TextWatcher(){
            boolean deleting = false;
            int lastCount = 0;

            @Override
                public void afterTextChanged(Editable s) {
                    if (!deleting) {
                        String working = s.toString();
                        String[] split = working.split("\\.");
                        String string = split[split.length - 1];
                        if (string.length() == 3 || string.equalsIgnoreCase("0")
                            || (string.length() == 2 && Character.getNumericValue(string.charAt(0)) > 1)) {
                            s.append('.');
                            return;
                        }
                    }
                }

            @Override
                public void onTextChanged(CharSequence s, int start, int before, int count) {
                    if (lastCount < count) {
                        deleting = false;
                    }
                    else {
                        deleting = true;
                    }
                }

            @Override
                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    // Nothing happens here
                }
        });
    }
}

I'm using this TextWatcher:

public class IPTextWatcher implements TextWatcher
{
    private static String LOG_TAG = "IPTextWatcher";

    private EditText editText;
    private BarcodeTextWatcher.ChangeListener listener = null;

    public IPTextWatcher( EditText editText )
    {
        this.editText = editText;
    }

    private static final String IPADDRESS_PATTERN =
            "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
                    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
                    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." +
                    "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";

    @Override
    public void afterTextChanged( Editable s )
    {
        Log.v( LOG_TAG, s.toString() );

        if( !s.toString().matches( IPADDRESS_PATTERN ) )
        {
            String ip = format( s.toString() );

            editText.removeTextChangedListener( this );
            editText.setText( ip );

            editText.setTextKeepState( ip );
            Selection.setSelection( editText.getText(), ip.length() );

            editText.addTextChangedListener( this );

            if( listener != null )
                listener.onChange();
        }
    }

    public static String format( String value )
    {
        String userInput = "" + value.replaceAll( "[^\\d\\.]", "" );
        StringBuilder ipBuilder = new StringBuilder();

        String[] address = userInput.split("\\.");

        String glue = null;
        for( String part : address )
        {
            if( glue != null ) ipBuilder.append( glue );

            int p = Integer.valueOf( part );

            if( p >= 256 )
            {
                int i = 1;

                do
                {
                    p = Integer.valueOf( part.substring( 0, part.length() -i ) );
                    i++;
                }
                while( p >= 256 );
            }

            ipBuilder.append( p );

            glue = ".";
        }

        if( userInput.charAt( userInput.length()-1 ) == '.' )
            ipBuilder.append( "." );

        return ipBuilder.toString();
    }

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

    @Override
    public void beforeTextChanged( CharSequence s, int start, int count, int after )
    {
    }
}
  <EditText
        android:id="@+id/ip_address"
        android:inputType="number|numberDecimal"
        android:digits="0123456789."
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

worked for me

Based on NathanOliver's and enneract's code I wrote, in kotlin, a variation that insertes a dot when one is deleted.

class IpAddressEditText : AppCompatEditText {

    constructor(context: Context) : super(context) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init()
    }

    private fun init() {
        // phone input type to show the numeric keyboard in all kinds of devices
        inputType = InputType.TYPE_CLASS_PHONE

        // restrict the input of the characters specified in string bellow
        keyListener = DigitsKeyListener.getInstance("0123456789.")

        // InputFilter decides what can be typed
        filters = arrayOf(InputFilter { source, start, end, dest, dstart, dend ->
            if (end > start) {
                val inputString = dest.toString()
                val substring = inputString.substring(0, dstart) + source.subSequence(start, end) + inputString.substring(dend)

                if (!substring.matches("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?".toRegex())) {
                    // do not allow the input of:
                    //  segments with more than 3 characters and less than 1;
                    //  segments != 4;
                    return@InputFilter ""

                } else {
                    val splits = substring.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                    for (i in splits.indices) {
                        // don't allow a segment with a value more than 255
                        if (Integer.valueOf(splits[i]) > 255) {
                            return@InputFilter ""
                        }
                    }
                }
            }
            null
        })

        // TextWatcher notifies what was typed
        addTextChangedListener(object : TextWatcher {
            var isDeleting = false
            var lastCount = 0

            override fun afterTextChanged(editable: Editable) {
                if (!isDeleting) {
                    val inputString = editable.toString()
                    val segmentList = inputString.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
                    val lastSegment = segmentList[segmentList.size - 1]

                    // each segment of the ip can have max size of 3 characters and a max value of 255
                    if (lastSegment.length == 3 || lastSegment.length == 2 && Integer.parseInt(lastSegment) > 25) {

                        // add a dot automatically if the conditions met
                        editable.append('.')
                        return
                    }
                } else {
                    // add a dot in the same position where it was deleted
                    editable.insert(selectionStart, ".")
                }
            }

            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
                isDeleting = lastCount >= count
            }

            override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
                // do nothing
            }
        })
    }
}

Here is the code that limits a user to only enter numbers and dots by displaying a soft keyboard with only numbers and a dot (but allows you to enter multiple dot s).

 etIpAddress.setInputType(InputType.TYPE_CLASS_NUMBER);
 etIpAddress.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);
 etIpAddress.setKeyListener(DigitsKeyListener.getInstance(false,false));
 etIpAddress.setKeyListener(DigitsKeyListener.getInstance("0123456789."));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM