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

RecyclerView显示Activity界面副本而非数据库数据问题求助

问题与解决方案

问题描述

我正在给修改用户名功能设置ClickListener,Google教程主要讲Fragment的用法,这个方案我之后再用。参考了其他示例和Android文档后,我觉得View Binding配置是对的,但搞不懂为什么RecyclerView里显示的是Activity界面的副本列表,而不是数据库表中的数据。

相关代码

UserListAdapter.kt

package com.neillbarrett.debitsandcredits

import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding

val inAdapter: String = "In UserListAdapter "

class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
    ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.UserViewHolder {

        Log.w(inAdapter,"OnCreateViewHolder started")

        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.activity_manage_users, parent, false)
        return UserViewHolder.create(parent)
    }

    class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit) {

            Log.w(inAdapter,"UserViewHolder started")

            itemView.setOnClickListener { View.OnClickListener {
/*                if (View.) { }*/

                val nameSelected = userSelect(usersTable)
                //userSelect(usersTable)
                //need to assign the result of the clicklistener to the editText
                //binding.etEditName.setText(R.layout.activity_list_of_users.toString())
            }}
        }

        companion object {
            fun create(parent: ViewGroup) : UserViewHolder {

                Log.w(inAdapter,"Companion object 'Create' function started")

                val view: View = LayoutInflater.from(parent.context)
                    .inflate(R.layout.activity_manage_users, parent, false)
                return UserViewHolder(view)
            }
        }
    }

    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        Log.w(inAdapter,"OnBindViewHolder started")

        val current = getItem(position)
        holder.bind(current, userSelect)
    }

    class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
        override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
            Log.w(inAdapter,"areItemsTheSame function started")
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
            Log.w(inAdapter,"areContentsTheSame function started")
            return oldItem.userName == newItem.userName
        }
    }
}

ManageUsers.kt

package com.neillbarrett.debitsandcredits

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.CreditsAndDebitsApp
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding

class ManageUsers : AppCompatActivity() {

    lateinit var binding: ActivityManageUsersBinding
    lateinit var recyclerView: RecyclerView
    lateinit var editTextAddUser: EditText
    lateinit var editTextChangeUser: EditText
    lateinit var newUser: String
    var userSelect: ((UsersTable?) -> Unit) = {}
    var position: Long = 0
    val inWhichActivity: String = "In ManageUsers"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityManageUsersBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
        //setContentView(R.layout.activity_manage_users)

        Log.w(inWhichActivity, "Setting up userViewModel & repository")
        val userViewModel: UserViewModel by viewModels {
            UserViewModelFactory((application as CreditsAndDebitsApp).repository)
        }

        recyclerView = findViewById(R.id.rec_view_userList)
        editTextAddUser = findViewById(R.id.et_AddUser)
        editTextChangeUser = findViewById(R.id.et_Edit_Name)

        val adapter = UserListAdapter(userSelect)
        binding.recViewUserList.adapter = adapter
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)

        userViewModel.allUsers.observe(this, Observer() {user ->
            Log.w(inWhichActivity,"Starting Observer")
            user?.let { adapter.submitList(it) }
            Log.w(inWhichActivity, "Started Observer")
        })

        val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
        btnAddUser.setOnClickListener {
            Log.w(inWhichActivity,"Started btnAddUser.setOnClickListener")
            if (TextUtils.isEmpty(editTextAddUser.text)) {
                Toast.makeText(this, "User name cannot be empty", Toast.LENGTH_SHORT).show()
            } else {
                newUser = editTextAddUser.text.toString()
//                Log.w("Add user button", "Username put into newUser")
                userViewModel.insertUser(UsersTable(0, newUser))
//                Toast.makeText(this, "Username added to table", Toast.LENGTH_SHORT).show()
//                Log.w("Add user button", "Username added to table")
            }
        }

        val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
        btnChangeUser.setOnClickListener {
            Log.w(inWhichActivity,"Started btnChangeUser.setOnClickListener")
            Toast.makeText(this, "Selected position is ${recyclerView.getChildAdapterPosition(it)}", Toast.LENGTH_SHORT).show()
/*            if (recyclerView.getChildAdapterPosition(it) == -1) {
                Toast.makeText(this, "Select a name.", Toast.LENGTH_SHORT).show()
            } else {
                if (editTextChangeUser.text.toString() == recyclerView.adapter.toString()) {
                    Toast.makeText(this, "Name has not been changed.", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(this, "Name would have been changed.", Toast.LENGTH_SHORT).show()
                    val rvItemRecId: Long
                    rvItemRecId = adapter.getItemId(position.toInt())
                    userViewModel.updateUser(UsersTable(rvItemRecId.toInt(), adapter.toString()))
                }
            }*/
        }
    }
}

