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界面,自然会显示成副本列表。
分步修复
创建列表项布局
新建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"/>修正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) } } }完善点击回调
在ManageUsers中实现userSelect回调,点击列表项时填充修改框:var userSelect: ((UsersTable?) -> Unit) = { user -> user?.let { editTextChangeUser.setText(it.userName) position = it.userId.toLong() // 保存选中用户ID,用于后续更新 } }修复更新按钮逻辑
修改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
相关产品推荐
相关产品推荐

