Метод onClick не вызывается из выражения привязки .xml с привязкой данных

#kotlin #android-databinding

#котлин #android-привязка данных

Вопрос:

Я пытаюсь вызвать функцию, которая находится в «MainActivity.kt» из «content_main.xml» с помощью выражения привязки в onClick (как они на самом деле называются, атрибут?) пуговицы.

Проблема в том, что им никогда не звонят, когда я нажимаю восемь кнопок.

Вот как выглядит кодовый отрывок в «content_main.xml файл» (я исключил ненужный код для лучшей читабельности):

 lt;?xml version="1.0" encoding="utf-8"?gt; lt;layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"gt;  lt;datagt;  lt;variable  name="viewModel"  type="com.ronnabyte.ntatools.model.FuelViewModel" /gt;   lt;variable  name="activity"  type="com.ronnabyte.ntatools.MainActivity" /gt; lt;/datagt;  lt;androidx.constraintlayout.widget.ConstraintLayout  android:layout_width="match_parent"  android:layout_height="match_parent"  app:layout_behavior="@string/appbar_scrolling_view_behavior"gt;   ...   lt;Button  android:id="@ id/clear_required_fuel_btn"  style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:onClick="@{()-gt;activity.clearRequiredFuel()}"  android:text="@string/clear_fuel_on_board"  app:icon="@drawable/ic_baseline_delete_24"  app:layout_constraintBottom_toBottomOf="@ id/required_fuel_ed"  app:layout_constraintEnd_toEndOf="parent"  app:layout_constraintStart_toEndOf="@ id/required_fuel_ed"  app:layout_constraintTop_toTopOf="@ id/required_fuel_ed" /gt;   ...   lt;Button  android:id="@ id/clear_remaining_fuel_btn"  style="@style/Widget.MaterialComponents.Button.OutlinedButton"  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:onClick="@{()-gt;activity.clearRemainingFuel()}"  android:text="@string/clear_fuel_on_board"  app:icon="@drawable/ic_baseline_delete_24"  app:layout_constraintBottom_toBottomOf="@ id/remaining_fuel_ed"  app:layout_constraintEnd_toEndOf="parent"  app:layout_constraintStart_toEndOf="@ id/remaining_fuel_ed"  app:layout_constraintTop_toTopOf="@ id/remaining_fuel_ed" /gt;   ...  lt;/androidx.constraintlayout.widget.ConstraintLayoutgt;  

