111
votes

I was wondering what the best way to do a live character count of an edit-text box is in Android. I was looking at this but I couldn't seem to make any sense of it.

To describe the problem, I have an EditText and I'm trying to limit the characters to 150. I can do this with an input filter, however I want to show right below the text box the number of characters a user has entered(Almost like stack overflow is doing right now).

If someone could write a small snippet of example code or point me in the right direction I'd appreciate it a lot.

15

15 Answers

161
votes

you can use a TextWatcher to see when the text has changed

private TextView mTextView;
private EditText mEditText;
private final TextWatcher mTextEditorWatcher = new TextWatcher() {
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
           //This sets a textview to the current length
           mTextView.setText(String.valueOf(s.length()));
        }

        public void afterTextChanged(Editable s) {
        }
};

you set the TextWatcher for the edittext with

mEditText.addTextChangedListener(mTextEditorWatcher);
116
votes

You can do character counting from xml itself using TextInputLayout wrapper for EditText introduced in SupportLibrary v23.1

Just wrap your EditText with a TextInputLayout and set CounterEnabled to true and Set a counterMaxLength.

<android.support.design.widget.TextInputLayout
    android:id="@+id/textContainer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:counterEnabled="true"
    app:counterMaxLength="20"
    >
    <EditText
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Text Hint"
        />
</android.support.design.widget.TextInputLayout>

You'll get a material effect like this

You may use counterOverflowTextAppearance , counterTextAppearance to style the counter.

EDIT

From Android documentation.

The TextInputEditText class is provided to be used as a child of this layout. Using TextInputEditText allows TextInputLayout greater control over the visual aspects of any text input. An example usage is as so:

     <android.support.design.widget.TextInputLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content">

     <android.support.design.widget.TextInputEditText
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:hint="@string/form_username"/>

 </android.support.design.widget.TextInputLayout>

TextInputLayout TextInputEditText

32
votes

You can do it with TextInputLayout and compat libraries with:

app:counterEnabled="true"
app:counterMaxLength="420"

and complete:

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:counterEnabled="true"
    app:counterMaxLength="420">

    <EditText
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:maxLength="420" />

</android.support.design.widget.TextInputLayout>
18
votes

in xml add this attribute for editText

    android:maxLength="80"

in java add this listener

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

        }

        @Override
        public void afterTextChanged(Editable s) {
            tv_counter.setText(80 - s.toString().length() + "/80");

        }
    });
8
votes

Its very Simple Follow the instructions below:

====Add them to your Imports===

import android.text.Editable;
import android.text.TextWatcher;

=====Define this=====

private TextView sms_count;

==========Inside On Create=====

sms_count = (TextView) findViewById(R.id.textView2);


final TextWatcher txwatcher = new TextWatcher() {
   public void beforeTextChanged(CharSequence s, int start, int count, int after) {
   }

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

      sms_count.setText(String.valueOf(s.length()));
   }

   public void afterTextChanged(Editable s) {
   }
};

sms_message.addTextChangedListener(txwatcher);
5
votes
    You can use TextWatcher class to see text has changed and how much number of character remains.Here i have set counter of 140 characters.

    EditText typeMessageToPost;
    TextView number_of_character;
public void onCreate(Bundle savedBundleInstance) {
        super.onCreate(savedBundleInstance);
setContentView(R.layout.post_activity);
typeMessageToPost.addTextChangedListener(mTextEditorWatcher);
}
private final TextWatcher mTextEditorWatcher=new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // TODO Auto-generated method stub

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
            // TODO Auto-generated method stub

        }

        @Override
        public void afterTextChanged(Editable s) {
            // TODO Auto-generated method stub
            number_of_character.setText(String.valueOf(140-s.length()));
        }
    };
5
votes

You can add a counter to your TextInputEditText being wrapped in a TextInputLayout. As you can see in the example, counterEnabled enables this feature and counterMaxLengh defines the number of characters for it.

<com.google.android.material.textfield.TextInputLayout
        android:id="@+id/til_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:counterEnabled="true"
        app:counterMaxLength="50">
    <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/et_title"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
</com.google.android.material.textfield.TextInputLayout>
4
votes

Just set these 2 lines in TextInputLayoutin your XML file:

