Set unchangeable some part of editText android

前端 未结 5 851
青春惊慌失措
青春惊慌失措 2020-11-30 01:03

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

5条回答
  •  有刺的猬
    2020-11-30 01:50

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

提交回复
热议问题