Android (Kotlin) Двусторонняя привязка данных с нестроковым примитивным типом - PullRequest
0 голосов
/ 06 февраля 2020

Я пытаюсь выполнить двустороннюю привязку данных в 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, так что я уверен, что это может быть что-то, что я упускаю из виду, но любая информация будет оценена по поводу того, что я здесь упускаю.

1 Ответ

0 голосов
/ 07 февраля 2020

Я понял это, и это полностью моя вина. Это не показано в посте, но в моем фрагменте я никогда не назначал переменную из макета для моей привязки, поэтому ему было нечего отправить значение обратно.

Установка этого исправила это для меня потому что я забыл это раньше:

FragmentNewOrder.kt

        val app = this.activity!!.application
        val ds = SvcDatabase.getInstance(app).DAO
        val factory = FragmentNewOrderViewModelFactory(ds, app)
        val viewModel =
            ViewModelProviders.of(this, factory).get(FragmentNewOrderViewModel::class.java)

        // Very important, don't forget to bind your data ?
        binding.viewModel = viewModel
...