微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用占位符填充edittext直到未填充

如何解决如何使用占位符填充edittext直到未填充

我遇到了以下问题:我需要针对应将电话输入 EditText 的情况实施解决方案。这个手机应该有不可拆卸的部分,最后四个数字应该在开头用下划线填充,然后当用户输入它们时下划线应该改为数字,例如:

+12345____ -> typing 6 -> +123456___

我实现了不可拆卸部分。这是我的做法:

binding.etPhoneNumber.filters = arrayOf(InputFilter.LengthFilter(args.phoneNumber?.length ?: 0))

binding.etPhoneNumber.doAfterTextChanged {
            val symbolsLeft = it?.toString()?.length ?: 0
            if (symbolsLeft < phoneNumberUi.length) {
                binding.etPhoneNumber.setText(phoneNumberUi)
                binding.etPhoneNumber.setSelection(symbolsLeft + 1)
            }
        }

但是现在我不明白,如何处理带有下划线的逻辑。我尝试在 doAfterTextChanged 中附加下划线,例如 ((args.phoneNumber?.length ?: 0) > (it?.toString()?.length ?: 0)) 附加 n 个下划线,其中 n 是长度之间的差,但在这种情况下,我无法添加新符号,因为 EditText 已填充并且下划线不会被删除。那么,我该如何解决这个问题呢?在此先感谢您的帮助!

解决方法

您可以删除 LengthFilter 并检查 doAfterTextChanged 中的长度:

    val phoneNumberUi = "+12345"
    val length = 10

    binding.etPhoneNumber.doAfterTextChanged {
        when {
            it == null -> {
            }
            // missing / incomplete prefix
            it.length < phoneNumberUi.length -> {
                it.replace(0,it.length,phoneNumberUi)
            }
            // prefix was edited
            !it.startsWith(phoneNumberUi) -> {
                it.replace(0,phoneNumberUi.length,phoneNumberUi)
            }
            // too short
            it.length < length -> {
                it.append("_".repeat(length - it.length))
            }
            // too long
            it.length > length -> {
                it.replace(length,"")
            }
            // set the cursor at the first _
            it.indexOf("_") >= 0 -> {
                binding.etPhoneNumber.setSelection(it.indexOf("_"))
            }
        }
    }

注意:这使用了 when,因为每次更改都会立即触发对 doAfterTextChanged 的递归调用

,

这种方法有以下条件分支

  • 用户添加输入的地方(不可移动部分或可变部分)
  • 输入的字符(数字或退格)

并通过在 onTextChanged() 及其索引(第二个参数)中获取输入的字符(数字/退格符)并根据它们的值设置新的 EditText 值来工作。

EditText 的值也由 currentText 变量跟踪。这样我们一次只能替换用户输入的一个字符,避免操作整个文本的负担。

您可以通过代码在下面的注释中找到其余的解释:

attachTextWatcher(findViewById(R.id.edittext))

fun attachTextWatcher(editText: EditText) {

    // set the cursor to the first underscore
    editText.setSelection(editText.text.indexOf("_"))

    var currentText = editText.text.toString() // which is "+12345____"

    val watcher: TextWatcher = object : TextWatcher {

        override fun onTextChanged(
            s: CharSequence,newCharIndex: Int,// "newCharIndex" is the index of the new entered char
            before: Int,count: Int
        ) {

            // New entered char by the user that triggers the TextWatcher callbacks
            val newChar = s.subSequence(newCharIndex,newCharIndex + count).toString().trim()

            /* Stop the listener in order to programmatically
            change the EditText Without triggering the TextWatcher*/
            editText.removeTextChangedListener(this)

            // Setting the new text of the EditText upon examining the user input
            currentText =
                if (newChar.isEmpty()) { // User entered backspace to delete a char
                    if (newCharIndex in 0..5) { // The backspace is pressed in the non-removable part
                        "+12345" + currentText.substring(6)

                    } else { // The backspace is pressed in the changeable part
                        val sb = StringBuilder(currentText)
                        // replace the the number at which backspace pressed with underscore
                        sb.setCharAt(newCharIndex,'_')
                        sb.toString()
                    }

                } else { // User entered a number
                    if (newCharIndex in 0..5) { // The number is entered in the non-removable part
                        // replace the first underscore with the entered number
                        val sb = StringBuilder(currentText)
                        sb.setCharAt(sb.indexOf("_"),newChar[0])
                        sb.toString()

                    } else { // The number is entered in the changeable part
                        if (newCharIndex < 10) { // Avoid ArrayOutOfBoundsException as the number length should not exceed 10
                            val sb = StringBuilder(currentText)
                            // replace the the number at which the number is entered with the new number
                            sb.setCharAt(newCharIndex,newChar[0])
                            sb.toString()
                        } else currentText
                    }
                }

            // Set the adjusted text to the EditText
            editText.setText(currentText)

            // Set the current cursor place
            if (editText.text.contains("_"))
                editText.setSelection(editText.text.indexOf("_"))
            else
                editText.setSelection(editText.text.length)

            // Re-add the listener,so that the EditText can intercept the number by the user
            editText.addTextChangedListener(this)
        }

        override fun beforeTextChanged(s: CharSequence,start: Int,count: Int,after: Int) {
        }

        override fun afterTextChanged(s: Editable) {
        }
    }

    editText.addTextChangedListener(watcher)
}

