I have some EditText for mobile number input. App must add unique text for every country. For example for Armenia is must add +374
, and user must fill other nu
Use Text watcher and when user enters +3 it completes with +374 and it compares the first four characters and if it is same disable the back press of the softkeyboard so that it will not change the text and enters the other digit which append for the text used from the textwatcher
Create a custom drawable class that will help to convert text into drawable.
public class TextDrawable extends Drawable {
private final String text;
private final Paint paint;
public TextDrawable(String text) {
this.text = text;
this.paint = new Paint();
paint.setColor(Color.BLACK);
paint.setTextSize(16f);
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.LEFT);
}
@Override
public void draw(Canvas canvas) {
canvas.drawText(text, 0, 6, paint);
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Then set the drawable to left of the edittext as
EditText et = (EditText)findViewById(R.id.editText1);
String code = "+374";
et.setCompoundDrawablesWithIntrinsicBounds(new TextDrawable(code), null, null, null);
et.setCompoundDrawablePadding(code.length()*10);
Where the edittext is defined in the layout file as
<EditText
android:id="@+id/editText1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:ems="10" >
<requestFocus />
</EditText>
Final Output looks like
you can create class extend AppCompatEditText or EditText.
public class CEditTextMaster extends AppCompatEditText {
private boolean isNeedNoChangeSomeCharacters;
private String charactersNoChange;
public CEditTextMaster(Context context) {
super(context);
init(context, null);
}
public CEditTextMaster(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CEditTextMaster(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public void init(Context context, @Nullable AttributeSet attrs) {
if (isInEditMode())
return;
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 (isNeedNoChangeSomeCharacters && charactersNoChange != null) {
if (!getText().toString().startsWith(charactersNoChange)) {
removeTextChangedListener(this);
if (charactersNoChange.length() > s.length()) {
setText(charactersNoChange);
} else {
setText(charactersNoChange + getText());
}
setSelection(getText().toString().length());
addTextChangedListener(this);
}
}
}
});
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (isNeedNoChangeSomeCharacters && charactersNoChange != null) {
if (length() > charactersNoChange.length() && selStart < charactersNoChange.length()) {
setSelection(charactersNoChange.length(),selEnd);
}
}
}
@Override
public void setText(CharSequence text, BufferType type) {
super.setText(text, type);
if (isNeedNoChangeSomeCharacters && charactersNoChange != null) {
if (!getText().toString().trim().startsWith(charactersNoChange)) {
setText(charactersNoChange + getText());
}
}
}
public void setCharactersNoChangeInitial(String charactersNoChange) {
isNeedNoChangeSomeCharacters = true;
this.charactersNoChange = charactersNoChange;
if (!getText().toString().trim().startsWith(charactersNoChange)) {
setText(getText());
}
}
}
for use:
edt.setCharactersNoChangeInitial("+734 ");
I opted for a solution, similar to another one submitted, of extending EditText
and modifying onSelectionChanged
. This prevents the user from even entering that region.
Here's what I'm using:
import android.content.Context
import android.text.Editable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatEditText
class PrefixEditText(context: Context, attrs: AttributeSet?) : AppCompatEditText(context, attrs) {
private var prefix: String? = null
fun setPrefix(prefix: String) {
this.prefix = prefix
setText(prefix)
addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
override fun afterTextChanged(s: Editable?) {
// Block deleting the prefix
if (s?.startsWith(prefix) == false) {
setText(prefix)
}
}
})
}
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
var newStart = selStart
var newEnd = selEnd
prefix?.length?.let {
newStart = if (selStart < it) it else selStart
newEnd = if (selEnd < it) it else selEnd
}
if (selStart != newStart || selEnd != newEnd) {
setSelection(newStart, newEnd)
} else {
super.onSelectionChanged(selStart, selEnd)
}
}
// Here to conform to EditText's API
// Could also just override getText()
fun getPostText(): Editable? {
return prefix?.let {
Editable.Factory.getInstance().newEditable(text)?.delete(0, it.length)
} ?: run {
text
}
}
// Here for convenience, to avoid instantiating a new Editable, if the text is all you need
fun getPostCharSeq(): CharSequence? {
return prefix?.let {
text?.substring(it.length)
} ?: run {
text
}
}
}
With tests:
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import com.roosterteeth.roosterteeth.TestActivity
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class PrefixEditTextTest {
@Rule
@JvmField
var activityRule: ActivityTestRule<TestActivity> = ActivityTestRule(TestActivity::class.java, true, true)
private fun setupView(prefix: String, message: String): PrefixEditText {
val editText = PrefixEditText(activityRule.activity, null)
val lp = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
activityRule.activity.addContentView(editText, lp)
editText.setPrefix(prefix)
editText.append(message)
return editText
}
@Test
fun testSelection() {
activityRule.runOnUiThread {
val prefix = "pre: "
val message = "hello world"
val editText = setupView(prefix, message)
editText.setSelection(0)
Assert.assertEquals(editText.selectionStart, prefix.length)
Assert.assertEquals(editText.selectionEnd, prefix.length)
editText.setSelection(0, editText.length())
Assert.assertEquals(editText.selectionStart, prefix.length)
Assert.assertEquals(editText.selectionEnd, editText.length())
}
}
@Test
fun testGetPostText() {
activityRule.runOnUiThread {
val prefix = "pre: "
val message = "hello world"
val editText = setupView(prefix, message)
Assert.assertEquals(message, editText.getPostText().toString())
// This test is after to make sure that getting the post text did not actually modify the contents
Assert.assertEquals("pre: $message", editText.text.toString())
}
}
@Test
fun testGetPostCharSeq() {
activityRule.runOnUiThread {
val prefix = "pre: "
val message = "hello world"
val editText = setupView(prefix, message)
Assert.assertEquals(message, editText.getPostCharSeq())
// This test is after to make sure that getting the post text did not actually modify the contents
Assert.assertEquals("pre: $message", editText.text.toString())
}
}
}
public class MainActivity extends Activity {
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (EditText) findViewById(R.id.editText1);
editText.setText("+374");
Selection.setSelection(editText.getText(), editText.getText().length());
editText.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("+374")) {
editText.setText("+374");
Selection.setSelection(editText.getText(), editText
.getText().length());
}
}
});
}
}