Set unchangeable some part of editText android-ThrowExceptions

Exception or error:

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 numbers. Also +374 must be unchangeable, user can’t change or remove it. So is there some kind of ways for doing this?enter image description here

EDIT:

I don’t want to use textView or another view with this text and put it left of the ediText. I want to find some way with less operations. I need text to be frozen not to check every text changes or add missing text when user will delete some part of it.

How to solve:

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

enter image description here

###

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());

            }

        }

    });
}

}

###

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 ");

###

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

###

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())
        }
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *