onClick 方法不是从带有 dataBinding 的 .xml 绑定表达式中调用的

Posted

技术标签:

【中文标题】onClick 方法不是从带有 dataBinding 的 .xml 绑定表达式中调用的【英文标题】:onClick method is not called from .xml binding expression with dataBinding 【发布时间】:2022-01-10 11:25:33 【问题描述】:

我正在尝试通过在按钮的 onClick 中使用绑定表达式(这些实际上是什么,属性?)从“content_main.xml”调用“MainActivity.kt”中的函数。

问题是,当我按下八个按钮时,它们永远不会被调用。

这就是“content_main.xml”文件中代码段落的样子(为了更好的可读性,我省略了不必要的代码):

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
    <variable
        name="viewModel"
        type="com.ronnabyte.ntatools.model.FuelViewModel" />

    <variable
        name="activity"
        type="com.ronnabyte.ntatools.MainActivity" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_
    android:layout_
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    ...

    <Button
        android:id="@+id/clear_required_fuel_btn"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
        android:layout_
        android:layout_
        android:onClick="@()->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" />

    ...

    <Button
        android:id="@+id/clear_remaining_fuel_btn"
        style="@style/Widget.MaterialComponents.Button.OutlinedButton"
        android:layout_
        android:layout_
        android:onClick="@()->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" />

    ...

</androidx.constraintlayout.widget.ConstraintLayout>

以及“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 -> true
        else -> 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'

【问题讨论】:

如果将这些方法移至ViewModel 并在MainActivity 订阅ViewModel LiveData 会怎样? 即使我知道该怎么做,这不违反 viewModel 架构,它说 viewModel 不应该知道视图吗?我正在这两个函数中设置视图...抱歉,我对此很陌生。 我认为,我的假设是错误的。但是对于ViewModel,这不会错。您将在ViewModel 中拥有LiveDataMutableStateFlow 并在那里设置它的值。然后在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) //<--
    setContentView(binding.root) //<--
    setSupportActionBar(binding.toolbar)

【讨论】:

这不起作用,代码运行低谷,但是当我按下按钮时仍然没有调用该函数。【参考方案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")
    

<?xml version="1.0" encoding="utf-8"?>
<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="activity"
            type="com.example.myapplication.MainActivity" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_
        android:layout_
        tools:context=".MainActivity">

        <Button
            android:layout_
            android:layout_
            android:onClick="@() -> 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" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

【讨论】:

不幸的是,这不起作用,我在活动中得到“未解决的参考”。 哪个变量未解析?我写了一个演示来测试,它工作。请检查我编辑的答案。 非常感谢您的调查,非常感谢。当我开始一个新项目并使用您提供的代码时,它就像您所说的那样完美运行。但是当我在我的项目中尝试“binding.activity = this”行时,“.activity”会变成红色,工具提示“未解析的引用:活动”。但是“绑定”与正在运行的示例的类型完全相同,即“私有最终后期初始化变量绑定:ActivityMainBinding”类型。我清理并重建了这个项目,但没有运气。我将尝试包含 gradle 文件,以便您有时间可以查看一下。非常感谢。

以上是关于onClick 方法不是从带有 dataBinding 的 .xml 绑定表达式中调用的的主要内容,如果未能解决你的问题,请参考以下文章

java.lang.IllegalStateException:找不到带有 id_button 的方法 onClick 处理程序

Android 将 Activity 添加到带有 Intent 的对话框

调用函数onclick on some buttons而不影响单个onclick函数的按钮(已经为按钮分配) - JavaScript

jquery怎么和纯js一样接收onclick带来的参数?

通过 onclick 原生方法 VueJs 监听事件点击

带有 Onclick 的自定义按钮,隐藏 Onclick 事件