这是我正在测试的布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/edittext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"
        android:maxLength="11"
        android:text="+12345____"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

注意:确保在配置更改时保留 currentText 的值。

预览

,

我认为 PhúcNguyễn 有一个好主意,将 TextViewEditText 结合以生成您正在寻找的内容。您可以将它们作为单独的字段放置在布局中或将它们放置在复合视图中。无论哪种方式,效果都是一样的,你可以达到你想要的。

您已经知道如何处理字段开头的静态文本。我在下面展示的是如何处理下划线,以便输入的字符看起来覆盖下划线。

对于演示,我在自定义 EditText 旁边放置了一个带有静态文本的 TextView。真正令人感兴趣的是自定义 EditText。在自定义视图中,onDraw() 函数被覆盖以将下划线作为背景的一部分写入。尽管这些下划线会像字段中的任何其他字符一样出现,但不能以任何方式选择、删除、跳过或操纵它们,除非在用户键入时,下划线会被逐一覆盖。自定义视图的结束填充被操纵为下划线和文本提供空间。

enter image description here

这是自定义视图:

EditTextFillInBlanks.kt

class EditTextFillInBlanks @JvmOverloads constructor(
    context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : androidx.appcompat.widget.AppCompatEditText(context,attrs,defStyleAttr) {

    // Right padding before we manipulate it
    private var mBaseRightPadding = 0

    // Width of text that has been entered
    private var mTextWidth = 0f

    // Mad length of data that can be entered in characters
    private var mMaxLength = 0

    // The blanks (underscores) that we will show
    private lateinit var mBlanks: String

    // MeasureSpec for measuring width of entered characters.
    private val mUnspecifiedWidthHeight = MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED)

    init {
        mBaseRightPadding = paddingRight
        doOnTextChanged { text,_,_ ->
            measure(mUnspecifiedWidthHeight,mUnspecifiedWidthHeight)
            mTextWidth = measuredWidth.toFloat() - paddingStart - paddingEnd
            updatePaddingForBlanks(text)
        }
        setText("",BufferType.EDITABLE)
    }

    /*
        Make sure that the end padding is sufficient to hold the blanks that we are showing.
        The blanks (underscores) are written into the expanded padding.
     */
    private fun updatePaddingForBlanks(text: CharSequence?) {
        if (mMaxLength <= 0) {
            mMaxLength = determineMaxLen()
            check(mMaxLength > 0) { "Maximum length must be > 0" }
        }
        text?.apply {
            val blanksCount = max(0,mMaxLength - length)
            mBlanks = "_".repeat(blanksCount).apply {
                updatePadding(right = mBaseRightPadding + paint.measureText(this).toInt())
            }
        }
    }

    /*
        Draw the underscores on the canvas. They will appear as characters in the field but
        cannot be manipulated by the user.
     */
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        if (mBlanks.isNotEmpty()) {
            canvas?.withSave {
                drawText(mBlanks,paddingStart + mTextWidth,baseline.toFloat(),paint)
            }
        }
    }

    fun setMaxLen(maxLen: Int) {
        mMaxLength = maxLen
    }

    private fun determineMaxLen(): Int {
        // Before Lollipop,we can't get max for InputFilter.LengthFilter
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return 0

        return filters.firstOrNull { it is InputFilter.LengthFilter }
            ?.let {
                it as InputFilter.LengthFilter
                it.max
            } ?: 0
    }
}

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout 
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/holo_blue_light"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:paddingStart="8dp"
        android:paddingTop="8dp"
        android:text="+12345"
        android:textColor="@android:color/black"
        android:textSize="36sp"
        app:layout_constraintBaseline_toBaselineOf="@id/editableSuffix"
        app:layout_constraintEnd_toStartOf="@+id/editableSuffix"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toStartOf="@+id/guideline2" />

    <com.example.edittextwithblanks.EditTextFillInBlanks
        android:id="@+id/editableSuffix"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/edittext_background"
        android:inputType="number"
        android:maxLength="@integer/blankFillLen"
        android:paddingTop="8dp"
        android:paddingEnd="8dp"
        android:textColor="@android:color/black"
        android:textSize="36sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/textView"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="____">

        <requestFocus />
    </com.example.edittextwithblanks.EditTextFillInBlanks>

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guideline2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_begin="92dp" />

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val mStaticStart = "+12345"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (BuildConfig.VERSION_CODE < Build.VERSION_CODES.P) {
            val maxLen = resources.getInteger(R.integer.blankFillLen)
            findViewById<EditTextFillInBlanks>(R.id.editableSuffix).setMaxLen(maxLen)
        }
    }
}

很可能您可以将静态文本处理合并到自定义视图中以获得完整的解决方案。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。