The text of the edittext can't modified [duplicate] - android

I want to have constant text inside editText like:
http://<here_user_can_write>
User should not be able to delete any chars from "http://", I searched about this and found this:
editText.setFilters(new InputFilter[] {
new InputFilter() {
public CharSequence filter(CharSequence src, int start,
int end, Spanned dst, int dstart, int dend) {
return src.length() < 1 ? dst.subSequence(dstart, dend) : "";
}
}
});
but I don't know whether it restricts user to not delete any chars from start to end limit. I also could not understand use of Spanned class.
One way would be a good choice if we can put a TextView inside EditText but I don't think it is possible in Android since both are Views, is it possible?

Did u try this method?
final EditText edt = (EditText) findViewById(R.id.editText1);
edt.setText("http://");
Selection.setSelection(edt.getText(), edt.getText().length());
edt.addTextChangedListener(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) {
if(!s.toString().startsWith("http://")){
edt.setText("http://");
Selection.setSelection(edt.getText(), edt.getText().length());
}
}
});

As of version 1.2.0-alpha01 of material design library, prefix and suffix is supported for text fields:
<com.google.android.material.textfield.TextInputLayout
app:prefixText="Price: "
app:prefixTextAppearance="..."
app:prefixTextColor="..."
app:suffixText="Dollar"
app:suffixTextColor="..."
app:suffixTextAppearance="...">
<com.google.android.material.textfield.TextInputEditText .../>
</com.google.android.material.textfield.TextInputLayout>
The only downside in my opinion is that the suffix is fixed at the end of the text field and there is no option to make it flow with the input text. You can vote on this issue for that.

That's how you can actually do it with an InputFilter:
final String prefix = "http://"
editText.setText(prefix);
editText.setFilters(new InputFilter[] {
new InputFilter() {
#Override
public CharSequence filter(final CharSequence source, final int start,
final int end, final Spanned dest, final int dstart, final int dend) {
final int newStart = Math.max(prefix.length(), dstart);
final int newEnd = Math.max(prefix.length(), dend);
if (newStart != dstart || newEnd != dend) {
final SpannableStringBuilder builder = new SpannableStringBuilder(dest);
builder.replace(newStart, newEnd, source);
if (source instanceof Spanned) {
TextUtils.copySpansFrom(
(Spanned) source, 0, source.length(), null, builder, newStart);
}
Selection.setSelection(builder, newStart + source.length());
return builder;
} else {
return null;
}
}
}
});
If you also want the prefix to be not selectable you can add the following code.
final SpanWatcher watcher = new SpanWatcher() {
#Override
public void onSpanAdded(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanRemoved(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanChanged(final Spannable text, final Object what,
final int ostart, final int oend, final int nstart, final int nend) {
if (what == Selection.SELECTION_START) {
if (nstart < prefix.length()) {
final int end = Math.max(prefix.length(), Selection.getSelectionEnd(text));
Selection.setSelection(text, prefix.length(), end);
}
} else if (what == Selection.SELECTION_END) {
final int start = Math.max(prefix.length(), Selection.getSelectionEnd(text));
final int end = Math.max(start, nstart);
if (end != nstart) {
Selection.setSelection(text, start, end);
}
}
}
};
editText.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

There was a slight problem with #Rajitha Siriwardena's answer.
It assumes that the entire string except the suffix has been deleted before the suffix is meaning if you have the string
http://stackoverflow.com/
and try to delete any part of http:// you will delete stackoverflow.com/ resulting in only http://.
I also added a check incase the user tries to input before the prefix.
#Override
public void afterTextChanged(Editable s) {
String prefix = "http://";
if (!s.toString().startsWith(prefix)) {
String cleanString;
String deletedPrefix = prefix.substring(0, prefix.length() - 1);
if (s.toString().startsWith(deletedPrefix)) {
cleanString = s.toString().replaceAll(deletedPrefix, "");
} else {
cleanString = s.toString().replaceAll(prefix, "");
}
editText.setText(prefix + cleanString);
editText.setSelection(prefix.length());
}
}
Note: this doesn't handle the case where the user tries to edit the prefix itself only before and after.

Taken from Ali Muzaffar's blog, see the original post for more details.
Use custom EditText View to draw the prefix text and add padding according to the prefix text size:
public class PrefixEditText extends EditText {
private String mPrefix = "$"; // add your prefix here for example $
private Rect mPrefixRect = new Rect(); // actual prefix size
public PrefixEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
mPrefixRect.right += getPaint().measureText(" "); // add some offset
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), getPaint());
}
#Override
public int getCompoundPaddingLeft() {
return super.getCompoundPaddingLeft() + mPrefixRect.width();
}
}

You had it almost right, try
private final String PREFIX="http://";
editText.setFilters(new InputFilter[]{new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int
dend) {
return dstart<PREFIX.length()?dest.subSequence(dstart,dend):null;
}
}});

CODE TO ADD CUSTOM PREFIX TO YOUR EDITTEXT (PREFIX NOT EDITABLE)
Code from Medium by Ali Muzaffar
public class PrefixEditText extends AppCompatEditText {
float originalLeftPadding = -1;
public PrefixEditText(Context context) {
super(context);
}
public PrefixEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PrefixEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
calculatePrefix();
}
private void calculatePrefix() {
if (originalLeftPadding == -1) {
String prefix = (String) getTag();
float[] widths = new float[prefix.length()];
getPaint().getTextWidths(prefix, widths);
float textWidth = 0;
for (float w : widths) {
textWidth += w;
}
originalLeftPadding = getCompoundPaddingLeft();
setPadding((int) (textWidth + originalLeftPadding),
getPaddingRight(), getPaddingTop(),
getPaddingBottom());
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String prefix = (String) getTag();
canvas.drawText(prefix, originalLeftPadding, getLineBounds(0, null), getPaint());
}
}
And XML
<com.yourClassPath.PrefixEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:textSize="14sp"
android:tag="€ " />

An easy to use Kotlin extension function for this purpose
fun EditText.stickPrefix(prefix: String) {
this.addTextChangedListener(afterTextChanged = {
if (!it.toString().startsWith(prefix) && it?.isNotEmpty() == true) {
this.setText(prefix + this.text)
this.setSelection(this.length())
}
})
}
//someEditText.stickPrefix("+")

I know I'm reviving an old post but I want to share with the community that I have struggled with this topic these days and I found that placing a TextView over the EditText is not only perfectly doable (to respond to the second part of the question), much more in this case when the constant text is needed in the starting position, but preferable, too. Moreover the cursor won't even move before the "mutable" text at all, which is an elegant effect.
I prefer this solution because it doesn't add workload and complexity to my app with listeners and whatsoever.
Here's a sample code of my solution:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="3dp"
android:text="http://" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textUri"
android:paddingStart="choose an appropriate padding" />
</RelativeLayout>
By enclosing the views in a RelativeLayout they will be overlapped.
The trick here is playing with the android:paddingStart property of the EditText, to make the text start just right after the TextView, while android:layout_centerVertical="true" and android:layout_marginStart="3dp" properties of the TextView make sure that its text is correctly aligned with text inputted and with the start of the underlying line of the EditText (or at least this happens when using a Material themed one).

