You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何通过setOnClickListener修改ViewModel的LiveData值

问题原因

你代码里存在4个核心错误导致功能不生效:

  1. 点击事件设置逻辑完全错误:你将queryBtn的点击事件设置放在了userName的observe回调中,只有当userName变化时才会给按钮绑定点击事件,初始状态下userName为null不会触发回调,按钮根本没有绑定有效点击事件;后续你又在response的observe回调中再次给queryBtn设置了空的点击事件,直接把之前的有效点击覆盖了。
  2. 取输入内容时写错控件类型,取值方法错误:nameText是EditText类型,你写成了findViewById<Button>,而且直接对控件调用toString拿到的是控件对象的内存地址,不是输入的文本内容,正确写法是findViewById<EditText>(R.id.nameText).text.toString()。
  3. ViewModel中请求接口传参错误:你调用api.listRepos时传的是userName.toString(),userName是MutableLiveData对象,直接toString拿到的是LiveData的类信息,正确应该取userName.value的值。
  4. ViewModel初始化时就调用了refreshData,此时userName还没有赋值,会直接触发异常。

修复后的代码

RestActivity.kt

package com.example.internet_ex

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.*
import kotlinx.coroutines.launch
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Path
import java.lang.StringBuilder

data class Owner(val login: String)
data class Repo(val name: String, val owner: Owner, val url: String)
data class Contributor(val login: String, val contributions: Int)

interface RestApi {
    @GET("users/{user}/repos")
    suspend fun listRepos(@Path("user") user: String): List<Repo>

    @GET("/repos/{owner}/{repo}/contributors")
    suspend fun contributors(
        @Path("owner") owner: String,
        @Path("repo") repo: String
    ): List<Contributor>
}

class MyViewModel : ViewModel() {
    private val baseURL = "https://api.github.com/"
    private lateinit var api: RestApi
    val userName = MutableLiveData<String>()
    val response = MutableLiveData<String>()

    init {
        retrofitInit()
        // 初始化不要直接调用refreshData,此时没有userName
    }

    fun refreshData() {
        viewModelScope.launch {
            // 先判断userName有没有值
            val user = userName.value ?: return@launch
            try {
                val repos = api.listRepos(user)
                response.value = StringBuilder().apply {
                    repos.forEach {
                        append(it.name)
                        append(" - ")
                        append(it.owner.login)
                        append("\n")
                    }
                }.toString()
            } catch (e: Exception) {
                response.value = "Failed to connect to the server"
            }
        }
    }

    private fun retrofitInit() {
        val retrofit = Retrofit.Builder()
            .baseUrl(baseURL)
            .addConverterFactory(MoshiConverterFactory.create())
            .build()
        api = retrofit.create(RestApi::class.java)
    }
}


class RestActivity : AppCompatActivity() {
    private lateinit var myViewModel: MyViewModel
    // 提前初始化控件,不要每次都findViewById
    private lateinit var nameText: EditText
    private lateinit var queryBtn: Button
    private lateinit var textResponse: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_rest)

        // 初始化控件
        nameText = findViewById(R.id.nameText)
        queryBtn = findViewById(R.id.queryBtn)
        textResponse = findViewById(R.id.textResponse)

        myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)

        // 只设置一次点击事件,不要放在observe里
        queryBtn.setOnClickListener {
            // 取输入框内容赋值给userName
            myViewModel.userName.value = nameText.text.toString()
            // 触发请求
            myViewModel.refreshData()
        }

        // 观察响应结果更新UI
        myViewModel.response.observe(this) {
            textResponse.text = it
        }
    }
}

activity_rest.xml (原有代码正确,不需要修改)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".RestActivity">

    <TextView
        android:id="@+id/textResponse"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:text="TextView"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/queryBtn" />

    <Button
        android:id="@+id/queryBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Query"
        app:layout_constraintStart_toEndOf="@+id/nameText"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/nameText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="textPersonName"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

需要补充学习的知识点

  • Android基础控件使用:掌握EditText、Button等常见控件的属性和方法,正确获取输入框文本内容
  • LiveData工作机制:理解observe回调的触发时机,不要在observe回调中做控件事件绑定这类只需要执行一次的操作
  • ViewModel使用规范:掌握LiveData的取值、赋值方法,不要直接对LiveData对象调用toString
  • 点击事件设置逻辑:同一个控件不要重复设置OnClickListener,后设置的会覆盖之前的逻辑
  • 协程+Retrofit网络请求规范:请求前做好参数合法性校验,合理捕获异常

内容的提问来源于stack exchange,提问作者yeong

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 07:39:03