android 的 EditText 的动态前导文本

Dynamic leading text for android's EditText

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

我们的想法是让 EditText 从右到左插入文本。随着使用输入的变化,我希望发生以下情况。

- 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 似乎是 运行 无限循环。

试试这个:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <EditText
        android:id="@+id/edittext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25dp"
        android:padding="20dp"
        android:inputType="number"
        android:maxLength="4"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textview"
        android:textSize="25dp"
        android:padding="20dp"/>

</LinearLayout>

MainActivity.kt

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        edittext.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(s: Editable) {}
            override fun beforeTextChanged(s: CharSequence, start: Int,
                                           count: Int, after: Int) {
            }
            override fun onTextChanged(s: CharSequence, start: Int,
                                       before: Int, count: Int) {
                if (s.length<5){
                    var a=""
                    var i=4-s.length
                    var j=0
                    while (j<i){
                        a+="0"
                        j++
                    }
                    var b=a+s
                    b = b.substring(0, 2) + ":" + b.substring(2, b.length)
                textview.setText(b)}
            }
        })
    }
}

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

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

        <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 - 小时和分钟看起来完全相同,除了 enum/cursor 位置。这些字段本身将允许在光标位置输入第三个数字。因此,带有“12”的字段会变成“123”,然后触发更新时间,它会确定我们是否需要在其他地方设置文本,或者拒绝数字,或者需要任何自定义逻辑。

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来监视删除键,专门用于实现自定义逻辑。但在 return 按下按键的结果之前,我会跟踪每个事件的 keydown 上的光标位置。分钟和小时输入看起来都是 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)
            }
        }

最后,我有我的逻辑 class,我实际执行了字符串操作。

// 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[]

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

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

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