【发布时间】:2023-02-03 07:08:42
【问题描述】:
我想通过我的应用程序中的对话框添加编辑任务。问题是我无法初始化 viewHolder 以访问 bindingAdapterPosition。我需要它来传递数据并更新 viewModel。尝试将它添加到构造函数中 - 没有用。我知道我必须初始化 viewHolder,但不知道如何初始化。
RecyclerviewFragment.kt:
class RecyclerviewFragment : Fragment() {
private lateinit var mUserViewModel: UserViewModel
private lateinit var viewHolder: ViewHolder
private lateinit var adapter: ListAdapter
private var _binding: FragmentRecyclerviewBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentRecyclerviewBinding.inflate(inflater, container, false)
mUserViewModel = ViewModelProvider(this)[UserViewModel::class.java]
adapter = ListAdapter{showUpdateDialog()}
val adapter = ListAdapter{showUpdateDialog()}
val recyclerView = binding.recyclerView
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
// Creates a controller responsible for swiping and moving the views in recyclerview
val itemTouchController = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder, target: ViewHolder,
): Boolean {
// Move specific item from "fromPos" to "toPos" in recyclerview adapter
val fromPos = viewHolder.bindingAdapterPosition
val toPos = target.bindingAdapterPosition
adapter.notifyItemMoved(fromPos, toPos)
return true // true if moved, false otherwise
}
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
mUserViewModel.deleteUser(adapter.getTaskPosition(viewHolder.bindingAdapterPosition))
Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show()
adapter.notifyItemRemoved(viewHolder.bindingAdapterPosition)
}
})
itemTouchController.attachToRecyclerView(binding.recyclerView)
mUserViewModel.readAllData.observe(viewLifecycleOwner) { user ->
adapter.setData(user)
}
return binding.root
}
private fun updateItemInDatabase(dialog: DialogInterface) {
val editText = (dialog as AlertDialog).findViewById<EditText>(R.id.editTextDialog)
val task = editText?.text.toString()
if(inputCheck(task)) {
// Update an entity
mUserViewModel.updateUser(adapter.getTaskPosition(viewHolder.bindingAdapterPosition))
Toast.makeText(context, "Task updated", Toast.LENGTH_SHORT).show()
}
else {
Toast.makeText(context, "Please fill out required fields", Toast.LENGTH_SHORT).show()
}
}
private fun inputCheck(task: String): Boolean {
return !(TextUtils.isEmpty(task))
}
private fun showUpdateDialog() {
MaterialAlertDialogBuilder(requireContext())
.setView(R.layout.fragment_add)
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
// Respond to negative button press
Toast.makeText(context, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
}
.setPositiveButton(getString(R.string.ok)) { dialogInterface, _ ->
// Respond to positive button press
updateItemInDatabase(dialogInterface)
}
.show()
}
}
编辑:
class RecyclerviewFragment : Fragment() {
private lateinit var mUserViewModel: UserViewModel
private lateinit var adapter: ListAdapter
private var _binding: FragmentRecyclerviewBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentRecyclerviewBinding.inflate(inflater, container, false)
mUserViewModel = ViewModelProvider(this)[UserViewModel::class.java]
adapter = ListAdapter{ user -> showUpdateDialog(user)}
val recyclerView = binding.recyclerView
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
// Creates a controller responsible for swiping and moving the views in recyclerview
val itemTouchController = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder, target: ViewHolder,
): Boolean {
// Move specific item from "fromPos" to "toPos" in recyclerview adapter
val fromPos = viewHolder.bindingAdapterPosition
val toPos = target.bindingAdapterPosition
adapter.notifyItemMoved(fromPos, toPos)
return true // true if moved, false otherwise
}
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
mUserViewModel.deleteUser(adapter.getTaskPosition(viewHolder.bindingAdapterPosition))
Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show()
adapter.notifyItemRemoved(viewHolder.bindingAdapterPosition)
}
})
itemTouchController.attachToRecyclerView(binding.recyclerView)
mUserViewModel.readAllData.observe(viewLifecycleOwner) { user ->
adapter.setData(user)
}
return binding.root
}
private fun updateItemInDatabase(user: User) {
val editText = view?.findViewById<EditText>(R.id.editTextDialog)
val task = editText?.text.toString()
if(inputCheck(task)) {
// Update an entity
mUserViewModel.updateUser(user)
Toast.makeText(context, "Task updated", Toast.LENGTH_SHORT).show()
}
else {
Toast.makeText(context, "Please fill out required fields", Toast.LENGTH_SHORT).show()
}
}
private fun inputCheck(task: String): Boolean {
return !(TextUtils.isEmpty(task))
}
private fun showUpdateDialog(user: User) {
MaterialAlertDialogBuilder(requireContext())
.setView(R.layout.fragment_add)
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
// Respond to negative button press
Toast.makeText(context, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
}
.setPositiveButton(getString(R.string.ok)) { _, _ ->
// Respond to positive button press
val taskText = view
?.findViewById<EditText>(R.id.editTextDialog)
?.text?.toString()
updateItemInDatabase(user)
}
.show()
}
}
当您在 updateDialog 中按 ok 时,应用程序不会崩溃,但它不会真正更新数据库或 recyclerview 项目。原因是我不知道如何更新它,因为我让列表适配器返回整个用户(id,任务)并且不知道如何只更新任务。添加一些适配器代码让它自己解释。
class ListAdapter(var imageListener:(user: User)->Unit) : RecyclerView.Adapter<ListAdapter.MyViewHolder>() {
...
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = dataSet[position]
holder.taskTitle.text = currentItem.task
holder.editImage.setOnClickListener {
imageListener(getTaskPosition(position))
}
holder.notificationImage.setOnClickListener {
val action = RecyclerviewFragmentDirections.actionRecyclerFragmentToNotificationFragment()
holder.itemView.findNavController().navigate(action)
}
}
fun getTaskPosition(position: Int): User {
return dataSet[position]
}
我明白了,似乎更了解这个问题。现在我发现我并没有充分利用从适配器传递数据的全部潜力,但仍然存在一个问题,如果你能指导我完成它,我将很荣幸 :))
编辑 2:
class RecyclerviewFragment : Fragment() {
private lateinit var mUserViewModel: UserViewModel
private lateinit var adapter: ListAdapter
private var _binding: FragmentRecyclerviewBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentRecyclerviewBinding.inflate(inflater, container, false)
mUserViewModel = ViewModelProvider(this)[UserViewModel::class.java]
adapter = ListAdapter{ user -> showUpdateDialog(user)}
val recyclerView = binding.recyclerView
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(requireContext())
// Creates a controller responsible for swiping and moving the views in recyclerview
val itemTouchController = ItemTouchHelper(
object : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP or ItemTouchHelper.DOWN, ItemTouchHelper.LEFT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: ViewHolder, target: ViewHolder,
): Boolean {
// Move specific item from "fromPos" to "toPos" in recyclerview adapter
val fromPos = viewHolder.bindingAdapterPosition
val toPos = target.bindingAdapterPosition
adapter.notifyItemMoved(fromPos, toPos)
return true // true if moved, false otherwise
}
override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
mUserViewModel.deleteUser(adapter.getTaskPosition(viewHolder.bindingAdapterPosition))
Toast.makeText(context, "Task deleted", Toast.LENGTH_SHORT).show()
adapter.notifyItemRemoved(viewHolder.bindingAdapterPosition)
}
})
itemTouchController.attachToRecyclerView(binding.recyclerView)
mUserViewModel.readAllData.observe(viewLifecycleOwner) { user ->
adapter.setData(user)
}
return binding.root
}
@SuppressLint("NotifyDataSetChanged")
private fun updateItemInDatabase(user: User) {
val editText = view?.findViewById<EditText>(R.id.editTextDialog)
val task = editText?.text.toString()
if(inputCheck(task)) {
// Update an entity
mUserViewModel.updateUser(user)
Toast.makeText(context, "Task updated", Toast.LENGTH_SHORT).show()
adapter.notifyDataSetChanged()
}
else {
Toast.makeText(context, "Please fill out required fields", Toast.LENGTH_SHORT).show()
}
}
private fun inputCheck(task: String): Boolean {
return !(TextUtils.isEmpty(task))
}
private fun showUpdateDialog(user: User) {
MaterialAlertDialogBuilder(requireContext())
.setView(R.layout.fragment_add)
.setNegativeButton(getString(R.string.cancel)) { _, _ ->
// Respond to negative button press
Toast.makeText(context, getString(R.string.cancelled), Toast.LENGTH_SHORT).show()
}
.setPositiveButton(getString(R.string.ok)) { _, _ ->
// Respond to positive button press
val taskText = view
?.findViewById<EditText>(R.id.editTextDialog)
?.text?.toString()
updateItemInDatabase(User(user.id, taskText.toString()))
}
.show()
}
}
使用此代码,该功能似乎可以正常工作,但是,它无法访问 taskText 值?如果我尝试编辑模拟器中的任何任务,它会更新为“空”提供 ViewModel,但我认为那里没有问题。它可能植根于价值本身的某个地方。
用户视图模型.kt:
class UserViewModel(application: Application) : AndroidViewModel(application) {
val readAllData: LiveData<List<User>>
private val repository: UserRepository
init {
val userDao = UserDatabase.getDatabase(application).userDao()
repository = UserRepository(userDao)
readAllData = repository.readAllData
}
fun addUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
repository.addUser(user)
}
}
fun updateUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
repository.updateUser(user)
}
}
fun deleteUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
repository.deleteUser(user)
}
}
}
【问题讨论】:
-
ViewHolder是你的Adapter处理的内部事务——Fragment根本不应该直接与他们交互。你的Adapter应该通过了事件对听众来说,比如删除此项管他呢。如果您的Fragment处理了这些事件,它会显示一个确认对话框,然后调用ViewModel上的deleteItem(item)方法。把它想象成流动的事件适配器 -> 片段 -> VM -> 存储库等等,你不需要“回去”问适配器它又在看哪个项目 -
我不认为你理解这个问题。删除方法非常有效,但是,如果我尝试在编辑对话框中按下“确定”按钮,一切都会崩溃,导致 viewHolder 未初始化。
-
为什么你的
updateItemInDatabase函数需要访问ViewHolder?它没有被初始化,因为它是Fragment中的局部变量,而且您还没有将它设置为任何值。但是你无论如何不应该那样做因为 Fragment 不应该像那样在 Adapter 的内部闲逛。如果您要保留对任何内容的引用,只需首先从适配器存储您想要的内容 - 在本例中,是getTaskPosition的结果。当用户单击删除或其他任何操作时(从适配器)将其传递出去,并在确认对话框时使用它
标签: android