Giter Site home page Giter Site logo

input-mask-android's Introduction

Input Mask

Android Arsenal Awesome Actions iOS Version Telegram license

Input masks restrict data input and allow you to guide users to enter correct values.
Check out our wiki for quick start and further reading.

⚙️ Features

  • Apply formatting to your text fields, see examples
  • Filter out nonessential symbols (e.g. extract 0123456 from +1 (999) 012-34-56)
  • For international phone numbers
    • guess the country from the entered digits
    • apply corresponding value restrictions (e.g. a 🇺🇸US phone will have a format like +1 201 456-7890)
  • Apply number/currency formatting
  • Phone numbers: +1 ([000]) [000] [00] [00]
  • Dates: [00]{.}[00]{.}[9900]
  • Serial numbers: [AA]-[00000099]
  • IPv4: [099]{.}[099]{.}[099]{.}[099]
  • Visa/MasterCard numbers: [0000] [0000] [0000] [0000]
  • UK IBAN: GB[00] [____] [0000] [0000] [0000] [00]

Gradle

Make sure you've added Kotlin support to your project.

repositories {
    maven { url 'https://jitpack.io' }
}

dependencies {
    implementation 'com.redmadrobot:input-mask-android:7.2.4'
    
    implementation 'org.jetbrains.kotlin:kotlin-stdlib:$latest_version'
}

📢 Communication, Questions & Issues

Please take a closer look at our Known issues section before you incorporate our library into your project.

For your bugreports and feature requests please file new issues via GitHub.

Should you have any questions, please search for closed issues or ask questions at StackOverflow with the input-mask tag.

InputMask vs. NoClassDefFoundError

java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;

Receiving this error might mean you haven't configured Kotlin for your Java only project. Consider explicitly adding the following to the list of your project dependencies:

implementation 'org.jetbrains.kotlin:kotlin-stdlib:$latest_version'

— where latest_version is the current version of kotlin-stdlib.

InputMask vs. android:inputType and IndexOutOfBoundsException

Be careful when specifying field's android:inputType. The library uses native Editable variable received on afterTextChange event in order to replace text efficiently. Because of that, field's inputType is actually considered when the library is trying to mutate the text.

For instance, having a field with android:inputType="numeric", you cannot put spaces and dashes into the mentioned Editable variable by default. Doing so will cause an out of range exception when the MaskedTextChangedListener will try to reposition the cursor.

Still, you may use a workaround by putting the android:digits value beside your android:inputType; there, you should specify all the acceptable symbols:

<EditText
    android:inputType="number"
    android:digits="0123456789 -."
    ... />

— such that, you'll have the SDK satisfied.

Alternatively, if you are using a programmatic approach without XML files, you may consider configuring a KeyListener like this:

editText.setInputType(InputType.TYPE_CLASS_NUMBER);
editText.setKeyListener(DigitsKeyListener.getInstance("0123456789 -.")); // modify character set for your case, e.g. add "+()"

InputMask vs. autocorrection & prediction

(presumably fixed by PR50)

Symptoms:

  • You've got a wildcard template like [________], allowing user to write any kind of symbols;
  • Cursor jumps to the beginning of the line or to some random position while user input.

In this case text autocorrection & prediction might be a root cause of your problem, as it behaves somewhat weirdly in case when field listener tries to change the text during user input.

If so, consider disabling text suggestions by using corresponding input type:

<EditText
    ...
    android:inputType="textNoSuggestions" />

Additionally be aware that some of the third-party keyboards ignore textNoSuggestions setting; the recommendation is to use an extra workaround by setting the inputType to textVisiblePassword.

InputMask vs. android:textAllCaps

Kudos to Weiyi Li for reporting this issue

Please be advised that android:textAllCaps is not meant to work with EditText instances:

This setting will be ignored if this field is editable or selectable.

Enabling this setting on editable and/or selectable fields leads to weird and unpredictable behaviour and sometimes even crashes. Instead, consider using android:inputType="textCapCharacters" or workaround by adding an InputFilter:

final InputFilter[] filters = { new InputFilter.AllCaps() };
editText.setFilters(filters);

Bare in mind, you might have to befriend this solution with your existing android:digits property in case your text field accepts both digits and letters.

🙏 Special thanks

These folks rock:

♻️ License

The library is distributed under the MIT LICENSE.

input-mask-android's People

Contributors

antonkazakov avatar fi5t avatar johnjohndoe avatar osipxd avatar sepppenner avatar shipaaaa avatar taflanidi avatar vkotovv avatar xanderblinov avatar yatsinar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

input-mask-android's Issues

Format Money

Hello, does this feature have a cash/price format? reverse the digits when typing

Do I have to implement a listener to get the value without mask?

I read this "[00]-[00] and [00]{-}[00] will format the input to the same form of 12-34, but in the first case the value, extracted by the library, will be equal to 1234, and in the second case it will result in 12-34""