И код в «MainActivity.kt»:

 package com.ronnabyte.ntatools  import android.os.Bundle import android.util.Log import android.view.Menu import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil import androidx.lifecycle.LifecycleOwner import com.ronnabyte.ntatools.databinding.ActivityMainBinding import com.ronnabyte.ntatools.model.FuelViewModel  class MainActivity : AppCompatActivity() {  private lateinit var binding: ActivityMainBinding private val viewModel: FuelViewModel by viewModels()  override fun onCreate(savedInstanceState: Bundle?) {  super.onCreate(savedInstanceState)   binding = DataBindingUtil.setContentView(this, R.layout.activity_main)   setSupportActionBar(binding.toolbar)   binding.fab.setOnClickListener {  viewModel.calculateFuelUplift()  } }  override fun onCreateOptionsMenu(menu: Menu): Boolean {  // Inflate the menu; this adds items to the action bar if it is present.  menuInflater.inflate(R.menu.menu_main, menu)  return true }  override fun onOptionsItemSelected(item: MenuItem): Boolean {  // Handle action bar item clicks here. The action bar will  // automatically handle clicks on the Home/Up button, so long  // as you specify a parent activity in AndroidManifest.xml.  return when (item.itemId) {  R.id.action_settings -gt; true  else -gt; super.onOptionsItemSelected(item)  } }  fun clearRequiredFuel() {  Log.d("MainActivity", "ClearRequiredFuel has been called.")  binding.contentMain.apply {  requiredFuelUpliftTv.text = ""  requiredFuelEd.text.clear()  requiredFuelEd.requestFocus()  } }  fun clearRemainingFuel() {  Log.d("MainActivity", "ClearRemainingFuel has been called.")  binding.contentMain.apply {  requiredFuelUpliftTv.text = ""  remainingFuelEd.text.clear()  requiredFuelEd.requestFocus()  } }  

Любая помощь приветствуется, большое спасибо.

Изменить: вот файл gradle:

 plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-kapt' }  android { compileSdk 31  defaultConfig {  applicationId "com.ronnabyte.ntatools"  minSdk 26  targetSdk 31  versionCode 1  versionName "1.0"   testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" }  buildTypes {  release {  minifyEnabled false  proguardFiles getDefaultProguardFile('proguard-android- optimize.txt'), 'proguard-rules.pro'  } }  buildFeatures {  dataBinding true }  compileOptions {  sourceCompatibility JavaVersion.VERSION_1_8  targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions {  jvmTarget = '1.8' }  }  dependencies {  implementation 'androidx.appcompat:appcompat:1.4.0' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0' implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0" implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.fragment:fragment-ktx:1.4.0'  

}

Комментарии:

1. Что делать, если перенести эти методы ViewModel в MainActivity подписку на ViewModel LiveData и в нее?

2. Даже если бы я знал, как это сделать, разве это не противоречило бы архитектуре ViewModel, которая гласит, что ViewModel не должен знать о представлениях? Я устанавливаю представления в этих двух функциях… Прошу прощения, я довольно новичок в этом деле.

3. Я думаю, что мое предположение неверно. Но в отношении ViewModel этого не было бы ничего плохого. Вы будете иметь LiveData или MutableStateFlow в ViewModel и установите его значение там. Затем в MainActivity вы подпишетесь на это значение от ViewModel . Когда данные ViewModel изменятся (LiveData или MutableStateFlow изменят свое значение), подписчик также отразит изменения ( requiredFuelUpliftTv.text = "" и т. Д.).

Ответ №1:

Я нашел проблему, почему binding.activity = this это не работало. binding имеет тип ActivityMainBinding , но переменная activity была определена в content_main.xml вместо activity_main.xml .

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

Ответ №2:

внесите эти небольшие изменения в свой код:

 private lateinit var binding: ActivityMainBinding private val viewModel: FuelViewModel by viewModels()  override fun onCreate(savedInstanceState: Bundle?) { binding  super.onCreate(savedInstanceState)  binding = ActivityMainBinding.inflate(layoutInflater) //lt;--  setContentView(binding.root) //lt;--  setSupportActionBar(binding.toolbar)  

Комментарии:

1. Это не работает, код запускается через корыто, но функция по-прежнему не вызывается, когда я нажимаю кнопки (кнопки).

Ответ №3:

Добавьте эту строку в свой onCreate метод:

 binding.activity = this  

Демо-код ниже:

 class MainActivity : AppCompatActivity() {  private lateinit var binding: ActivityMainBinding   override fun onCreate(savedInstanceState: Bundle?) {  super.onCreate(savedInstanceState)  binding = DataBindingUtil.setContentView(this, R.layout.activity_main)  // without this line, test() will not called  binding.activity = this  }   fun test() {  Log.d("test", "test")  } }  
 lt;?xml version="1.0" encoding="utf-8"?gt; lt;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"gt;   lt;datagt;   lt;variable  name="activity"  type="com.example.myapplication.MainActivity" /gt;  lt;/datagt;   lt;androidx.constraintlayout.widget.ConstraintLayout  android:layout_width="match_parent"  android:layout_height="match_parent"  tools:context=".MainActivity"gt;   lt;Button  android:layout_width="wrap_content"  android:layout_height="wrap_content"  android:onClick="@{() -gt; activity.test()}"  android:text="Hello World!"  app:layout_constraintBottom_toBottomOf="parent"  app:layout_constraintLeft_toLeftOf="parent"  app:layout_constraintRight_toRightOf="parent"  app:layout_constraintTop_toTopOf="parent" /gt;   lt;/androidx.constraintlayout.widget.ConstraintLayoutgt; lt;/layoutgt;  

Комментарии:

1. К сожалению, это не работает, я получаю «Неразрешенную ссылку» на действие.

2. Какая переменная не решена? Я написал демо-версию для тестирования, и она сработала. Пожалуйста, проверьте мой отредактированный ответ.

3. Большое спасибо, что изучили это, очень признателен. Когда я начинаю новый проект и использую предоставленный вами код, он отлично работает, как вы и сказали. Но когда я пробую строку «привязка.активность = это» в своем проекте,». активность» становится красной с подсказкой «Неразрешенная ссылка: активность». Но «привязка» имеет тот же тип, что и в примере, который работает, а именно тип «частная окончательная привязка lateinit var: ActivityMainBinding». Я очистил и восстановил проект безрезультатно. Я постараюсь включить файл gradle, чтобы вы могли посмотреть, если у вас будет время. Большое спасибо.