android 的 EditText 的动态前导文本

2022-01-14 00:00:00 android kotlin java android-edittext

我已经研究过使用 TextWatcherInputFilter,但我不太确定如何解决这个问题.

I've looked into using a TextWatcher and an InputFilter, but I'm not too sure how to approach this problem.

这个想法是有一个 EditText 从右到左插入文本.随着使用输入的变化,我希望发生以下情况.

The idea is to have an EditText that inserts text from right to left. As the use input changes, I would like the following to occur.

- User enters "1" -> Text formats as 00:01
- User enters "2" -> Text formats as 00:12
- User enters "8" -> Text formats as 01:28

我该如何处理?Inputfilter 似乎用于排除文本并在 TextWatcher 内使用 setText 似乎在无限循环中运行.

How could I approach this? Inputfilter seems to be for excluding text and using setText inside the TextWatcher appears to run in an endless loop.

推荐答案

所以我昨天想出了一个相当不错的解决方案.

So I came up with a pretty decent solution yesterday.

我使用了两个编辑文本和一个文本视图.我在约束布局中对齐它们

I used two edit texts and a text view. I Aligned them within a constraint layout

        <TextView
            android:id="@+id/colon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text=":"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />


        <EditText
            android:id="@+id/hoursInput"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:inputType="number"
            android:maxLength="3"
            android:text="00"
            app:layout_constraintBottom_toBottomOf="@id/colon"
            app:layout_constraintEnd_toStartOf="@id/colon"
            app:layout_constraintTop_toTopOf="@id/colon"
            app:layout_constraintVertical_bias="0.0" />

        <EditText
            android:id="@+id/minutesInput"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="end"
            android:inputType="number"
            android:maxLength="3"
            android:text="00"
            app:layout_constraintBottom_toBottomOf="@id/colon"
            app:layout_constraintStart_toEndOf="@id/colon"
            app:layout_constraintTop_toTopOf="@id/colon" />

因为我只想使用数字,所以我不得不使用文本视图来表示冒号.修改了每个输入的文本观察器.我利用字符串大小来确定何时完全更新文本,并使用 onTextChanged 强制字符串始终为最小大小 2 - 小时和分钟看起来完全相同,除了枚举/光标位置.字段本身将允许在光标位置输入第三个数字.因此,带有12"的字段将变为123",然后触发更新时间,这会确定我们是否需要在其他位置设置文本,或者拒绝数字,或者需要任何自定义逻辑.

Since I only wanted to use numbers so I had to use a text view to represent the colon. modified the Text watcher for each input. I leveraged the string size in determining when to update the text entirely, and used onTextChanged to force the string to always be at minimum size 2 - Both the hours and minutes look exactly the same, with the exception of the enum/cursor position. The fields themselves would allow for a third digit to be enterd at the cursor position. So a fiel with "12" would turn into "123" and then trigger update time, which figures out if we need to set text else where, or reject the number, or whatever custom logic is required.

addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable?) {  
                if (s != null && s.toString().length > 2)
                    //I used an ENUM to tell my update time function which view I was in. More to come.  Cusor position is set in the keylistener
                    updateTime(s.toString, hoursInput.text, cursorPosition, Field.MINUTES) 
            }

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

            }

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                if (s != null && s.length < 2)
                    StringBuilder(s).apply { this.insert(0, "0") }.toString().also {
                        minutesInput.setText(it)
                    }
            }
        })

我还添加了一个 onKeyListener 来监视删除键,专门用于实现自定义逻辑.但在它返回按下键的结果之前,我会跟踪每个事件的 keydown 上的光标位置.分钟和小时输入看起来相同/相似.

I also added an onKeyListener to watch the delete key specifically to implement custom logic. But before it would return the result of the key pressed, i'd track the cursor position on keydown for every event. Both minutes and Hours inputs look the same/similar.

 setOnKeyListener { _, keyCode, keyEvent ->
            if (keyEvent.action == KeyEvent.ACTION_DOWN) cursorPosition = selectionStart
            (keyCode == KeyEvent.KEYCODE_DEL && keyEvent.action == KeyEvent.ACTION_DOWN && selectionStart == selectionEnd && selectionStart != 0).also { performDelete ->
                if (performDelete) Logic.deleteTime(minutesInputString, minutesInput.text, cursorPosition, Field.Minutes)
            }
        }

最后,我有我的逻辑类,我实际上执行了字符串操作.

Finally, I have my Logic class which I actually performed the string manipulation.

// Inside update/delete I convert the strings into a string that is like 00129 and use the cursorPosition/Field enum to determine where to add text.
Logic.updateTime(minutesInputString, hoursInputString, cursorPosition, Field) : String[]
Logic.deleteTime(minutesInputString, hoursInputString, cursorPosition, Field): String[]

然后我立即使用输入设置时间.

I then set the time immediately with the inputs.

updateTime(mins, hours, position, Field.MINUTES).also{ 
    // ("00", "129", 2, Minutes) returns as ["01", "29"] 
    minutesInput.setText(it[0])
    minutesInput.setText(it[1])
}

仍在解决的复杂问题,但这就是我解决它的方法.实施这个解决方案花了我 4 个小时,我已经转动了将近三天的轮子!

Complicated problem that is still being worked on, but this is how I've solved it. Implementing this solution took me 4 hours where I had spun my wheels for nearly three days!

相关文章