android 的 EditText 的动态前导文本
Dynamic leading text for android's EditText
我研究过使用 TextWatcher
和 InputFilter
,但我不太确定如何解决这个问题。
我们的想法是让 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 个小时,而我已经转动了将近三天的轮子!
我研究过使用 TextWatcher
和 InputFilter
,但我不太确定如何解决这个问题。
我们的想法是让 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 个小时,而我已经转动了将近三天的轮子!