I made Kotlin extension function for adding prefix to EditText
fun EditText.addPrefix(prefix: String) {
var text = ""
var isPrefixModified = false
val formattedPrefix = "$prefix "
var lastCharSequence: CharSequence? = null
val setEditText: () -> Unit = {
setText(text)
Selection.setSelection(editableText, text.length)
}
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
val newText = editable.toString()
when {
isPrefixModified -> {
isPrefixModified = false
setEditText()
}
isTryingToDeletePrefix(newText) -> {
setEditText()
}
isNewInput(newText) -> {
text = "$formattedPrefix$newText"
setEditText()
}
else -> {
text = newText
}
}
}
override fun beforeTextChanged(charSequence: CharSequence?, start: Int,
count: Int, after: Int) {
charSequence?.let {
if (it != lastCharSequence && it.isNotEmpty() && start <= prefix.length) {
isPrefixModified = true
}
lastCharSequence = charSequence
}
}
override fun onTextChanged(charSequence: CharSequence?, start: Int,
before: Int, count: Int) {
// Ignore
}
private fun isTryingToDeletePrefix(newText: String) =
text.isNotEmpty() && newText.length < text.length && isNewInput(newText)
private fun isNewInput(newText: String) = !newText.contains(formattedPrefix)
})
}

I just found the solution how to make prefix not-editable and how to save text if you try to remove prefix. That's very close to #Rajitha Siriwardena answer. All you missed is to save text before any changes applied. It will be restored in afterTextChanged(...).
Code:
final String prefix = "http://";
editText.setText(prefix);
Selection.setSelection(editText.getText(), editText.getText().length());
editText.addTextChangedListener(new TextWatcher() {
String text;
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
text = charSequence.toString();
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
if (!editable.toString().startsWith(prefix)) {
editText.setText(text);
Selection.setSelection(editText.getText(), editText.getText().length());
}
}
});

This one is basically to add prefix "+91" to your edit text field of phone number.
1.Add this code to oncreate() of activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
// Write other things......//
etPhoneNumber.setFilters(new InputFilter[]{getPhoneFilter(),newInputFilter.LengthFilter(13)});
etPhoneNumber.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
if (etPhoneNumber.getText().toString().isEmpty()) {
etPhoneNumber.setText("+91");
Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length()); }
} else {
if (etPhoneNumber.getText().toString().equalsIgnoreCase("+91")) {
etPhoneNumber.setFilters(new InputFilter[]{});
etPhoneNumber.setText("");
etPhoneNumber.setFilters(new InputFilter[]{getPhoneFilter(),new InputFilter.LengthFilter(13)});
}
}
}
});
}
2.Declare a method called getPhoneFilter()
private InputFilter getPhoneFilter() {
Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());
etPhoneNumber.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void afterTextChanged(Editable s) {
if(!s.toString().startsWith("+91")){
if (etPhoneNumber.getFilters() != null && etPhoneNumber.getFilters().length > 0) {
etPhoneNumber.setText("+91");
Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());
}
}
}
});
// Input filter to restrict user to enter only digits..
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!String.valueOf(getString(R.string.digits_number)).contains(String.valueOf(source.charAt(i)))) {
return "";
}
}
return null;
}
};
return filter;
}
3.declare "digits_number" in your values file
<string name="digits_number">1234567890+</string>

Based on #demaksee comment. I extend EditText and override function onSelectionChanged. So user even can`t edit prefix. Very simple and useful.
Kotlin:
private var prefix : String? = ""
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
if (prefix != null && prefix!!.isNotBlank()) {
var finalStart = selStart
var finalEnd = selEnd
val prefixLength = prefix!!.length
if (prefixLength > selStart) {
finalStart = prefixLength
}
if (prefixLength > selEnd) {
finalEnd = prefixLength
}
if (finalStart == selStart && finalEnd == selEnd) {
super.onSelectionChanged(finalStart, finalEnd)
} else {
val startWithPrefix = text?.startsWith(prefix ?: "") ?: prefix.isNullOrBlank()
if (!startWithPrefix) {
setText(prefix)
}
setSelection(finalStart, finalEnd)
}
}
}
public fun setPrefix(prefix: String) {
editText.setText(prefix)
editText.setSelection(prefix.length)
this.prefix = prefix
}

Here is a less efficient solution that should handle all cases for when characters OR words are deleted/inserted in OR around the prefix.
prefix = "http://"
extra = "ahhttp://"
differencePrefix(prefix, extra) = "aht"
Code:
public static String differencePrefix(String prefix, String extra) {
if (extra.length() < prefix.length()) return "";
StringBuilder sb = new StringBuilder();
StringBuilder eb = new StringBuilder();
int p = 0;
for (int i = 0; i < extra.length(); i++) {
if (i >= prefix.length()) {
while(p < extra.length()) {
eb.append(extra.charAt(p));
p++;
}
break;
}
if (p >= extra.length()) break;
char pchar = extra.charAt(p);
char ichar = prefix.charAt(i);
while(pchar != ichar) {
//check if char was deleted
int c = i + 1;
if (c < prefix.length()) {
char cchar = prefix.charAt(c);
if (cchar == pchar) {
break;
}
}
sb.append(pchar);
p++;
if (p >= extra.length()) break;
pchar = extra.charAt(p);
}
p++;
}
return eb.toString() + sb.toString();
}
You can use it like this
editText.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) {
String input = s.toString();
if (!input.startsWith(prefix)) {
String extra = differencePrefix(prefix, input);
String newInput = prefix + extra;
editText.setText(newInput);
editText.setSelection(newInput.length());
}
}
});