So, I'm doing something like this:

    final MaterialEditText test1 = (MaterialEditText) view.getRootView().findViewById(R.id.test1);
    final MaskedTextChangedListener test1Listener = new MaskedTextChangedListener(
            "[00]-[00]",true, test1, null, null );
    test1.addTextChangedListener(test1Listener);
    test1.setOnFocusChangeListener(test1Listener);
    test1.setHelperText(test1Listener.placeholder());

    final MaterialEditText test2 = (MaterialEditText) view.getRootView().findViewById(R.id.test2);
    final MaskedTextChangedListener test2Listener = new MaskedTextChangedListener(
            "[00]{-}[00]",true, test2, null, null );
    test2.addTextChangedListener(test2Listener);
    test2.setOnFocusChangeListener(test2Listener);
    test2.setHelperText(test2Listener.placeholder());

In both case I got something like XX-XX, when I call test1.getText(). What I'm doing wrong? thxs for help.

maskFilled wrong value for subsequent optional blocks

Library version:
4.0.0
Issue:
Hello,
I've faced the issue of wrong 'maskFilled' argument value when subsequent blocks with optional digits had been installed.

Consider the following example:

MaskedTextChangedListener.installOn(
    testEditText,
    "[0000] [9999] [999]",
    object : MaskedTextChangedListener.ValueListener{
        override fun onTextChanged(maskFilled: Boolean, extractedValue: String) {
            Log.d("TEXT_CHANGED", "extractedValue=[$extractedValue], maskFilled=[$maskFilled]")
        }
    }
)

The actual output as follows:

D/TEXT_CHANGED: extractedValue=[111], maskFilled=[false]  
D/TEXT_CHANGED: extractedValue=[1111], maskFilled=[false] 
D/TEXT_CHANGED: extractedValue=[11111], maskFilled=[false]
D/TEXT_CHANGED: extractedValue=[111111], maskFilled=[false]
D/TEXT_CHANGED: extractedValue=[1111111], maskFilled=[false]
D/TEXT_CHANGED: extractedValue=[11111111], maskFilled=[true]
D/TEXT_CHANGED: extractedValue=[111111111], maskFilled=[true]

But the expected output should be:

D/TEXT_CHANGED: extractedValue=[111], maskFilled=[false]  
D/TEXT_CHANGED: extractedValue=[1111], maskFilled=[true] 
D/TEXT_CHANGED: extractedValue=[11111], maskFilled=[true]
D/TEXT_CHANGED: extractedValue=[111111], maskFilled=[true]
D/TEXT_CHANGED: extractedValue=[1111111], maskFilled=[true]
D/TEXT_CHANGED: extractedValue=[11111111], maskFilled=[true]
D/TEXT_CHANGED: extractedValue=[111111111], maskFilled=[true]

Thanks in advance.

Dynamic mask is possible?

Is it possible to dynamically change the mask?
I'm using MaskedTextChangedListener and SwitchCompat to change the pattern of documents in a view but it's not switching and I'm getting this error
java.lang.IndexOutOfBoundsException: setSpan (3 ... 3) ends beyond length 2

image

Wrong formatting when letters mask used [___]

It works well for digits, but for letters, there is one issue:
For example mask is [____]
when you type the second character it will turn cursor to the start of the line, so new chars will appear from zero position.

This is because isDeletion defined in wrong way. I suggest using this method instead
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
isDeletion = p2 > p3
this.listener?.beforeTextChanged(p0, p1, p2, p3)
}

I can't make PR, so will wait for the fix)))

Force field validation after initial value set

Is it possible, when initially setting up a MaskedTextChangedListener to force the validation and call back the ValueListener? I am setting up some EditText fields with the following little method:

    private void setupField(EditText editText, String mask, MaskedTextChangedListener.ValueListener valueListener) {
        final MaskedTextChangedListener listener = new MaskedTextChangedListener(
                mask,
                false,
                editText,
                null,
                valueListener
        );

        editText.setOnFocusChangeListener(listener);
        editText.addTextChangedListener(listener);
    }

And then in a Fragment's onCreateView I'm setting up some of my fields as follows (I am using databinding):

        setupField(binding.viewCreditCardInformation.cardExpirationMonthValue, "[00]", (maskFilled, value) -> {
            expirationMonthFilled = maskFilled;
            updateSubmitButton();
        });

        setupField(binding.viewCreditCardInformation.cardExpirationYearValue, "[0000]", (maskFilled, value) -> {
            expirationYearFilled = maskFilled;
            updateSubmitButton();
        });

        setupField(binding.viewCreditCardInformation.cardSecurityCodeValue, "[000]", (maskFilled, value) -> {
            cvcFilled = maskFilled;
            updateSubmitButton();
        });

I find that my value listeners are only getting called on a user typing. Which is fine. I just would also like to trigger these to be called when I set up the current values for the fields when the data is bound to my views. Is that possible?

Format Money Thousand Separator

Hi, good day! I have an inquiry about the formatter for Text Mask Input. All of the mask are working aside from the Money Format. I can't find a way to format money input. I want something to look like this:

