Я пытаюсь выполнить двустороннюю привязку данных в Android (эмуляция пикселя 3a, работающего на уровне API 29, с минимальной целью API 21). Я хочу использовать синтаксис @={}
в свойстве EditText android:text
.
Документация , похоже, говорит о том, что мы можем использовать класс * stati c преобразователя для преобразования в / из примитивных (или предположительно сложных) типов данных, используя аннотацию для описания того, какие методы обратны, какие другие. В настоящее время я работаю с целочисленными значениями, но также буду работать как минимум с двойными.
Метод преобразования из Int в строку работает нормально. Он вызывается, когда макет моего фрагмента раздувается (хотя, как ни странно, он вызывается дважды сразу), однако преобразование строки обратно в int никогда не выполняется с помощью отладчика.
DatabindingConverters.kt
package com.razelon.svcman.database
import android.widget.EditText
import androidx.databinding.InverseMethod
object DatabindingConverters {
@JvmStatic
@InverseMethod("intToString")
fun stringToInt(v: EditText, old: Int, new: String): Int {
return new.toInt()
}
@JvmStatic
fun intToString(v: EditText, old: Int, new: Int): String {
return new.toString()
}
}
Выдержки из frag_new_order. xml:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.razelon.svcman.orders.FragmentNewOrderViewModel" />
<import type="com.razelon.svcman.database.DatabindingConverters" />
</data>
<ScrollView
android:id="@+id/order_scroller"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbarStyle="outsideOverlay">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="48dp">
<EditText
android:id="@+id/edtWorkOrder"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="16dp"
android:ems="6"
android:hint="@string/hint_work_order"
android:importantForAutofill="no"
android:inputType="number"
// The line in question, for XML
android:text="@={DatabindingConverters.intToString(edtWorkOrder, viewModel.orderNo, viewModel.orderNo)}"
app:layout_constraintBaseline_toBaselineOf="@+id/edtAcct"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/edtAcct" />
// Other views
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</layout>
FragmentNewOrderViewModel.kt
package com.razelon.svcman.orders
import android.app.Application
import androidx.databinding.Bindable
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.razelon.svcman.database.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlin.math.max
const val MAX_WORK_CODES = 5
class FragmentNewOrderViewModel(
val database: SvcDao,
app: Application
) : AndroidViewModel(app) {
private val job = Job()
private val uiScope = CoroutineScope(Dispatchers.Main + job)
private val _listWorkCodes = MutableLiveData<List<WorkCode>>(listOf())
private val _listParts = MutableLiveData<List<SvcPart>>(listOf())
private val _eventResetOrder = MutableLiveData<Boolean>(false)
val serviceMen = database.getAllServiceMen()
val listWorkCodes: LiveData<List<WorkCode>>
get() = _listWorkCodes
val listSvcParts: LiveData<List<SvcPart>>
get() = _listParts
val eventResetOrder: LiveData<Boolean>
get() = _eventResetOrder
val orderNo = MutableLiveData<Int>(0)
fun addWorkCode(): Boolean {
if(_listWorkCodes.value!!.size >= MAX_WORK_CODES) {
return false
}
_listWorkCodes.value = List<WorkCode>(_listWorkCodes.value!!.size + 1) {
if (it < (_listWorkCodes.value!!.size)) {
_listWorkCodes.value?.get(it)!!
} else {
WorkCode(0)
}
}
return true
}
fun updateWorkCode(code: WorkCode) {
}
fun deleteWorkCode(pos: Int) {
_listWorkCodes.value = List<WorkCode>(max(_listWorkCodes.value!!.size - 1, 0)) {
if(it >= pos) _listWorkCodes.value!![it + 1] else _listWorkCodes.value!![it]
}
}
fun addNewPart() {
_listParts.value = List<SvcPart>(_listParts.value!!.size + 1) {
if(it < _listParts.value!!.size) {
_listParts.value!![it]
} else {
SvcPart("", 0.0, 1)
}
}
}
fun deletePart(pos: Int) {
_listParts.value = List<SvcPart>(max(_listParts.value!!.size - 1, 0)) {
if(it >= pos) _listParts.value!![it + 1] else _listParts.value!![it]
}
}
fun resetOrder() {
_eventResetOrder.value = true
_listParts.value = listOf()
_listWorkCodes.value = listOf()
}
fun onOrderReset() {
_eventResetOrder.value = false
}
}
Я в недоумении, почему он не вызывает обратный преобразователь с пометкой по аннотации @InverseMethod("intToString")
. Я использовал ключевое слово object
для объявления класса, пометил методы необходимыми аннотациями, импортировал тип в привязку данных макета и сопоставил параметры в методах, как описано в документации. Я новичок в разработке Kotlin и Android, так что я уверен, что это может быть что-то, что я упускаю из виду, но любая информация будет оценена по поводу того, что я здесь упускаю.