EditText msg=new EditText(getContext());
msg.setSingleLine(true);
msg.setSingleLine();
msg.setId(View.generateViewId());
msg.measure(0,0);
TextView count=new TextView(getContext());
count.setTextColor(Color.parseColor("#666666"));
count.setText("20");
count.setPadding(0,0,(int)Abstract.getDIP(getContext(),10),0);
count.measure(0,0);
float tenPIX =TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,getContext().getResources().getDisplayMetrics());
msg.setPadding((int)tenPIX,(int)tenPIX,(int)(int)tenPIX+count.getMeasuredWidth(),(int)tenPIX);
RelativeLayout ll1=new RelativeLayout(getContext());
ll1.addView(msg,new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
LayoutParams countlp=new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
countlp.addRule(RelativeLayout.ALIGN_END,msg.getId());
countlp.addRule(RelativeLayout.ALIGN_BASELINE,msg.getId());
ll1.addView(count,countlp);

The code below works for me. It handles cases when the user edits the prefix, deletes it, inserts text from the buffer, changes the selected text. If the user changes the prefix, the focus moves to the end of the prefix.
final String prefix = "http://";
final String[] aLastText = {prefix};
et.setText(prefix);
et.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable sNew) {
if (!sNew.toString().startsWith(prefix)) {
String sLast = aLastText[0];
boolean isRemoving = sNew.length() < sLast.length();
int start;
int end = sNew.length() - 1;
for (start = 0; start < sLast.length() && start < sNew.length(); start++) {
if (sLast.charAt(start) != sNew.charAt(start))
break;
}
int k = sLast.length() - 1;
for (; end >= start && k >= 0; end--, k--) {
if (sLast.charAt(k) != sNew.charAt(end))
break;
}
String sEdited = sNew.toString().substring(start, ++end);
k += isRemoving ? 1 : 0;
k = k < prefix.length() ? prefix.length() : k;
String sSuffix = sLast.substring(k, sLast.length());
et.setText(prefix + sEdited + sSuffix);
et.setSelection(et.getText().length() - sSuffix.length());
}
aLastText[0] = et.getText().toString();
}
});
Examples:
ht5tp://localhost, 5http://localhost, http:/5/localhost -> http://5localhost
http:localhost -> http://localhost

what worked for me is to add some changes on Rajitha Siriwardena code :
First, put text on the Edittext or TextInputEditText xml layout :
android:text="http://"
the purpose is to test the if condition on the first attempt
Second,
test the condition with if like this
#Override
public void afterTextChanged(Editable s) {
if (!s.toString().startsWith("http://")) {
etPhone.setText("http://");
etPhone.setSelection(etPhone.length());
}

I am baffled by the complex answers posted. More easier way will be to add a textview with code as text as a prefix and put some elevation. This way, you will much finer control over the designing of the code("+91"). Here's an example of the same.
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/mobile_bottom_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/card_with_top_radius"
android:elevation="12dp"
android:paddingBottom="#dimen/dim_18"
app:flow_verticalAlign="bottom"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="#+id/code"
style="#style/color_333333_text_14_roboto_regular_venus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dim_16"
android:text="+91"
android:elevation="1dp"
app:layout_constraintBottom_toBottomOf="#id/number"
app:layout_constraintStart_toStartOf="#id/number"
app:layout_constraintTop_toTopOf="#id/number" />
<com.gradeup.baseM.view.custom.TabletEditText
android:id="#+id/number"
style="#style/color_333333_text_12_roboto_medium_venus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="#dimen/dim_16"
android:layout_marginTop="#dimen/dim_17"
android:layout_marginBottom="#dimen/dim_16"
android:background="#drawable/e6e6e6_4dp_curved_border_white_bg"
android:hint="#string/enter_mobile_number"
android:imeOptions="flagNoExtractUi"
android:inputType="number"
android:maxLength="10"
android:maxLines="1"
android:paddingVertical="#dimen/dim_17"
android:paddingStart="#dimen/dim_50"
android:paddingEnd="20dp"
android:textColorHint="#color/color_999999_8799ae"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/subTitle">
</com.gradeup.baseM.view.custom.TabletEditText>
</androidx.constraintlayout.widget.ConstraintLayout>
Things to keep in mind :-
Adjust editText paddingStart attribute according to your need.
Put some elevation in code TextView.

Related

How to force decimal point after two digit in edittext?

How can I set the fractional point after two integers in android and set the limit to 55 digits only? After searching a lot I am posting my problem here. I have an Edittext with an android:inputType="number" . I am able to set max limit to the EditText to 55 digits. I want user can able to type fractional digit also between 0 to 55 digits like 24.22 or 52.88.
Can some please tell me how can I achieve this?
Here is my code.
public class FuelCostCalculator extends DialogFragment {
EditText etLocation, etKilo, etLiter, etFuelCost;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, R.style.fullscreen_dialog);
}
private void init(View view) {
//this is my editText
etLiter = view.findViewById(R.id.etLiter);
InputFilterMinMax filter = new InputFilterMinMax("0", "5499") {};
etLiter.setFilters(new InputFilter[]{filter});
}
}
My Filter class is :
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;
}
}
Any kind of help is appreciated :
Check this answer . Though it is not so much clean but it will work as per your needs. Assuming that actAddItemFromDropdownList is your edit text and you have set maximum digits limit in your Xml.
int max =0;
String text ="";
actAddItemFromDropdownList.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) {
}
#Override
public void afterTextChanged(Editable editable) {
if(actAddItemFromDropdownList.getText().length()>0){
if(text.equals(actAddItemFromDropdownList.getText().toString())){
text = "";
}else {
text = actAddItemFromDropdownList.getText().toString();
max = max + 1;
if (max == 2) {
text = text + ".";
max = 0;
actAddItemFromDropdownList.setText(text);
actAddItemFromDropdownList.setSelection(actAddItemFromDropdownList.getText().length());
}
}
}
}
});
try it and if it works mark as selected answer.

Limit a EditText to exactly certain number of word count

How can we limit the EditText length to certain number of word count (100 words).
Even though copy a large text from some where,while pasting it should accept only 100 words.
I know we can limit it by character count (maxLength)
I tried with below code, but it doesn't limit it by exactly 100 words and it is allowing copy-paste large text.
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
int wordsLength = countWords(charSequence.toString());// words.length;
// count == 0 means a new word is going to start
if (count == 0 && wordsLength >= MAX_WORD_LIMIT) {
int charLength = mDescription.getText().length();
setCharLimit(mDescription, charLength > 0 ? charLength - 2 : charLength);
} else {
removeFilter(mDescription);
}
and
private InputFilter filter;
private void setCharLimit(EditText et, int max) {
filter = new InputFilter.LengthFilter(max);
et.setFilters(new InputFilter[]{filter});
}
private void removeFilter(EditText et) {
if (filter != null) {
et.setFilters(new InputFilter[0]);
filter = null;
}
}
edittext.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
//To fix word count
String yourText= editText.getText().toString().replace(String.valueOf((char) 160), " ");
if (yourText.split("\\s+").length > MAX_WORD_LIMIT ) {
int space = 0;
int length = 0;
for (int i = 0; i < yourText.length(); i++) {
if (yourText.charAt(i) == ' ') {
space++;
if (space >= MAX_WORD_LIMIT) {
length = i;
break;
}
}
}
if (length > 1) {
editText.getText().delete(length, yourSelf.length()); // deleting last space
setCharLimit(editText, length - 1); //or limit edit text
}
} else {
removeFilter(editText);
}
}
#Override
public void afterTextChanged(Editable s) {
});
private InputFilter filter;
private void setCharLimit(EditText et, int max) {
filter = new InputFilter.LengthFilter(max);
et.setFilters(new InputFilter[] { filter });
}
private void removeFilter(EditText et) {
if (filter != null) {
et.setFilters(new InputFilter[0]);
filter = null;
}
}
here 160 is the non breaking space
You can do this by using:
android:maxLength="50"
in your EditText.
Hope its work for you
edittext.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) {
// Check your edittext length here
if (edittext.getText().toString().length() > 100)
edittext.setText(edittext.getText().toString().substring(0, 100);
}
});
Kotlin Language -- To check maximum words need to check total space
--> Step-1
var filter: InputFilter?=null
--> Step-2
edt.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
var count : Int = 0
if(s.toString().length > 0)
for(i in 0..s.toString().length-1){
if(s.toString()[i].toString() == " "){
count++
}
if(i > 1 && s.toString()[i-1].toString() == " " && s.toString()[i].toString() == " "){
count--
}
}
if (count >= MAX_COUNTS_WORDS){
filter = InputFilter.LengthFilter(edt.text.toString().length)
edt.filters = arrayOf<InputFilter>(filter ?: return)
}
else if (filter != null) {
edt.filters = arrayOfNulls(0)
filter = null
}
}
override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
If you want to find word count then you can use java logic.
String text = "More than 100 word sentence";
String[] txtArr = text.split(" ");
Now Pick only first 100 index words.
This is just a hack. May be there are more optimal ways out there.
You can use split with index of functions. Sorry for so long string, hope you will get the idea. Just extract some variables.
edittext.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) {
if (edittext.getText().toString().split(" ").size() > 100)
edittext.setText(edittext.getText().toString().substring(0, edittext.getText().toString().indexOf(edittext.getText().toString().split(" ").get(100))));
}
});
use android:maxLength property of edittext to solve your problem.