1
12
123
1,234
12,345
123,456
1,234,567
12,345,678
123,456,789

Can you help me out about this issue? Please see attached picture below. Thank you so much in advance!

screen shot 2018-09-25 at 2 15 08 pm

Affine Masks problem

Hi!

I try to implement universal TextEdit view using this library for phone entering. The idea is to change phone number mask dependent on country code prefix, entered by user.

I define the masks patterns array

    private final static List<String> sAffineMasks = new ArrayList<String>() {
        {
            add("+1([000])[000]-[0000]");
            add("+20([000])[000]-[0000]");
            add("+27-[00]-[000]-[0000]");
            add("+30([000])[000]-[0000]");
            add("+31-[00]-[000]-[0000]");
            add("+49([000])[000]-[0000]");
//and other 100500 masks

And listener as described in readme:

        final MaskedTextChangedListener listener = new PolyMaskTextChangedListener(
                "+7([000])[000]-[00]-[00]",
                sAffineMasks,
                true,
                this,
                null,
                new MaskedTextChangedListener.ValueListener() {
                    @Override
                    public void onTextChanged(boolean maskFilled, @NonNull final String extractedValue) {
                        Timber.d(extractedValue);
                        Timber.d(String.valueOf(maskFilled));
                    }
                }
        );

Then I try this. I erase default mask start (+7) and try to type +1, as result I see +7(1. How can I fix this issue?

Example from readme work fine.

Не работает форматирование телефона

исходные данные:

<EditText
            android:id="@+id/editTextPhone"
            style="@style/CabinetEditTextStyle"
            android:layout_height="40dp"
            android:layout_marginLeft="@dimen/registration_side_margin"
            android:layout_marginRight="@dimen/registration_side_margin"
            android:layout_marginTop="@dimen/registration_input_top_margin"
            android:hint="@string/registration_phone"
            android:inputType="phone"
             />
final MaskedTextChangedListener listener = new MaskedTextChangedListener(
                "+7 ([000]) [000]-[00]-[00]",
                true,
                mEditTextPhone,
                null,
                (maskFilled, extractedValue) -> {
                    String phone = mEditTextPhone.getText().toString();
                    getPresenter().onPhoneChangeText(phone);
                }
        );

        mEditTextPhone.addTextChangedListener(listener);
        mEditTextPhone.setOnFocusChangeListener(listener);
        mEditTextPhone.setHint("+7 (___) ___-__-__");

Ввожу цифру 6, получается +7 (6, затем перевожу курсор на цифру 7 и ввожу 4, то получится +7 (476, автоматически ввелась лишняя цифра 7!!! Баг проявится с любыми числами, важна лишь последовательность

Crash in pure Java project

Hello & thanks for the most flexible input mask for Android! 👍
It seems that recent versions of the library don't support pure Java projects (without Kotlin): initialization constantly fails with NoClassDefFoundError.
Tested on 3.2.0, 3.3.0, 3.4.0. Version 2.3.1 works fine.
If Java support was dropped intentionally, maybe it should be stated clearly in Readme?
Probably the same problem is mentioned in #42 and #52.
Very simple demo project.
Full stacktrace:

05-24 23:06:03.089 3192-3192/gmk57.inputmasktest E/AndroidRuntime: FATAL EXCEPTION: main
    Process: gmk57.inputmasktest, PID: 3192
    java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Intrinsics;
        at com.redmadrobot.inputmask.MaskedTextChangedListener.<init>(MaskedTextChangedListener.kt)
        at gmk57.inputmasktest.MainActivity.onCreate(MainActivity.java:19)
        at android.app.Activity.performCreate(Activity.java:6723)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2619)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6121)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
     Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.internal.Intrinsics" on path: DexPathList[[zip file "/data/app/gmk57.inputmasktest-1/base.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_dependencies_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_0_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_1_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_2_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_3_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_4_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_5_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_6_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_7_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_8_apk.apk", zip file "/data/app/gmk57.inputmasktest-1/split_lib_slice_9_apk.apk"],nativeLibraryDirectories=[/data/app/gmk57.inputmasktest-1/lib/arm64, /system/lib64, /vendor/lib64]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
        at com.redmadrobot.inputmask.MaskedTextChangedListener.<init>(MaskedTextChangedListener.kt) 
        at gmk57.inputmasktest.MainActivity.onCreate(MainActivity.java:19) 
        at android.app.Activity.performCreate(Activity.java:6723) 
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119) 
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2619) 
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6121) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779) 

Optional digits in mask leads to wrong value maskFilled in ValueListener

Library version:
2.2.0
Issue:
I use optional digits in the end of the input mask, and it leads to wrong value of maskFilled returned by ValueListener attached to MaskedTextChangedListener.
Here's the code:

// EditText carNumber;
carNumberListener = new MaskedTextChangedListener("[00099]", false, carNumber, null, null);
carNumberListener.setValueListener((maskFilled, extractedValue) -> Log.d("mask", "is filled " + maskFilled));
carNumber.addTextChangedListener(carNumberListener);
carNumber.setOnFocusChangeListener(carNumberListener);

