Двухстороннее связывание данных с MutableLiveData во ViewModel - PullRequest
0 голосов
/ 05 апреля 2020

Я новичок в Kotlin и Android разработке. Я знаю, что здесь есть похожие, если не одни и те же вопросы. Да, я потратил целый день, пытаясь это выяснить.

Мне также жаль, что я не привел подходящего примера игрушки.

У меня простая цель. Я хочу, чтобы два EditText "связали" вместе. Когда я меняю одну, я ожидаю, что другая тоже изменится. Я хочу применить шифр Цезаря (шифр реализован и работает). Но мы пока проигнорируем второе EditText и сосредоточимся только на связывании первого.

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

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

СВЯЗЬ НЕ РАБОТАЕТ!

У меня есть некоторый макет некоторого фрагмента (я пропускаю неважные вещи)

<layout>
    <data>
        <variable name="caesarViewModel" type="cz.fortescue.ui.caesar.CaesarViewModel"/>
    </data>
    <EditText
         android:id="@+id/caesar_input"
         android:inputType="text"
         android:text="@={caesarViewModel.input}"/>
</layout>

Где "@={caesarViewModel.input}" является важной частью. (не пропустите =).

Теперь ViewModel.

class CaesarViewModel : ViewModel() {
    val input = MutableLiveData<String>().apply {
        value = "aaaaaa"
    }
}

Я использую MutableLiveData, поэтому DataBiding сможет изменить его. Я также использую apply для проверки наблюдателя, который я покажу вам позже.

Последний, но не менее важный фрагмент

class CaesarFragment : Fragment() {
    private lateinit var binding: FragmentCaesarBinding
    private val caesarViewModel by lazy { ViewModelProvider(this).get(CaesarViewModel::class.java) }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentCaesarBinding.inflate(
            inflater,
            container,
            false
        )

        binding.caesarViewModel = caesarViewModel
        binding.lifecycleOwner = viewLifecycleOwner

        val root = inflater.inflate(R.layout.fragment_caesar, container, false)

        caesarViewModel.input.observe(viewLifecycleOwner, Observer { text ->
            caesarViewModel.output.value = encrypt(text) { caesarEncrypt(it, 1)}
            Toast.makeText(context, text, Toast.LENGTH_SHORT).show()
        })
        return root
    }
}

Поскольку я новичок в Kotlin, Я наполовину догадываюсь, что произойдет.

  1. caesarViewModel

caesarViewModel создается по мере необходимости. Обратите внимание, что я использую ViewModelProvider не ViewModelProviders.

binding

В Интернете было 5 разных способов, как правильно получить привязку. Я надеюсь, что это сейчас правильный. Я подозреваю здесь некоторые ошибки, но я не нашел никаких ...

Настройка членов binding

Я устанавливаю lifecycleOwner и caesarViewModel. Последнее должно быть важной частью всей системы DataBinding.

Observer

Я создаю Observer, который должен уведомить меня с помощью Toast, если что-то изменится. Он делает это один раз с aaaaaa. Поскольку это значение, я применил к MutableLiveData Я подозреваю, что наблюдатель работает очень хорошо.

Кажется, также необходимо изменить build.gradle

Плагины:

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android:

dataBinding {
    enabled = true
}
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
    jvmTarget = JavaVersion.VERSION_1_8.toString()
}

Зависимости:

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.navigation:navigation-fragment:2.2.1'
    implementation 'androidx.navigation:navigation-ui:2.2.1'
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

Чего мне не хватает ??? Должно быть что-то, что я упустил из виду или о чем не знаю. В некоторых руководствах они используют декоратор @Bindable, однако в MutableLiveData.

это не требуется.

1 Ответ

1 голос
/ 05 апреля 2020

из onCreateView return binding.root вы возвращаете неправильно root вы возвращаете раздутое представление с этим кодом (это неправильно для привязки данных):

val root = inflater.inflate(R.layout.fragment_caesar, container, false)

Вы должны заменить вышеуказанную строку с onCreateView на:

return binding.root
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...