Put constant text inside EditText which should be non-editable - Android

I want to have constant text inside editText like:
http://<here_user_can_write>
User should not be able to delete any chars from "http://", I searched about this and found this:
editText.setFilters(new InputFilter[] {
new InputFilter() {
public CharSequence filter(CharSequence src, int start,
int end, Spanned dst, int dstart, int dend) {
return src.length() < 1 ? dst.subSequence(dstart, dend) : "";
}
}
});
but I don't know whether it restricts user to not delete any chars from start to end limit. I also could not understand use of Spanned class.
One way would be a good choice if we can put a TextView inside EditText but I don't think it is possible in Android since both are Views, is it possible?
Did u try this method?
final EditText edt = (EditText) findViewById(R.id.editText1);
edt.setText("http://");
Selection.setSelection(edt.getText(), edt.getText().length());
edt.addTextChangedListener(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) {
if(!s.toString().startsWith("http://")){
edt.setText("http://");
Selection.setSelection(edt.getText(), edt.getText().length());
}
}
});
As of version 1.2.0-alpha01 of material design library, prefix and suffix is supported for text fields:
<com.google.android.material.textfield.TextInputLayout
app:prefixText="Price: "
app:prefixTextAppearance="..."
app:prefixTextColor="..."
app:suffixText="Dollar"
app:suffixTextColor="..."
app:suffixTextAppearance="...">
<com.google.android.material.textfield.TextInputEditText .../>
</com.google.android.material.textfield.TextInputLayout>
The only downside in my opinion is that the suffix is fixed at the end of the text field and there is no option to make it flow with the input text. You can vote on this issue for that.
That's how you can actually do it with an InputFilter:
final String prefix = "http://"
editText.setText(prefix);
editText.setFilters(new InputFilter[] {
new InputFilter() {
#Override
public CharSequence filter(final CharSequence source, final int start,
final int end, final Spanned dest, final int dstart, final int dend) {
final int newStart = Math.max(prefix.length(), dstart);
final int newEnd = Math.max(prefix.length(), dend);
if (newStart != dstart || newEnd != dend) {
final SpannableStringBuilder builder = new SpannableStringBuilder(dest);
builder.replace(newStart, newEnd, source);
if (source instanceof Spanned) {
TextUtils.copySpansFrom(
(Spanned) source, 0, source.length(), null, builder, newStart);
}
Selection.setSelection(builder, newStart + source.length());
return builder;
} else {
return null;
}
}
}
});
If you also want the prefix to be not selectable you can add the following code.
final SpanWatcher watcher = new SpanWatcher() {
#Override
public void onSpanAdded(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanRemoved(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanChanged(final Spannable text, final Object what,
final int ostart, final int oend, final int nstart, final int nend) {
if (what == Selection.SELECTION_START) {
if (nstart < prefix.length()) {
final int end = Math.max(prefix.length(), Selection.getSelectionEnd(text));
Selection.setSelection(text, prefix.length(), end);
}
} else if (what == Selection.SELECTION_END) {
final int start = Math.max(prefix.length(), Selection.getSelectionEnd(text));
final int end = Math.max(start, nstart);
if (end != nstart) {
Selection.setSelection(text, start, end);
}
}
}
};
editText.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
There was a slight problem with #Rajitha Siriwardena's answer.
It assumes that the entire string except the suffix has been deleted before the suffix is meaning if you have the string
http://stackoverflow.com/
and try to delete any part of http:// you will delete stackoverflow.com/ resulting in only http://.
I also added a check incase the user tries to input before the prefix.
#Override
public void afterTextChanged(Editable s) {
String prefix = "http://";
if (!s.toString().startsWith(prefix)) {
String cleanString;
String deletedPrefix = prefix.substring(0, prefix.length() - 1);
if (s.toString().startsWith(deletedPrefix)) {
cleanString = s.toString().replaceAll(deletedPrefix, "");
} else {
cleanString = s.toString().replaceAll(prefix, "");
}
editText.setText(prefix + cleanString);
editText.setSelection(prefix.length());
}
}
Note: this doesn't handle the case where the user tries to edit the prefix itself only before and after.
Taken from Ali Muzaffar's blog, see the original post for more details.
Use custom EditText View to draw the prefix text and add padding according to the prefix text size:
public class PrefixEditText extends EditText {
private String mPrefix = "$"; // add your prefix here for example $
private Rect mPrefixRect = new Rect(); // actual prefix size
public PrefixEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
mPrefixRect.right += getPaint().measureText(" "); // add some offset
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), getPaint());
}
#Override
public int getCompoundPaddingLeft() {
return super.getCompoundPaddingLeft() + mPrefixRect.width();
}
}
You had it almost right, try
private final String PREFIX="http://";
editText.setFilters(new InputFilter[]{new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int
dend) {
return dstart<PREFIX.length()?dest.subSequence(dstart,dend):null;
}
}});
CODE TO ADD CUSTOM PREFIX TO YOUR EDITTEXT (PREFIX NOT EDITABLE)
Code from Medium by Ali Muzaffar
public class PrefixEditText extends AppCompatEditText {
float originalLeftPadding = -1;
public PrefixEditText(Context context) {
super(context);
}
public PrefixEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PrefixEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
calculatePrefix();
}
private void calculatePrefix() {
if (originalLeftPadding == -1) {
String prefix = (String) getTag();
float[] widths = new float[prefix.length()];
getPaint().getTextWidths(prefix, widths);
float textWidth = 0;
for (float w : widths) {
textWidth += w;
}
originalLeftPadding = getCompoundPaddingLeft();
setPadding((int) (textWidth + originalLeftPadding),
getPaddingRight(), getPaddingTop(),
getPaddingBottom());
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
String prefix = (String) getTag();
canvas.drawText(prefix, originalLeftPadding, getLineBounds(0, null), getPaint());
}
}
And XML
<com.yourClassPath.PrefixEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:textSize="14sp"
android:tag="€ " />
An easy to use Kotlin extension function for this purpose
fun EditText.stickPrefix(prefix: String) {
this.addTextChangedListener(afterTextChanged = {
if (!it.toString().startsWith(prefix) && it?.isNotEmpty() == true) {
this.setText(prefix + this.text)
this.setSelection(this.length())
}
})
}
//someEditText.stickPrefix("+")
I know I'm reviving an old post but I want to share with the community that I have struggled with this topic these days and I found that placing a TextView over the EditText is not only perfectly doable (to respond to the second part of the question), much more in this case when the constant text is needed in the starting position, but preferable, too. Moreover the cursor won't even move before the "mutable" text at all, which is an elegant effect.
I prefer this solution because it doesn't add workload and complexity to my app with listeners and whatsoever.
Here's a sample code of my solution:
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="3dp"
android:text="http://" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="textUri"
android:paddingStart="choose an appropriate padding" />
</RelativeLayout>
By enclosing the views in a RelativeLayout they will be overlapped.
The trick here is playing with the android:paddingStart property of the EditText, to make the text start just right after the TextView, while android:layout_centerVertical="true" and android:layout_marginStart="3dp" properties of the TextView make sure that its text is correctly aligned with text inputted and with the start of the underlying line of the EditText (or at least this happens when using a Material themed one).
I made Kotlin extension function for adding prefix to EditText
fun EditText.addPrefix(prefix: String) {
var text = ""
var isPrefixModified = false
val formattedPrefix = "$prefix "
var lastCharSequence: CharSequence? = null
val setEditText: () -> Unit = {
setText(text)
Selection.setSelection(editableText, text.length)
}
this.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
val newText = editable.toString()
when {
isPrefixModified -> {
isPrefixModified = false
setEditText()
}
isTryingToDeletePrefix(newText) -> {
setEditText()
}
isNewInput(newText) -> {
text = "$formattedPrefix$newText"
setEditText()
}
else -> {
text = newText
}
}
}
override fun beforeTextChanged(charSequence: CharSequence?, start: Int,
count: Int, after: Int) {
charSequence?.let {
if (it != lastCharSequence && it.isNotEmpty() && start <= prefix.length) {
isPrefixModified = true
}
lastCharSequence = charSequence
}
}
override fun onTextChanged(charSequence: CharSequence?, start: Int,
before: Int, count: Int) {
// Ignore
}
private fun isTryingToDeletePrefix(newText: String) =
text.isNotEmpty() && newText.length < text.length && isNewInput(newText)
private fun isNewInput(newText: String) = !newText.contains(formattedPrefix)
})
}
I just found the solution how to make prefix not-editable and how to save text if you try to remove prefix. That's very close to #Rajitha Siriwardena answer. All you missed is to save text before any changes applied. It will be restored in afterTextChanged(...).
Code:
final String prefix = "http://";
editText.setText(prefix);
Selection.setSelection(editText.getText(), editText.getText().length());
editText.addTextChangedListener(new TextWatcher() {
String text;
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
text = charSequence.toString();
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
if (!editable.toString().startsWith(prefix)) {
editText.setText(text);
Selection.setSelection(editText.getText(), editText.getText().length());
}
}
});
This one is basically to add prefix "+91" to your edit text field of phone number.
1.Add this code to oncreate() of activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
// Write other things......//
etPhoneNumber.setFilters(new InputFilter[]{getPhoneFilter(),newInputFilter.LengthFilter(13)});
etPhoneNumber.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
if (etPhoneNumber.getText().toString().isEmpty()) {
etPhoneNumber.setText("+91");
Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length()); }
} else {
if (etPhoneNumber.getText().toString().equalsIgnoreCase("+91")) {
etPhoneNumber.setFilters(new InputFilter[]{});
etPhoneNumber.setText("");
etPhoneNumber.setFilters(new InputFilter[]{getPhoneFilter(),new InputFilter.LengthFilter(13)});
}
}
}
});
}
2.Declare a method called getPhoneFilter()
private InputFilter getPhoneFilter() {
Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());
etPhoneNumber.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
#Override
public void afterTextChanged(Editable s) {
if(!s.toString().startsWith("+91")){
if (etPhoneNumber.getFilters() != null && etPhoneNumber.getFilters().length > 0) {
etPhoneNumber.setText("+91");
Selection.setSelection(etPhoneNumber.getText(), etPhoneNumber.getText().length());
}
}
}
});
// Input filter to restrict user to enter only digits..
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (!String.valueOf(getString(R.string.digits_number)).contains(String.valueOf(source.charAt(i)))) {
return "";
}
}
return null;
}
};
return filter;
}
3.declare "digits_number" in your values file
<string name="digits_number">1234567890+</string>
Based on #demaksee comment. I extend EditText and override function onSelectionChanged. So user even can`t edit prefix. Very simple and useful.
Kotlin:
private var prefix : String? = ""
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
if (prefix != null && prefix!!.isNotBlank()) {
var finalStart = selStart
var finalEnd = selEnd
val prefixLength = prefix!!.length
if (prefixLength > selStart) {
finalStart = prefixLength
}
if (prefixLength > selEnd) {
finalEnd = prefixLength
}
if (finalStart == selStart && finalEnd == selEnd) {
super.onSelectionChanged(finalStart, finalEnd)
} else {
val startWithPrefix = text?.startsWith(prefix ?: "") ?: prefix.isNullOrBlank()
if (!startWithPrefix) {
setText(prefix)
}
setSelection(finalStart, finalEnd)
}
}
}
public fun setPrefix(prefix: String) {
editText.setText(prefix)
editText.setSelection(prefix.length)
this.prefix = prefix
}
Here is a less efficient solution that should handle all cases for when characters OR words are deleted/inserted in OR around the prefix.
prefix = "http://"
extra = "ahhttp://"
differencePrefix(prefix, extra) = "aht"
Code:
public static String differencePrefix(String prefix, String extra) {
if (extra.length() < prefix.length()) return "";
StringBuilder sb = new StringBuilder();
StringBuilder eb = new StringBuilder();
int p = 0;
for (int i = 0; i < extra.length(); i++) {
if (i >= prefix.length()) {
while(p < extra.length()) {
eb.append(extra.charAt(p));
p++;
}
break;
}
if (p >= extra.length()) break;
char pchar = extra.charAt(p);
char ichar = prefix.charAt(i);
while(pchar != ichar) {
//check if char was deleted
int c = i + 1;
if (c < prefix.length()) {
char cchar = prefix.charAt(c);
if (cchar == pchar) {
break;
}
}
sb.append(pchar);
p++;
if (p >= extra.length()) break;
pchar = extra.charAt(p);
}
p++;
}
return eb.toString() + sb.toString();
}
You can use it like this
editText.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) {
String input = s.toString();
if (!input.startsWith(prefix)) {
String extra = differencePrefix(prefix, input);
String newInput = prefix + extra;
editText.setText(newInput);
editText.setSelection(newInput.length());
}
}
});
EditText msg=new EditText(getContext());
msg.setSingleLine(true);
msg.setSingleLine();
msg.setId(View.generateViewId());
msg.measure(0,0);
TextView count=new TextView(getContext());
count.setTextColor(Color.parseColor("#666666"));
count.setText("20");
count.setPadding(0,0,(int)Abstract.getDIP(getContext(),10),0);
count.measure(0,0);
float tenPIX =TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,getContext().getResources().getDisplayMetrics());
msg.setPadding((int)tenPIX,(int)tenPIX,(int)(int)tenPIX+count.getMeasuredWidth(),(int)tenPIX);
RelativeLayout ll1=new RelativeLayout(getContext());
ll1.addView(msg,new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
LayoutParams countlp=new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
countlp.addRule(RelativeLayout.ALIGN_END,msg.getId());
countlp.addRule(RelativeLayout.ALIGN_BASELINE,msg.getId());
ll1.addView(count,countlp);
The code below works for me. It handles cases when the user edits the prefix, deletes it, inserts text from the buffer, changes the selected text. If the user changes the prefix, the focus moves to the end of the prefix.
final String prefix = "http://";
final String[] aLastText = {prefix};
et.setText(prefix);
et.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable sNew) {
if (!sNew.toString().startsWith(prefix)) {
String sLast = aLastText[0];
boolean isRemoving = sNew.length() < sLast.length();
int start;
int end = sNew.length() - 1;
for (start = 0; start < sLast.length() && start < sNew.length(); start++) {
if (sLast.charAt(start) != sNew.charAt(start))
break;
}
int k = sLast.length() - 1;
for (; end >= start && k >= 0; end--, k--) {
if (sLast.charAt(k) != sNew.charAt(end))
break;
}
String sEdited = sNew.toString().substring(start, ++end);
k += isRemoving ? 1 : 0;
k = k < prefix.length() ? prefix.length() : k;
String sSuffix = sLast.substring(k, sLast.length());
et.setText(prefix + sEdited + sSuffix);
et.setSelection(et.getText().length() - sSuffix.length());
}
aLastText[0] = et.getText().toString();
}
});
Examples:
ht5tp://localhost, 5http://localhost, http:/5/localhost -> http://5localhost
http:localhost -> http://localhost
what worked for me is to add some changes on Rajitha Siriwardena code :
First, put text on the Edittext or TextInputEditText xml layout :
android:text="http://"
the purpose is to test the if condition on the first attempt
Second,
test the condition with if like this
#Override
public void afterTextChanged(Editable s) {
if (!s.toString().startsWith("http://")) {
etPhone.setText("http://");
etPhone.setSelection(etPhone.length());
}
I am baffled by the complex answers posted. More easier way will be to add a textview with code as text as a prefix and put some elevation. This way, you will much finer control over the designing of the code("+91"). Here's an example of the same.
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/mobile_bottom_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/card_with_top_radius"
android:elevation="12dp"
android:paddingBottom="#dimen/dim_18"
app:flow_verticalAlign="bottom"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="#+id/code"
style="#style/color_333333_text_14_roboto_regular_venus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/dim_16"
android:text="+91"
android:elevation="1dp"
app:layout_constraintBottom_toBottomOf="#id/number"
app:layout_constraintStart_toStartOf="#id/number"
app:layout_constraintTop_toTopOf="#id/number" />
<com.gradeup.baseM.view.custom.TabletEditText
android:id="#+id/number"
style="#style/color_333333_text_12_roboto_medium_venus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="#dimen/dim_16"
android:layout_marginTop="#dimen/dim_17"
android:layout_marginBottom="#dimen/dim_16"
android:background="#drawable/e6e6e6_4dp_curved_border_white_bg"
android:hint="#string/enter_mobile_number"
android:imeOptions="flagNoExtractUi"
android:inputType="number"
android:maxLength="10"
android:maxLines="1"
android:paddingVertical="#dimen/dim_17"
android:paddingStart="#dimen/dim_50"
android:paddingEnd="20dp"
android:textColorHint="#color/color_999999_8799ae"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/subTitle">
</com.gradeup.baseM.view.custom.TabletEditText>
</androidx.constraintlayout.widget.ConstraintLayout>
Things to keep in mind :-
Adjust editText paddingStart attribute according to your need.
Put some elevation in code TextView.

Custom format edit text input android to accept credit card number

how to make edit text accept input in format
4digitnumber-4dignumber-4dignumber-4dignumber
The code
text.addTextChangedListener(new TextWatcher() {
int len = 0;
String string ;
#Override
public void afterTextChanged(Editable s) {
text.setOnKeyListener(new OnKeyListener()
{ public boolean onKey(View v, int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_DEL)
{
}
else{
string = text.getText().toString();
len = string.length()+1;
if(len%5==0){text.append("-");}
}
return false; } });
}
});
works fine upon adding, but deleting or editing causes problem.
Now this works fine for soft/hard keyboard for all delete/edit ops.
tx 4 ur help..
package com.and;
import android.app.Activity;
import android.app.AlertDialog;
import android.inputmethodservice.KeyboardView;
import android.os.Bundle;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.text.format.Formatter;
import android.text.method.NumberKeyListener;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;
import android.widget.Toast;
public class ccformat extends Activity {
String a;
int keyDel;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText text = (EditText) findViewById(com.and.R.id.editText1);
text.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
boolean flag = true;
String eachBlock[] = text.getText().toString().split("-");
for (int i = 0; i < eachBlock.length; i++) {
if (eachBlock[i].length() > 4) {
flag = false;
}
}
if (flag) {
text.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL)
keyDel = 1;
return false;
}
});
if (keyDel == 0) {
if (((text.getText().length() + 1) % 5) == 0) {
if (text.getText().toString().split("-").length <= 3) {
text.setText(text.getText() + "-");
text.setSelection(text.getText().length());
}
}
a = text.getText().toString();
} else {
a = text.getText().toString();
keyDel = 0;
}
} else {
text.setText(a);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
}
This is working:
public class EditTextSample extends Activity {
// This regexp has to be improved, it does not detect case where you have
// more than 4 digits in a middle group like: 1234-12345-123
static final Pattern CODE_PATTERN = Pattern.compile("([0-9]{0,4})|([0-9]{4}-)+|([0-9]{4}-[0-9]{0,4})+");
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.edit_text_sample);
final EditText editText = (EditText) findViewById(R.id.input);
editText.addTextChangedListener(new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
Log.w("", "input" + s.toString());
if (s.length() > 0 && !CODE_PATTERN.matcher(s).matches()) {
String input = s.toString();
String numbersOnly = keepNumbersOnly(input);
String code = formatNumbersAsCode(numbersOnly);
Log.w("", "numbersOnly" + numbersOnly);
Log.w("", "code" + code);
editText.removeTextChangedListener(this);
editText.setText(code);
// You could also remember the previous position of the cursor
editText.setSelection(code.length());
editText.addTextChangedListener(this);
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
private String keepNumbersOnly(CharSequence s) {
return s.toString().replaceAll("[^0-9]", ""); // Should of course be more robust
}
private String formatNumbersAsCode(CharSequence s) {
int groupDigits = 0;
String tmp = "";
for (int i = 0; i < s.length(); ++i) {
tmp += s.charAt(i);
++groupDigits;
if (groupDigits == 4) {
tmp += "-";
groupDigits = 0;
}
}
return tmp;
}
});
}
}
If you want to just group visually the numbers, but you don't want to alter the value of the EditText adding dashes, you can use this Span approach:
EditText editText = findViewById(R.id.editText);
editText.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 editable) {
Object[] paddingSpans = editable.getSpans(0, editable.length(), DashSpan.class);
for (Object span : paddingSpans) {
editable.removeSpan(span);
}
addSpans(editable);
}
private static final int GROUP_SIZE = 4;
private void addSpans(Editable editable) {
final int length = editable.length();
for (int i = 1; i * (GROUP_SIZE) < length; i++) {
int index = i * GROUP_SIZE;
editable.setSpan(new DashSpan(), index - 1, index,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
});
where the DashSpan class looks like this:
/**
* A {#link ReplacementSpan} used for spacing in {#link android.widget.EditText}
* to space things out. Adds '-'s
*/
public class DashSpan extends ReplacementSpan {
#Override
public int getSize(#NonNull Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
float padding = paint.measureText("-", 0, 1);
float textSize = paint.measureText(text, start, end);
return (int) (padding + textSize);
}
#Override
public void draw(#NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y,
int bottom, #NonNull Paint paint) {
canvas.drawText(text.subSequence(start, end) + "-", x, y, paint);
}
}
This way you will have visually the grouping using the dashes, but the getText() will return the text without any grouping.
To force only numbers you can add the attributes android:digits="0123456789" and android:inputType="number" to the EditText.
This solution is based on the code of this library.
In my case below code is working fine.
editTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher());
Add custom class for TextWatcher.
public class FourDigitCardFormatWatcher implements TextWatcher {
private static final char space = ' ';
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable s) {
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());
}
}
if (s.length() > 0 && (s.length() % 5) == 0) {
char c = s.charAt(s.length() - 1);
if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {
s.insert(s.length() - 1, String.valueOf(space));
}
}
}
}
Hope this would help you.
It works in all cases, when you insert or remove a character, the format will always be right. Make sure you set
android:inputType="number"
/
myEditText.addTextChangedListener(new TextWatcher() {
private final String space = "-"; // you can change this to whatever you want
private final Pattern pattern = Pattern.compile("^(\\d{4}"+space+"{1}){0,3}\\d{1,4}$"); // check whether we need to modify or not
#Override
public void onTextChanged(CharSequence s, int st, int be, int count) {
String currentText = myEditText.getText().toString();
if (currentText.isEmpty() || pattern.matcher(currentText).matches())
return; // no need to modify
String numbersOnly = currentText.trim().replaceAll("[^\\d.]", "");; // remove everything but numbers
String formatted = "";
for(int i = 0; i < numbersOnly.length(); i += 4)
if (i + 4 < numbersOnly.length())
formatted += numbersOnly.substring(i,i+4)+space;
else
formatted += numbersOnly.substring(i);
myEditText.setText(formatted);
myEditText.setSelection(myEditText.getText().toString().length());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void afterTextChanged(Editable e) {}
});
It seems to me the answers presented here do not work properly with delete, delete from the middle operations, etc.
Here is my code. It doesn't restrict the length of input, but seems to be ok with various insertions and deletions:
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
public class HyphenDelimitTextWatcher implements TextWatcher {
EditText mEditText;
boolean mInside = false;
boolean mWannaDeleteHyphen = false;
boolean mKeyListenerSet = false;
final static String MARKER = "|"; // filtered in layout not to be in the string
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(!mKeyListenerSet) {
mEditText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
try {
mWannaDeleteHyphen = (keyCode == KeyEvent.KEYCODE_DEL
&& mEditText.getSelectionEnd() - mEditText.getSelectionStart() <= 1
&& mEditText.getSelectionStart() > 0
&& mEditText.getText().toString().charAt(mEditText.getSelectionEnd() - 1) == '-');
} catch (IndexOutOfBoundsException e) {
// never to happen because of checks
}
return false;
}
});
mKeyListenerSet = true;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mInside) // to avoid recursive calls
return;
mInside = true;
int currentPos = mEditText.getSelectionStart();
String string = mEditText.getText().toString().toUpperCase();
String newString = makePrettyString(string);
mEditText.setText(newString);
try {
mEditText.setSelection(getCursorPos(string, newString, currentPos, mWannaDeleteHyphen));
} catch (IndexOutOfBoundsException e) {
mEditText.setSelection(mEditText.length()); // last resort never to happen
}
mWannaDeleteHyphen = false;
mInside = false;
}
#Override
public void afterTextChanged(Editable s) {
}
private String makePrettyString(String string) {
String number = string.replaceAll("-", "");
boolean isEndHyphen = string.endsWith("-") && (number.length()%4 == 0);
return number.replaceAll("(.{4}(?!$))", "$1-") + (isEndHyphen ?"-":"");
}
private int getCursorPos(String oldString, String newString, int oldPos, boolean isDeleteHyphen) {
int cursorPos = newString.length();
if(oldPos != oldString.length()) {
String stringWithMarker = oldString.substring(0, oldPos) + MARKER + oldString.substring(oldPos);
cursorPos = (makePrettyString(stringWithMarker)).indexOf(MARKER);
if(isDeleteHyphen)
cursorPos -= 1;
}
return cursorPos;
}
public HyphenDelimitTextWatcher(EditText editText) {
mEditText = editText;
}
}
Usage:
mSomeEditText.addTextChangedListener(new HyphenDelimitTextWatcher(mSomeEditText));
if you neeed this efect,ou can use this code in EditText
Here is a formatting regex used to show card details in format XXXX XXXX XXXX XXXX
etCreditCardNumber.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) {
etCreditCardNumber.setFloatingLabel(MaterialEditText.FLOATING_LABEL_HIGHLIGHT);
String initial = s.toString();
// remove all non-digits characters
String processed = initial.replaceAll("\\D", "");
// insert a space after all groups of 4 digits that are followed by another digit
processed = processed.replaceAll("(\\d{4})(?=\\d)(?=\\d)(?=\\d)", "$1 ");
//Remove the listener
etCreditCardNumber.removeTextChangedListener(this);
int index = etCreditCardNumber.getSelectionEnd();
if (index == 5 || index == 10 || index == 15)
if (count > before)
index++;
else
index--;
//Assign processed text
etCreditCardNumber.setText(processed);
try {
etCreditCardNumber.setSelection(index);
} catch (Exception e) {
e.printStackTrace();
etCreditCardNumber.setSelection(s.length() - 1);
}
//Give back the listener
etCreditCardNumber.addTextChangedListener(this);
}
#Override
public void afterTextChanged(Editable s) {
}
});

