Related
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.
For example, I have EditText with length limitation of two characters. When the first and second letters entered it's ok. But when we will try to enter a third letter the first letter should be replaced with it. Next letter should replace the second and so on in a circle. How can I do this one.
Try using TextWatcher on your edit text to achieve the goal
editText.addTextChangedListener(new TextWatcher() {
private int lastModifiedIndex = 1;
#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() > 2) {
char toReplace = s.charAt(s.length() - 1);
if (lastModifiedIndex == 0) {
editText.setText("" + s.charAt(lastModifiedIndex) + toReplace);
lastModifiedIndex = 1;
editText.setSelection(s.length());
} else {
editText.setText("" + toReplace + s.charAt(lastModifiedIndex));
lastModifiedIndex = 0;
editText.setSelection(s.length());
}
} else {
lastModifiedIndex = 1;
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
Try this one
editText.addTextChangedListener(new TextWatcher() {
private int charLimit = 5;
private int position = 5;
private String newSequence;
#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() > charLimit ) {
if (position == charLimit) {
newSequence = s.subSequence(s.length()-1, s.length()).toString() +
s.subSequence(1, charLimit);
position = 1;
} else {
position++;
newSequence = s.subSequence(0, position).toString() +
s.subSequence(position+1, s.length());
}
editText.setText(null);
editText.setText(newSequence);
editText.setSelection(position);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
I want to implement algorithm like this :
As soon as user starts entering numbers for "MM" , it should give "/" after the user enters two digits of "MM" and then , it should let only enter last two digits for "yy". How can I achieve this kind of functionality?
android:maxLength="5"
And set as phone to editText so that it can display "/"
android:inputType="phone"
on the textWatcher do this .... I will validate later for the correct mm/yy month year to avoid users input data like 55/66 max should be 12/31 but it should be validated for months with less or 30 days...
#Override
public void afterTextChanged(Editable editable) {
if (editable.length() > 0 && (editable.length() % 3) == 0) {
final char c = editable.charAt(editable.length() - 1);
if ('/' == c) {
editable.delete(editable.length() - 1, editable.length());
}
}
if (editable.length() > 0 && (editable.length() % 3) == 0) {
char c = editable.charAt(editable.length() - 1);
if (Character.isDigit(c) && TextUtils.split(editable.toString(), String.valueOf("/")).length <= 2) {
editable.insert(editable.length() - 1, String.valueOf("/"));
}
}
}
I just implement a method like Uber credit card date format with auto check, autocomplete, auto move to CVV edittext.
<EditText
android:id="#+id/credit_card_expire_et"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="#dimen/activity_horizontal_margin"
android:layout_weight="1"
android:hint="MM/YY"
android:inputType="number"
android:maxLength="5"
android:maxLines="1" />
<EditText
android:id="#+id/credit_card_cvv_et"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/activity_horizontal_margin"
android:layout_weight="1"
android:hint="CVV"
android:inputType="number"
android:maxLength="3"
android:maxLines="1" />
.
#BindView(R.id.credit_card_expire_et)
EditText creditExpireEt;
#BindView(R.id.credit_card_cvv_et)
EditText creditCVVEt;
#OnTextChanged(value = R.id.credit_card_expire_et, callback = BEFORE_TEXT_CHANGED)
void beforeExpireEtChanged() {
previousLength = creditExpireEt.getText().toString().length();
}
#OnTextChanged(R.id.credit_card_expire_et)
void autoFixAndMoveToNext() {
int length = creditExpireEt.getText().toString().trim().length();
if (previousLength <= length && length < 3) {
int month = Integer.parseInt(creditExpireEt.getText().toString());
if (length == 1 && month >= 2) {
String autoFixStr = "0" + month + "/";
creditExpireEt.setText(autoFixStr);
creditExpireEt.setSelection(3);
} else if (length == 2 && month <= 12) {
String autoFixStr = creditExpireEt.getText().toString() + "/";
creditExpireEt.setText(autoFixStr);
creditExpireEt.setSelection(3);
} else if (length ==2 && month > 12) {
creditExpireEt.setText("1");
creditExpireEt.setSelection(1);
}
} else if (length == 5) {
creditCVVEt.requestFocus(); // auto move to next edittext
}
}
EditText et_expiry_date= (EditText) findViewById(R.id.et_expiry_date);
et_expiry_date.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) {
String current = s.toString();
if (current.length() == 2 && start == 1) {
et_expiry_date.setText(current + "/");
et_expiry_date.setSelection(current.length() + 1);
}
else if (current.length() == 2 && before == 1) {
current = current.substring(0, 1);
et_expiry_date.setText(current);
et_expiry_date.setSelection(current.length());
}
}
#Override
public void afterTextChanged(Editable s) {}
});
First set the max amount of characters of the EditText to 5, like this:
android:maxLength="5"
And set as numbers editText
android:inputType="number"
Then add an onEditTextChangedListener to the EditText to detect if the amount of characters is changed to two and did not change from three to two and remove "/" if a number before "/" is removed:
edit_text.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(p0: CharSequence?, start: Int, removed: Int, added: Int) {
if (start == 1 && start+added == 2 && p0?.contains('/') == false) {
edit_text.setText(p0.toString() + "/")
} else if (start == 3 && start-removed == 2 && p0?.contains('/') == true) {
edit_text.setText(p0.toString().replace("/", ""))
}
}
})
private static final String EXP_DATE_REGAX = "(0[1-9]|1[0-2])[0-9]{2}";
if (!edt_expiry_date.getText().toString().isEmpty()) {
Pattern pattern = Pattern.compile(EXP_DATE_REGAX);
Matcher matcher = pattern.matcher(edt_expiry_date.getText().toString());
if(!matcher.find()){
edt_expiry_date.setError("Invalid Expiry Date");
}else{
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR) % 100;
int month = c.get(Calendar.MONTH)+1;
if(Integer.parseInt((edt_expiry_date.getText().toString().substring(2)))>=year){
if(Integer.parseInt((edt_expiry_date.getText().toString().substring(2)))==year){
if(Integer.parseInt((edt_expiry_date.getText().toString().substring(0,2)))>=month){
str_expiry_date = edt_expiry_date.getText().toString();
dialog.dismiss();
}else{
edt_expiry_date.setError("Invalid Expiry Date");
}
}else{
str_expiry_date = edt_expiry_date.getText().toString();
dialog.dismiss();
}
}else{
edt_expiry_date.setError("Invalid Expiry Date");
}
// dialog.dismiss();
}
} else {
edt_expiry_date.setError("Enter Expiry Date");
}
add these on youredittext.addTextChangedListener
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() == 2) {
if(start==2 && before==1 && !s.toString().contains("/")){
youredittext.setText(""+s.toString().charAt(0));
youredittext.setSelection(1);
}
else {
youredittext.setText(s + "/");
youredittext.setSelection(3);
}
}
}
The issues and string manipulation needed with text based date entry just doesn't seem worth it here. What happens if a user goes back to the beginning of the string and starts typing? What happens if a user starts entering the wrong format? You have to account for a ton of cases without having a very good way of controlling what the user can input. In these instances, I'd just use a DatePicker like the one in this solution.
You can keep the EditText if you have your heart set on it, and just set the onClickListener to bring up the DatePicker when selected. I recommend setting these two fields on it though:
android:cursorVisible="false"
android:focusable="false"
This way the EditText won't look like it can be typed in. My only suggestion or addition to the other answer would be a "Clear" button so the EditText value can be wiped if needed. To do so, just add it via the neutral button like so:
builder.setView(dialog)
// Add action buttons
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int id) {
listener.onDateSet(null, yearPicker.getValue(), monthPicker.getValue(), 0);
}
})
.setNeutralButton(R.string.clear, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
//check for a month value of -1 in onDateSet to clear current field
listener.onDateSet(null, -1, -1, 0);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
MonthYearPickerFragment.this.getDialog().cancel();
}
});
Then to check and see if the field should be cleared, just do something like this:
#Override
public void onDateSet(DatePicker datePicker, int i, int i1, int i2) {
if (i > -1){
et.setText(i1 + "/" + i);
} else {
et.setText("");
}
}
In Kotlin for the format like
"MM/YY"
you can use this,
et_expire_date.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
var working = s.toString()
var isValid = true
if (working.length == 2 && before == 0) {
if (Integer.parseInt(working) < 1 || Integer.parseInt(working) > 12) {
isValid = false
} else {
working += "/"
et_expire_date.setText(working)
et_expire_date.setSelection(working.length)
}
} else if (working.length == 5 && before == 0) {
val enteredYear = working.substring(3)
val currentYear = Calendar.getInstance().get(Calendar.YEAR) % 100//getting last 2 digits of current year i.e. 2018 % 100 = 18
if (Integer.parseInt(enteredYear) < currentYear) {
isValid = false
}
} else if (working.length != 5) {
isValid = false
}
if (!isValid) {
et_expire_date.error = getString(R.string.enter_valid_date_mm_yy)
} else {
et_expire_date.error = null
}
}
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
})
where et_expire_date is edittext for date input
Use TextWatcher, example:
EditText editText = (EditText) findViewById(R.id.yourEditTextId);
yourEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// Do what you want here
String myStr = s.toString();
// Check if MM and do what you want
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
Kotlin version of #Goodlife
override fun afterTextChanged(s: Editable?) {
if (s!!.isNotEmpty() && (s.length % 3) == 0) {
val c = s[s.length-1]
if (c == '/') {
s.delete(s.length-1, s.length)
}
}
if (s.isNotEmpty() && (s.length % 3) == 0) {
val c = s[s.length-1]
if (Character.isDigit(c) && TextUtils.split(s.toString(), "/").size <= 2) {
s.insert(s.length-1, "/")
}
}
}
Simple Answer, set length 5 for mm/yy or 7 for mm/yyyy
YourEditText.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 len=s.toString().length();
if (before == 0 && len == 2)
YourEditText.append("/");
}
#Override
public void afterTextChanged(Editable s) {
}
});
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.
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