app:counterEnabled="true"
app:counterMaxLength="200"
4
votes

This solution uses Kotlin and shows the number of characters left. Also, if the current number of characters surpasses the limit of 50, the text color will change to red.

Kotlin

private val mTitleTextWatcher = object : TextWatcher {
    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}

    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if(YOUR_EDIT_TEXT_ID.text.toString().trim().length < 51){
            YOUR_CHAR_LEFT_TEXTVIEW_ID.text = (50 - YOUR_EDIT_TEXT_ID.text.toString().trim().length).toString()
            YOUR_CHAR_LEFT_TEXTVIEW_ID.setTextColor(Color.BLACK)
        }
        else{
            YOUR_CHAR_LEFT_TEXTVIEW_ID.text = "0"
            YOUR_CHAR_LEFT_TEXTVIEW_ID.setTextColor(Color.RED)
        }
    }

    override fun afterTextChanged(s: Editable) {}
}

Also, don't forget to add the TextWatcher to your EditText

YOUR_EDIT_TEXT_ID.addTextChangedListener(mTitleTextWatcher)
2
votes

I met the same problem and I tried Cameron's method. It works but there is a minor bug: If the user use copy and paste then it fails to count the chars. So I suggest to do after the text changed, like below:

    private final TextWatcher mTextEditorWatcher = new TextWatcher() {
         public void beforeTextChanged(CharSequence s, int start, int count, int after) {

         }

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

         }

          public void afterTextChanged(Editable s) {
             //This sets a textview to the current length
             mTextView.setText(String.valueOf(s.length()));
         }
    };
1
votes

Use android:maxLength="140"

That should work. :)

Hope that helps

1
votes
     edtmm.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) {
                    int data = Integer.parseInt(String.valueOf(s.length()));
                    if (data == 2) {
                        edtdd.requestFocus();
                    } else {
                        edtmm.requestFocus();
                    }
                }

                @Override
                public void afterTextChanged(Editable s) {

                }
            });
0
votes

Try doing something like this.

This solution may be more performant as opposed to getting CharSequence.length. Each time you tap on the soft keyboard the event fires; therefore, if you do a length it will count the CharSequence each time, which may slow if you start getting into large CharSequnces. The event listener on text change tacks the before and after count. This works well for increment and decrement values

@Override
        public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
            int tick = start + after;
            if(tick < mMessageMax) {
                int remaining = mMessageMax - tick;
                ((TextView)findViewById(R.id.contact_us_chars)).setText(String.valueOf(remaining));
            }
        }
0
votes

try this

private TextWatcher textWatcher = new TextWatcher() {
    @Override
    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
        editText.post(new Runnable() {
            @Override
            public void run() {
                if (length < 100) {
                    if (count > 0 && after <= 0)/*remove emoij*/ {
                        length--;
                    } else if (count > after)/*remove text*/ {
                        length--;
                    } else if (count == 0 && after > 1)/*emoij*/ {
                        ++length;
                    } else if (count == 0 && after == 1)/*Text*/ {
                        ++length;
                    } else if (count > 0 && after > 1) {
                        ++length;
                    }
                    if (s.length() <= 0)
                        length = 0;
                    Log.w("MainActivity", " Length: " + length);
                } else {
                    if (count > 0 && after <= 0)/*remove emoij*/ {
                        length--;
                    } else if (count > after)/*remove text*/ {
                        length--;
                    }
                    Log.w("MainActivity", " Length: " + length);
                }

                if (length == 100) {
                    editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(s.length())});
                } else {
                    editText.setFilters(new InputFilter[]{});
                }
            }
        });
    }

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

    }

    @Override
    public void afterTextChanged(Editable s) {

    }
};

`

0
votes

Clear way;

abstract class CharacterWatcher : TextWatcher {
    override fun afterTextChanged(text: Editable?) {
        afterCharacterChanged(text?.lastOrNull(), text?.length)
    }

    override fun beforeTextChanged(text: CharSequence?, start: Int, count: Int, before: Int) {}

    override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) {}

    abstract fun afterCharacterChanged(char: Char?, count: Int?)
}



 editText.addTextChangedListener(new CharacterWatcher() {
            @Override
            public void afterCharacterChanged(@Nullable Character character, @Nullable Integer count) {
                action()
            }
        });