Expected result:
when user entered 3, 4 or 5 digits maskFilled = true.

Actual result:
when user entered 3 or 4 digits maskFilled = false;
when user entered 5 digits maskFilled = true.

The same behavior is observed using masks [09] and [A] [000] [AA] [009].

Для taflanidi

В issue #64 @taflanidi изволил себе ряд неподобающих рассуждений, а затем ответы в теме были запрещены. Поэтому я открою новый issue.

Очень плохо, @CoolMind.
Вместо того, чтобы разобрать ситуацию и признать свой косяк, вы уходите в отрицание и начинаете придумывать оправдания своей ерунде.

Очень плохо, @taflanidi, сначала благодарить (и это правильно), что-то спрашивать, а затем хамить. Я запрещаю вам разговаривать со мной на уровне "косяк", "уходить в отрицание" и т.п. Так разговаривать вы можете со своими коллегами, и то, пока вас не пошлют лесом. Косяк - это ваш поток сознания и поведение библиотеки при смене inputType. Не смогли избавиться от этого бага, когда требуется в середине текста менять способ ввода. Ерунда - это ваши грубые заявления, не несущие полезной информации по теме. Я говорю об одном, вы о другом. Не ваше право навязывать мне стиль программирования и что-то спрашивать с меня. У меня свой подход, у вас свой.

Что за чушь? Я вообще ничего не писал про Activity.

Нельзя просто так брать — и дёргать системные методы у фрагмента просто потому что он не Activity. Что это за аргумент?

Этот метод вам не принадлежит. Вы думаете, в нём просто так должен вызываться super?

Более того скажу, Android SDK тут вообще ни при чём. Activity или Fragment — не имеет значения. У ряда шаблонов проектирования (Factory method, Chain of responsibility, Command, Template method и другие) есть методы, которые точно так же вам не принадлежат, они лежат в другом слое абстрации, и их нельзя от балды напрямую вызывать.

И да, Fragment lifecycle — это прямая реализация шаблона Template method.

Можно было банально сделать так:

void onResume() {
super.onResume();
configureTextFields();
}

void configureTextFields() {
// я могу вызывать этот метод вообще в любой момент
}

Вместо этого вы безо всяких причин нарушаете инкапсуляцию, нарушаете LSP и OCP, и осложняете жизнь себе и своим коллегам по проекту. Если не сейчас, то в будущем.

Т.е. вы решили не разбираться, почему у фрагментов не происходит вызов onResume(), а начинать мне тыкать паттернами, не относящимися к делу, инкапсуляцией, тоже не относящейся к делу. Весь ваш поток сознания кричит о том, что вы-то знаете, как надо делать, а ни разу даже не почитали, что проблема такая существует и имеет решения, которые с вашим не связаны никак.

Это стандартный в Андроиде подход

... который вы притянули за уши вместо того, чтобы ответить на мой прямой вопрос:

Я ответил на ваш вопрос, но вы не поняли ответ, поэтому проблема на вашей стороне.

Что это даёт?

Вам. Что это даёт вам, вашему проекту? Какую выгоду вы получаете от того, что (опять!) усложняете жизнь себе и своим коллегам? «Best practices», «Это стандартный подход»... бросать пыль в глаза вместо того, чтобы ответить по существу — какую вы ожидали реакцию от меня?

Я открыто утверждаю: вы вставили палку в колесо своего велосипеда на полном ходу, и теперь героически превозмогаете трудности, которые сами себе создали.

Я максимально подробно ответил на все вопросы, но вы любите бросаться словами и не понимать, что вам отвечают. Вы даже не знакомы с проектом и не в курсе, какие есть трудности, но уже лезете о них рассуждать.

Если бы я чистил в onResume(), то предполагалось, что они подключены и существуют, а кроме того, я и не думал, что их там может оказаться два-три-десять одинаковых.

А вы документацию читаете перед тем, как использовать инструмент?

List. Знаете, ArrayList, LinkedList, вот эти ребята. Не Set.

Да даже в документацию лазить не нужно. Метод называется ADD TextChangedListener. У вас состояние фрагмента восстанавливается после паузы. С чего вдруг список слушателей должен был стать пустым?

Не считайте других людей глупее себя. По-моему, в данной ситуации вы показали себя не просто хамом. Я читал документацию, знаком с коллекциями. Это вы можете объяснять новичкам, которые первый день видят Java, а тыкать мне ArrayList'ом просто смешно. Может, лучше расскажете о многопоточности или сокетах, а то я давненько не тренировался, начал подзабывать? И то я не уверен, что у вас сносно получится рассказать о чём-то, поскольку вы за других придумываете проблемы и способы решения.

Самое забавное, что ваш изначальный посыл был помочь людям избежать возможной ошибки, но вы не удосужились даже перевести первое сообщение на английский язык.

