5

I'm trying to design a simple credit / debit card form. I have a cardNumber EditText field. As the user starts typing in, I want my app to insert a space after every 4 digits. I took help from this tutorial and modified a bit, but it's not working. It's not entering a space after every 4 digits.

MainActivity.java

package *************************

import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

import java.security.Key;

public class MainActivity extends AppCompatActivity {

    private EditText cardNumber, expiryDate, CVV, nameOnCard;

    private boolean flag = false;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);//Locks the screen orientation
        init(); //Initializes the variables

        typefunc();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void init()
    {
        cardNumber = (EditText) findViewById(R.id.cardNumber); //Field for storing the card number
        expiryDate = (EditText) findViewById(R.id.expiryDate); //For storing the Expiry Date
        CVV = (EditText) findViewById(R.id.CVV); //For storing the CVV
        nameOnCard = (EditText) findViewById(R.id.nameOnCard); //For storing the name of the Cardholder
    }

    public void typefunc()
    {
        cardNumber.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after)
            {

            }

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

                if(s.length()==16)
                {
                    expiryDate.setVisibility(View.VISIBLE);
                    expiryDate.requestFocus();
                    CVV.setVisibility(View.VISIBLE);
                }
            }

            @Override
            public void afterTextChanged(Editable s)
            {

                char space = ' ';

                if (s.length() > 0 && (s.length() % 5) == 0)
                {
                    char c = s.charAt(s.length() - 1);

                    if (Character.isDigit(c))
                    {
                        s.insert(s.length() - 1, String.valueOf(space));
                    }
                }
            }
        });

        expiryDate.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count)
            {
                if(s.length()==6)
                    CVV.requestFocus();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        CVV.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count)
            {
                if(s.length()==3) {
                    nameOnCard.setVisibility(View.VISIBLE);


                }
            }

            @Override
            public void afterTextChanged(Editable s)
            {
                if(s.length()==3)
                    nameOnCard.requestFocus();
            }
        });
    }

}
Community
  • 1
  • 1
Auro
  • 1,578
  • 3
  • 31
  • 62
  • You want the space when the user is changing the content not on e it's completed right?! So why not put it within onTextChanged – silverFoxA Jun 29 '16 at 08:16
  • `s.length() % 5` returns `0` if `s.length()` is a multiple of `5` not `4`. – Titus Jun 29 '16 at 08:16
  • @silverFoxA You are not allowed to change the content of `s` from `onTextChanged()`. It can be done only from `afterTextChanged()`. – Auro Jun 29 '16 at 08:37
  • Why you modified ?? the question you referred is having the same purpose as yours i.e. **Format credit card in edit text in android**. What is the problem then ? – Janki Gadhiya Jun 29 '16 at 09:00
  • Possible duplicate of [android edittext textwatcher format phone number like xxx-xxx-xx-xx](https://stackoverflow.com/questions/40036794/android-edittext-textwatcher-format-phone-number-like-xxx-xxx-xx-xx) – ashishdhiman2007 Jul 14 '17 at 10:10

4 Answers4

7

This is working fine

editText.addTextChangedListener(new TextWatcher() {
    private static final char space = ' ';

    @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) {
        // Remove spacing char
        if (s.length() > 0 && (s.length() % 5) == 0) {
            final char c = s.charAt(s.length() - 1);
            if (space == c) {
                s.delete(s.length() - 1, s.length());
            }
        }
        // Insert char where needed.
        if (s.length() > 0 && (s.length() % 5) == 0) {
            char c = s.charAt(s.length() - 1);
            // Only if its a digit where there should be a space we insert a space
            if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {
                s.insert(s.length() - 1, String.valueOf(space));
            }
        }
    }
});

and don't forgot to add below attributes to edittext

android:inputType="phone" android:maxLength="19"

hope this helps :)

Mr.7
  • 2,292
  • 1
  • 20
  • 33
2

Try this:

    cardNumber.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after){
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if(s.length()==16) {
                expiryDate.setVisibility(View.VISIBLE);
                expiryDate.requestFocus();
                CVV.setVisibility(View.VISIBLE);
            }
        }

        @Override
        public void afterTextChanged(Editable s) {
            String txt = s.toString();
            s.clear();
            s.insert(0, txt.replaceAll("\\D","").replaceAll("(\\d{4}(?!$))","$1-"));
        }
    });
Titus
  • 22,031
  • 1
  • 23
  • 33
0

That's because of the afterTextChanged()

afterTextChanged() - This will excecute after the text has changed!
This method is called to notify you that, somewhere within s, the text has been changed.

onTextChanged() - This will excecute while the text is changing
This method is called to notify you that, within s, the count characters beginning at start have just replaced old text that had length before.
More info here

So u could do something like:

cardNumber.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after)
    {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count)
    {
        if(cardNumber.length() == 4)
        {
            cardNumber.setText(cardNumber.getText() + " ");
        }
    }

    @Override
    public void afterTextChanged(Editable s)
    {

    }
});

Note: setText() will also trigger the onTextChanged() but because the length is now 5 it should not matter.

Strider
  • 4,452
  • 3
  • 24
  • 35
  • From the documentation of `onTextChanged()`: "It is an error to attempt to make changes to `s` from this callback". Check out this answer also: http://stackoverflow.com/a/6003815/3363481 – earthw0rmjim Jun 29 '16 at 08:25
  • I don't understand ur point? `onTextChanged()` is used to perform changes to the text instantly (while the text is changing) – Strider Jun 29 '16 at 08:30
  • No, it is not for changing the text, but for observing the changes. – earthw0rmjim Jun 29 '16 at 08:32
  • @Strider Trying to change `s` from `onTextChanged()` raises error. `s` can be changed only from `afterTextChanged()` – Auro Jun 29 '16 at 08:35
0

remove you code from afterTextChanged method and out it in onTextChanged method as below,

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

            char space = ' ';

            if (s.length() > 0 && (s.length() % 5) == 0)
            {
                char c = s.charAt(s.length() - 1);

                if (Character.isDigit(c))
                {
                    //s.insert(s.length() - 1, String.valueOf(space));
                    cardNumber.append(String.valueOf(space));
                }
            }


            if(s.length()==16)
            {
                expiryDate.setVisibility(View.VISIBLE);
                expiryDate.requestFocus();
                CVV.setVisibility(View.VISIBLE);
            }
        }
Vickyexpert
  • 3,147
  • 5
  • 21
  • 34