UserViewModel.kt

package com.neillbarrett.debitsandcredits

import android.util.Log
import androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException

val inViewModel: String = "In UserViewModel "

class UserViewModel(private val repository: UserRepository) : ViewModel() {

    // Using LiveData and caching what allWords returns has several benefits:
    // - We can put an observer on the data (instead of polling for changes) and only update the
    //   the UI when the data actually changes.
    // - Repository is completely separated from the UI through the ViewModel.
    val allUsers: LiveData<List<UsersTable>> = repository.allUsers.asLiveData()
    /**
     * Launching a new coroutine to insert the data in a non-blocking way
     */

    fun insertUser(user: UsersTable) = viewModelScope.launch {
        repository.insertUser(user)
        Log.w(inViewModel,"insertUser called")
    //repository.insertUser(usersTable = List<UsersTable>())
    //repository.insertUser(UsersTable(0, userName = user.userName))
    }

    fun updateUser(user: UsersTable) = viewModelScope.launch {
        repository.updateUser(user)
        Log.w(inViewModel, "updateUser called")
    }

    fun deleteUser(user: UsersTable) = viewModelScope.launch {
        repository.deleteUser(user)
        Log.w(inViewModel, "deteUser called")
    }
}

class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        Log.w(inViewModel,"UserViewModelFactory called")
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return UserViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

问题原因与修复

核心问题

RecyclerView显示Activity界面副本,是因为你在Adapter的onCreateViewHolder和UserViewHolder.create方法里,错误加载了整个Activity的布局R.layout.activity_manage_users,而非RecyclerView单个列表项的布局。每个列表项都加载了完整的Activity界面,自然会显示成副本列表。

分步修复

  1. 创建列表项布局
    新建res/layout/item_user.xml,只保留单个用户条目的UI元素:

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tv_user_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:textSize="18sp"/>
    
  2. 修正Adapter布局加载
    修改UserListAdapter中加载布局的代码,替换为列表项布局:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.UserViewHolder {
        Log.w(inAdapter,"OnCreateViewHolder started")
        return UserViewHolder.create(parent)
    }
    
    class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val userNameTv: TextView = itemView.findViewById(R.id.tv_user_name)
    
        fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit) {
            Log.w(inAdapter,"UserViewHolder started")
            // 绑定用户名数据
            usersTable?.let {
                userNameTv.text = it.userName
            }
            // 设置正确的点击事件
            itemView.setOnClickListener {
                userSelect(usersTable)
            }
        }
    
        companion object {
            fun create(parent: ViewGroup) : UserViewHolder {
                Log.w(inAdapter,"Companion object 'Create' function started")
                val view: View = LayoutInflater.from(parent.context)
                    .inflate(R.layout.item_user, parent, false) // 替换为列表项布局
                return UserViewHolder(view)
            }
        }
    }
    
  3. 完善点击回调
    在ManageUsers中实现userSelect回调,点击列表项时填充修改框:

    var userSelect: ((UsersTable?) -> Unit) = { user ->
        user?.let {
            editTextChangeUser.setText(it.userName)
            position = it.userId.toLong() // 保存选中用户ID,用于后续更新
        }
    }
    
  4. 修复更新按钮逻辑
    修改btnChangeUser的点击事件,实现正确的用户更新:

    btnChangeUser.setOnClickListener {
        Log.w(inWhichActivity,"Started btnChangeUser.setOnClickListener")
        val newName = editTextChangeUser.text.toString()
        when {
            TextUtils.isEmpty(newName) -> {
                Toast.makeText(this, "新用户名不能为空", Toast.LENGTH_SHORT).show()
            }
            position == 0 -> {
                Toast.makeText(this, "请先选择一个用户", Toast.LENGTH_SHORT).show()
            }
            else -> {
                userViewModel.updateUser(UsersTable(position.toInt(), newName))
                Toast.makeText(this, "用户名已更新", Toast.LENGTH_SHORT).show()
                editTextChangeUser.setText("")
                position = 0
            }
        }
    }
    

额外优化

  • 修正DiffUtil比较逻辑:UsersComparator中areItemsTheSame应比较唯一标识userId,而非直接比较对象:
    override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
        Log.w(inAdapter,"areItemsTheSame function started")
        return oldItem.userId == newItem.userId
    }
    
  • 移除重复的Adapter设置:binding.recViewUserList.adapter = adapter和recyclerView.adapter = adapter保留其一即可。

内容的提问来源于stack exchange,提问作者N.Barrett

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:15:27