Возможно, для вас это будет новостью и вы уже не знаете, к чему придраться (к русскому вы придраться не можете, т.к. ваш уровень не выше моего), но часть issue вашей библиотеки написаны на русском. И, опять же, я их в большинстве почитал, прежде чем пользоваться библиотекой. Но с учётом вашей агрессии, возможно, уберу из проекта и заменю на известную библиотеку от Tinkoff. К вашему сведению, перевести на английский данный текст я могу, практически не заглядывая в словарь. Я думал об этом, но решил, что ищущие решение увидят слово Backspace и переведут самостоятельно онлайн.

Итого:

Вам следует пройти курс объектно-ориентированного дизайна. Любой, вам нужно усвоить основы.
Прочтите «Чистый код» и прочие книжки дяди Боба, Мартина Фаулера и др., которые сможете найти. Ключевые слова: SOLID, OOD. Перечитайте ещё раз, если уже читали.
Переделайте свой проект, сделайте его проще и чище.

Оплатите, может, пройду. А так - не вижу смысла. ООП за 20-30 лет не изменился. Иногда почитываю, чтобы не забывать.
Если будете отвечать, не забудьте пройти обучение хорошим манерам.

Text changes makes the caret jump to the beginning

Hi,

Very new to Android dev and java so I may just be misunderstanding something simple.

I'm using an EditTextPreference declared in XML and then programmatically setting up the input mask, but when adding or deleting text, the caret/cursor jumps to index 0.

Here's my Java implementation:

EditText editText = ((EditTextPreference) p).getEditText();
MaskedTextChangedListener l = new MaskedTextChangedListener("[_____]{-}[_____]{-}[_____]{-}[_____]{-}[___]", false, editText, null, null);
editText.addTextChangedListener(l);
editText.setOnFocusChangeListener(l);
editText.setHint(l.placeholder());

XML declaration:

<EditTextPreference
      android:title="License key"
      android:key="license_key"
      android:dialogTitle="License Key"
      android:dialogMessage="Set the location by entering a license key supplied to you. Case insensitive"
      android:defaultValue=""/>

Am I missing something?

Sample

final Mask mask = new Mask("+7 ([000]) [000] [00] [00]");
final String input = "+71234567890";
final Mask.Result result = mask.apply(
new CaretString(
input,
input.length()
),
true // you may consider disabling autocompletion for your case
);
final String output = result.getFormattedText().getString();

Not work. Constructor have two arguments

Optional input characters.

It would be useful to have the ability to input optional special characters such as '+'. Now it's only letters and numbers.

How to make part of text in EditText non-removable?

I have an EditText which has default value '998'

    <EditText
        android:id="@+id/login"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="phone"
        android:text="998"/>

I am trying to make '998' non-removable:

public class MainActivity extends AppCompatActivity {

    static String INPUT_MASK_PHONE = "{998} [00] [000]-[00]-[00]";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EditText login = (EditText) findViewById(R.id.login);

        MaskedTextChangedListener maskedTextChangedListener = new MaskedTextChangedListener(
                INPUT_MASK_PHONE,
                true,
                login,
                null,
                new MaskedTextChangedListener.ValueListener() {
                    @Override
                    public void onTextChanged(boolean b, @NotNull String s) {

                    }
                }
        );

        login.addTextChangedListener(maskedTextChangedListener);
        login.setSelection(login.length());

    }

}

I put 998 into {}, but this value can be removed when clear button is clicked. How to make '998' value non removable?

onMandatoryCharactersFilled() callback returns wrong result

Reproduces:
Mask: [000999]-[000999]
Input: 123456

Expected result:
onMandatoryCharactersFilled() callback returns false

Actual result:
onMandatoryCharactersFilled() callback returns true

Revealed reason:
onMandatoryCharactersFilled() callback result is calculated by formula:

val complete: Boolean = totalCountOfMandatoryCharacters <= inputLength

which does not work for the situation mentioned above as the mask contains 6 mandatory digits and the input contains 6 digits, too.

Suggestion:
The library needs more intelligent algorithm to calculate the count of filled mandatory characters in separate blocks.

Keep Hint While Typing

It's not an issue but an enhancement, I need to keep hint behind text even while typing... I try to add it by my self but have no time for it.. can you do this for me?

android:inputType="number" get ERROR

I could simulate the EditText example but it work's if the input type is specifically "text" or "phone".
As a number field I would like to restrict the keyboard to be only numeric, as a android:inputType="number".
How can I do that?
Thnaks!

mask_number
mask_phone
mask_text

ReversedMaskTextChangedListener allows endless input

Hi, Nice project

ReversedMaskTextChangedListener allows endless input, It means any entered number will erase the first entered numbers, I think the expected behavior is the behavior I get with the PolyMaskTextChangedListener and MaskedTextChangedListener which does not let the user enter more numbers than the size provided in the mask

final MaskedTextChangedListener listener = new ReversedMaskTextChangedListener(
                "[000],[000],[000]",
                editText,
                new MaskedTextChangedListener.ValueListener() {
                    @Override
                    public void onTextChanged(boolean maskFilled, @NonNull final String extractedValue) {
                        Log.d(MainActivity.class.getSimpleName(), extractedValue);
                        Log.d(MainActivity.class.getSimpleName(), String.valueOf(maskFilled));
                    }
                }
        );