Live editing of users input

Is it possible to auto insert characters into an EditText as the user inputs data?
I.e. if the user is entering a long number such as 123456789012, is it possible for this number to appear as he is typing it in the edit text box, but with a dash every 4th character?
So as you type the number above you would see it being entered in the EditText box but would look like this: 1234-5678-9012.
Currently I have an app where you can enter a long number and then press a button and it inserts the dashes for you, but I'm curious if it could be done as you type?
Many thanks for any help.
By tagging android, I think you are discussing about android editText, is so you can do it by listening the TextChangedListener,
EDITED: for backspace
editText.addTextChangedListener(new TextWatcher() {
int len=0;
#Override
public void afterTextChanged(Editable s) {
String str = editText.getText().toString();
if(str.length()==4&& len <str.length()){//len check for backspace
editText.append("-");
}
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
String str = editText.getText().toString();
len = str.length();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
});
to solve this issue, i write a class "AutoAddTextWatcher" :
1. Auto insert text into EditText.
2. insert text into EditText at positions you are setted.
3. delete text in EditText at positions you are setted, when text length bigger than 1.
code snippet :
mEditText_birthday.addTextChangedListener(new AutoAddTextWatcher(mEditText_birthday,
"/",
new TextWatcher() {},
4, 6));
AutoAddTextWatcher class
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
/**
* Created by henry.chuang on 2016/5/12.
*/
public class AutoAddTextWatcher implements TextWatcher {
private CharSequence mBeforeTextChanged;
private TextWatcher mTextWatcher;
private int[] mArray_pos;
private EditText mEditText;
private String mAppentText;
public AutoAddTextWatcher(EditText editText, String appendText, int... position){
this.mEditText = editText;
this.mAppentText = appendText;
this.mArray_pos = position.clone();
}
public AutoAddTextWatcher(EditText editText, String appendText, TextWatcher textWatcher, int... position){
this(editText, appendText, position);
this.mTextWatcher = textWatcher;
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mBeforeTextChanged = s.toString();
if(mTextWatcher != null)
mTextWatcher.beforeTextChanged(s, start, count, after);
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
for (int i = 0; i < mArray_pos.length; i++) {
if(((mBeforeTextChanged.length() - mAppentText.length() * i) == (mArray_pos[i] - 1) &&
(s.length() - mAppentText.length() * i) == mArray_pos[i])){
mEditText.append(mAppentText);
break;
}
if(((mBeforeTextChanged.length() - mAppentText.length() * i) == mArray_pos[i] &&
(s.length() - mAppentText.length() * i) == (mArray_pos[i] + 1))){
int idx_start = mArray_pos[i] + mAppentText.length() * i;
int idx_end = Math.min(idx_start + mAppentText.length(), s.length());
String sub = mEditText.getText().toString().substring(idx_start, idx_end);
if(!sub.equals(mAppentText)){
mEditText.getText().insert(s.length() - 1, mAppentText);
}
break;
}
if(mAppentText.length() > 1 &&
(mBeforeTextChanged.length() - mAppentText.length() * i) == (mArray_pos[i] + mAppentText.length()) &&
(s.length() - mAppentText.length() * i) == (mArray_pos[i] + mAppentText.length() - 1)){
int idx_start = mArray_pos[i] + mAppentText.length() * i;
int idx_end = Math.min(idx_start + mAppentText.length(), s.length());
mEditText.getText().delete(idx_start, idx_end);
break;
}
}
if(mTextWatcher != null)
mTextWatcher.onTextChanged(s, start, before, count);
}
#Override
public void afterTextChanged(Editable s) {
if(mTextWatcher != null)
mTextWatcher.afterTextChanged(s);
}
}
complete demo source :
https://github.com/henrychuangtw/AutoInsertEditText
#Override
public void afterTextChanged(Editable s) {
if(s.length() == 3 && len < s.length()){
s.append(" - ");
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
len = s.length();
}
This will do as well, only this code will insert " - " after 3rd character.
This is what I used
private boolean mInEdit;
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!mInEdit) {
mInEdit = true;
String delimiter = " - ";
//Remove chars from your delimiter first
String digits = s.toString().replaceAll("[- ]", "")
.replaceAll("\\d{4}", "$0" + delimiter);
//Handle deletion
int dLength = delimiter.length();
if (before > count && digits.endsWith(delimiter.charAt(dLength - 1)) {
digits = digits.substring(0, digits.length() - dLength);
}
mCardNumber.setText(digits);
mCardNumber.setSelection(mCardNumber.length());
mInEdit = false;
}
}
Here you replace delimiter with what you want to separate digits.
For those still facing trouble with backspace and multiple hyphens -
new TextWatcher()
{
boolean hyphenExists;
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (s.length() >= 6 && s.charAt(5) == '-') {
hyphenExists = true;
} else {
hyphenExists = false;
}
Log.d("TAG", "beforeTextChanged " + s.toString());
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("TAG", "onTextChanged " + s.toString());
}
#Override
public void afterTextChanged(Editable s) {
if (s.length() == 5) {
if (!hyphenExists)
s.append('-');
}
Log.d("TAG", "afterTextChanged " + s.toString());
}
}
You can achieve this with on text changed
in my case, i have to format input like this : xxx xxx-xxxx
i done as given below:
etMobileNumber.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (etMobileNumber.text.length == 3 && count != 0) {
val text = etMobileNumber.getText().toString() + " "
etMobileNumber.setText(text)
etMobileNumber.setSelection(text.length)
} else if (etMobileNumber.text.length == 7 && count != 0) {
val text = etMobileNumber.getText().toString() + "-"
etMobileNumber.setText(text)
etMobileNumber.setSelection(text.length)
}
}
})
and the result is very dynamic on the go while typing.
input- 1234567890
result - 123 456-7890

Categories

Resources