optional number ignored

Hi!
I'm trying to validate a Chilean rut,

so I have a mask like this [90].[000].[000]{-}[_] in a ReversedMaskTextChangedListener, the problem is that the last digit is ignored so if i write nine digits the last one is deleted then for example if i add 123456789 I get 2.345.678-9 result.

it does not happen if i add a second number required, leaving the mask like this: [900].[000].[000]{-}[_] in this case, if I add 1234567890, I get 123.456.789-0

Add smth like regex masking

Hi, Robots! It will be cool, if library will offer something like regex filtering for cases, when I need to mask cardholder name. It's clear, that name and surname can be very short or long and developer can't predict lengths.

Поддержка RTL

Здравствуйте!
При реализации в приложении персидской локализации (Right To Left) возникла проблема с отображением маски в EditText. Маска отзеркалилась, но не верно:

в обычном макете (Left To Right) маска +7 ([000]) [000]-[00]-[00]
при заполнении ведёт себя так: +7 (123) 456-78-90. (слева направо, как и ожидается)
в макете Right To Left так: 456-78-90 (123) 7+, то есть сама строка заполняется справа налево, но внутри блоков - слева направо.

другой пример, маска карты - [0000] [0000] [0000] [0000]
LTR - 1234 5678 9099 9999
RTL - 9999 9099 5678 1234

Пытался отключить RTL для поля, добавив android:layoutDirection="ltr" - но это не дало результатов, поле по прежнему заполняется справа налево и в неправильном порядке.

Подскажите, пожалуйста, как сделать так, что бы при работе приложения в RTL режиме, поля с масками продолжали работать в LTR режиме ?

P.S.: Спасибо большое за вашу работу, библиотека отличная!

Unexpected cursor behavior

We are using your library to provide masks for various types of date and time input fields. For example, we have a time input field that uses the following mask "[00]{:}[00]" for 24 hour time format. We have noticed that occasionally the cursor returns to the starting position after typing the first two digits of text when it should not be moving at all in this scenario. Any ideas why this might be happening and how to fix it?

This is how we are configuring the input mask library to work with our EditText fields:

timeText = findViewById(R.id.formItemTextBox)   // get EditText from layout
val mask = "[00]{:}[00]"
val listener = MaskedTextChangedListener(
        mask,
        false,
        timeText,
        null,
        null)
timeText.addTextChangedListener(listener)

Crashes

`

                                                              java.lang.IndexOutOfBoundsException: setSpan (4 ... 4) ends beyond length 2
                                                                  at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1094)
                                                                  at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:669)
                                                                  at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:662)
                                                                  at android.text.Selection.setSelection(Selection.java:123)
                                                                  at android.text.Selection.setSelection(Selection.java:134)
                                                                  at android.widget.EditText.setSelection(EditText.java:128)
                                                                  at com.redmadrobot.inputmask.MaskedTextChangedListener.afterTextChanged(MaskedTextChangedListener.kt:113)
                                                                  at android.widget.TextView.sendAfterTextChanged(TextView.java:9269)
                                                                  at android.widget.TextView.setText(TextView.java:5008)
                                                                  at android.widget.TextView.setText(TextView.java:4826)
                                                                  at android.widget.EditText.setText(EditText.java:114)
                                                                  at android.widget.TextView.setText(TextView.java:4801)
                                                                  at com.redmadrobot.inputmask.MaskedTextChangedListener.onFocusChange(MaskedTextChangedListener.kt:154)
                                                                  at android.view.View.onFocusChanged(View.java:6259)
                                                                  at android.widget.TextView.onFocusChanged(TextView.java:9519)
                                                                  at android.widget.EditText.onFocusChanged(EditText.java:203)
                                                                  at android.view.View.handleFocusGainInternal(View.java:5985)
                                                                  at android.view.View.requestFocusNoSearch(View.java:9058)
                                                                  at android.view.View.requestFocus(View.java:9037)
                                                                  at android.view.View.requestFocus(View.java:9004)
                                                                  at android.view.View.requestFocus(View.java:8983)
                                                                  at android.support.v7.widget.RecyclerView.recoverFocusFromState(RecyclerView.java:3411)
                                                                  at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3633)
                                                                  at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3275)
                                                                  at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3796)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1742)
                                                                  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
                                                                  at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344)
                                                                  at android.widget.FrameLayout.onLayout(FrameLayout.java:281)
                                                                  at android.widget.ScrollView.onLayout(ScrollView.java:2402)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344)
                                                                  at android.widget.FrameLayout.onLayout(FrameLayout.java:281)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:131)
                                                                  at android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42)
                                                                  at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1375)
                                                                  at android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:870)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.support.v4.widget.DrawerLayout.onLayout(DrawerLayout.java:1193)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.widget.FrameLayout.layoutChildren(FrameLayout.java:344)
                                                                  at android.widget.FrameLayout.onLayout(FrameLayout.java:281)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                                  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1742)
                                                                  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1585)
                                                                  at android.widget.LinearLayout.onLayout(LinearLayout.java:1494)
                                                                  at android.view.View.layout(View.java:17945)
                                                                  at android.view.ViewGroup.layout(ViewGroup.java:5812)
                                                              	at android.widget.FrameLayout.layoutChildren(FrameLayou

format "+7 ([000]) [000] [00] [00]";

the error comes after showing alert

bug java.lang.IndexOutOfBoundsException:

java.lang.IndexOutOfBoundsException: setSpan (11 ... 11) ends beyond length 10
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1265)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:684)
at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:677)
at android.text.Selection.setSelection(Selection.java:76)
at android.widget.TextView.semSetSelection(TextView.java:11571)
at android.widget.TextView.semSetSelection(TextView.java:11585)
at android.widget.EditText.setSelection(EditText.java:125)
at com.redmadrobot.inputmask.MaskedTextChangedListener.afterTextChanged(MaskedTextChangedListener.kt:113)
at android.widget.TextView.sendAfterTextChanged(TextView.java:9508)
at android.widget.TextView$ChangeWatcher.afterTextChanged(TextView.java:12460)
at android.text.SpannableStringBuilder.sendAfterTextChanged(SpannableStringBuilder.java:1218)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:579)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:509)
at android.text.SpannableStringBuilder.replace(SpannableStringBuilder.java:508)
at android.view.inputmethod.BaseInputConnection.replaceText(BaseInputConnection.java:850)
at android.view.inputmethod.BaseInputConnection.commitText(BaseInputConnection.java:200)
at com.android.internal.widget.EditableInputConnection.commitText(EditableInputConnection.java:183)
at android.view.inputmethod.InputConnectionWrapper.commitText(InputConnectionWrapper.java:158)
at com.android.tools.profiler.support.event.InputConnectionWrapper.commitText(InputConnectionWrapper.java:43)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:345)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:91)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)

java.lang.IndexOutOfBoundsException: setSpan (4 ... 4) ends beyond length 2

final List<String> affineFormats = new ArrayList<>();
        affineFormats.add("8 ([000]) [000] [000] [00]");

        final MaskedTextChangedListener listener = new PolyMaskTextChangedListener(
                "+7 ([000]) [000] [00] [00]",
                affineFormats,
                true,
                mUsernamePhone,
                null,
                (maskFilled, extractedValue) -> {
                    Log.d(PhoneRegisterFragment.class.getSimpleName(), extractedValue);
                    Log.d(PhoneRegisterFragment.class.getSimpleName(), String.valueOf(maskFilled));
                }
        );

        mUsernamePhone.addTextChangedListener(listener);
        mUsernamePhone.setOnFocusChangeListener(listener);
        mUsernamePhone.setHint(listener.placeholder());
<EditText
        android:id="@+id/et_username_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="@dimen/text.23"
        android:layout_marginTop="@dimen/margin_22"
        android:digits="0123456789 -."
        android:inputType="number"
        android:background="@drawable/edt_bg_selector"
        />

Editing is broken

I used the watcher from the sample, and the editing does some strange things.

Enter some valid phone number, e.g. +7 (923) 123 45 67

For instance, I made a mistake in "45", and want to change it to "78"

I place cursor after "45":
+7 (923) 123 45<cursor> 67

I press backspace, and expect to get this:
"+7 (923) 123 4<cursor>6 7"
but suddenly get this:
"+7 (923) 123 <cursor>46 7"

This is definitely not what I wanted, please fix it or explain how to set up the watcher to support editing correctly, maybe it's my mistake somehow :)

Replacing last symbol works weird

Using sample code from the guide:
Enter:
+7 (123) 456 78 90

Then place cursor here:
+7 (<cursor>123) 456 78 90

Start entering something else (for instance, all aeros):
+7 (000) 000 00 0<cursor>1

If I press "0" one more time, I get this:
+7 (000) 000 00 0<cursor>0
instead of this (which is expected):
+7 (000) 000 00 00<cursor>

У кого не до конца стирается текст при помощи Backspace

Спасибо за библиотеку!
Если вы создаёте listener в onCreateView() (onCreate()), а добавляете его в onResume() и удаляете в onPause(), возможна следующая трудность.

onCreateView():

    listener = new MaskedTextChangedListener("+[0] ([000]) [000]-[0000]", phone, null);

onResume():

    super.onResume()
    phone.addTextChangedListener(listener);
    phone.onFocusChangeListener = listener;

onPause():

    phone.removeTextChangedListener(listener);
    phone.onFocusChangeListener = null;
    super.onPause();

Если вы будете набирать и удалять текст Backspace'ом, то в некоторых случаях он удаляется не до начала, а до первого нецифрового символа (дефис, скобка). Тогда следует проверить, что onResume() не вызывается два раза подряд (или случайно не сделали где-то раньше phone.addTextChangedListener(listener);. Такое возможно, если в активности вы принудительно вызываете onResume() верхнего фрагмента.

Listener doesn't work with Upper Case TextEdit

I have added the following code to apply an input mask to my activationCodeEditText EditText. However, this results in Caps or Upper Case longer being supported by default.

How can I use a listener and still force upper case in text entry?

final MaskedTextChangedListener listener = MaskedTextChangedListener.Companion.installOn(
                activationCodeEditText,
                "[____]-[____]",
                new MaskedTextChangedListener.ValueListener() {
                    @Override
                    public void onTextChanged(boolean maskFilled, @NonNull final String extractedValue) {
                        if (maskFilled) {
                            emailEditText.requestFocus();
                        }
                    }
                }
        );

This is the XML of my view. I'm using textCapCharacters as an android:inputType.

<EditText
                    android:id="@+id/activation_code"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="XXXX-XXXX"
                    android:inputType="textNoSuggestions|textCapCharacters"
                    android:imeActionLabel="Activation Code"
                    android:imeOptions="actionUnspecified"
                    android:singleLine="true"
                    android:textAlignment="center"
                    android:ellipsize="start"
                    android:gravity="center_horizontal"
                    android:textSize="24sp"
                    android:textStyle="bold" />

Suggestion - skip mask characters on backspace

I have a question about edittext behavior when user tries to delete mask character, for example a brackedt or a whitespace in a phone number from the tutorial. Currently the cursor moves one character left, but does not actually delete anything.

"+7 (999) 888 <cursor>77 66" -(backspace)>"+7 (999) 888<cursor> 77 66"

I think this to be a bit counter-intuitive, and probably it would be better to delete the nearest user-typed character to the left of the cursor:

"+7 (999) 888 <cursor>77 66" -(backspace)>"+7 (999) 88<cursor> 77 66"

Affinity masks with a default (For a credit card)

Hi guys! Such a brilliant library you have!

I have a small question. Here is a piece of code:

MaskedTextChangedListener.installOn(
            binding.paymentCardNumberLayout.editText!!,
            "[0000] [0000] [0000] [0000] [999]",
            affineFormats = listOf(
                "3[000] [000000] [00009]",              // American Express, Diners Club (14-15)
                "4[000] [0000] [0000] [0000] [999]",    // Visa (16-19)
                "5[000] [0000] [0000] [0000] [999]",    // Master Card (16-19)
                "6[000] [0000] [0000] [0000]"           // Discover (16)
            ),
            affinityCalculationStrategy = AffinityCalculationStrategy.PREFIX)

I want my user to start typing his card number and if it starts with one of the affinity prefixes so use it to format. But in this example the default mask is always used.

If I change the default mask to "4[000] [0000] [0000] [0000] [999]" for example it works like a charm. But there is always "4" in the edittext on the first display of the form.

SetText inside TextWatcher Crashes

This looks like a really promising plugin for a project I am working on but I need it to support deleting the gap. I found #14 where you document that this will not be supported which makes sense based on the explanation provided.

The api documentation states that the TextWatcher would be the appropriate place to do this and I have logic in place to get if we are doing a backspace and if the next set of chars is the gap. If this is the gap then it removes it and attempts to update the EditText by calling the SetText, but this throws an exception.

I had this working fine without the plugin but when I came across the plugin it seemed like a great way to clean up the masking code.

On some Samsung devices mask doesn't prevent user input

Version: 3.4.0

Mask

[AA]{-}[0000]{-}[000000]

On some Samsung devices when user inputs last digits he can continue to input this digits. Such digits won't be shown at the EditText but will be added to the "autocomplete current word" and when you try to press backspace, it will remove symbols from "autocomplete current word" until you reach the input from the EditText.

For example, you input AA-0000-000000, then you keep pressing 0 5 times. The EditText will still show AA-0000-000000, but autosuggestion will show AA-0000-00000000000 and when you try to backspace it, you have to remove 5 symbols from the suggestion, before the backspace will work.

Workaround for android:inputType doesn't work

I want to implement masked field as it shown in GIF example with mobile phone number, but also i wan't to show numeric keyboard to input numbers in that field.
Correct me if I'm wrong - but if i use the code down below:

        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/phone_masked"
        android:inputType="number"
        android:digits="0123456789 -.+()"
        />

...

            final EditText maskedEditText =  (EditText) getView().findViewById(R.id.phone_masked);
            final MaskedTextChangedListener listener = new MaskedTextChangedListener(
                    "+7 ([000]) [000] [00] [00]",
                    true,
                    maskedEditText,
                    null, null
            );
            maskedEditText.addTextChangedListener(listener);
            maskedEditText.setOnFocusChangeListener(listener);

my app crashes and no numeric keyboard appears by tapping on that field.
Is there any way to show numeric keyboard for mobile phone number input, like in GIF example?
https://github.com/RedMadRobot/input-mask-android/raw/assets/assets/gif-animations/direct-input.gif

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.