【问题标题】:Why can I not inherit from ListAdapter with my custom defined class in Kotlin?为什么我不能使用 Kotlin 中的自定义类从 ListAdapter 继承?
【发布时间】:2022-09-24 03:00:13
【问题描述】:

我正在开发一个 Todo-list 作为 Android 应用程序以开始使用 Kotlin,但我遇到了问题,即我的 TodoAdapter 类(据我所知,它应该定义如何在回收站视图中处理所述 Todos ?) 由于某种原因,不能从 ListAdapter 类继承。

在我尝试通过保存到一个简单的 .txt 文件作为开始来为我的应用程序添加持久性之前,我相信我没有遇到问题。请在下面查看我的代码并帮助我修复我的代码。

我的 TodoAdapter 类:

class TodoAdapter (
    private val todos: MutableList<Todo>
) : ListAdapter<Todo,TodoAdapter.TodoViewHolder>() {

    class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        return TodoViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_todo,
                parent,
                false
            )
        )
    }

    fun addTodo(todo: Todo) {
        todos.add(todo)
        notifyItemInserted(todos.size - 1)
    }

    fun deleteDoneTodos() {
        todos.removeAll { todo ->
            todo.isChecked
        }
        notifyDataSetChanged()
    }

    private fun toggleStrikeThrough(tvTodoTitle: TextView, isChecked: Boolean) {
        if (isChecked) {
            tvTodoTitle.paintFlags = tvTodoTitle.paintFlags or STRIKE_THRU_TEXT_FLAG
        } else{
            tvTodoTitle.paintFlags = tvTodoTitle.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
        }
    }

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val curTodo = todos[position]
        holder.itemView.apply {
            tvTodoTitle.text = curTodo.title        //Hier stimmt etwas nicht: tvTodoTitle Import fehlt???
            cbDone.isChecked = curTodo.isChecked
            toggleStrikeThrough(tvTodoTitle, curTodo.isChecked)
            cbDone.setOnCheckedChangeListener{ _, isChecked ->
                toggleStrikeThrough(tvTodoTitle, isChecked)
                curTodo.isChecked = !curTodo.isChecked
            }
        }
    }

    override fun getItemCount(): Int {
        return todos.size
    }

我的数据类 Todo:

data class Todo(
    val title: String,
    var isChecked: Boolean = false
)

这是我尝试添加持久性的 MainActivity.kt 中的代码:

    private fun setupInternalStorageRecyclerView() = binding.rvTodoItems.apply {
    adapter = todoAdapter
    layoutManager = rvTodoItems.layoutManager
}

private fun loadTodoItemsFromInternalStorageIntoRecyclerView() {
    lifecycleScope.launch {
        val todoItems = loadTodoItemsFromInternalStorage()
        todoAdapter.submitList(todoItems)
    }

}

private suspend fun loadTodoItemsFromInternalStorage(): List<Todo> {
    return withContext(Dispatchers.IO) {
        val todoItemList: MutableList<Todo> = mutableListOf<Todo>()
        val files = filesDir.listFiles()
        files?.filter { it.canRead() && it.isFile && it.name.endsWith(\".txt\") }?.map {
            val lines = it.bufferedReader().readLines()
            for (i in lines.indices step 2) {
                todoItemList.add(Todo(lines[i], lines[i+1].toBoolean()))
            }
            todoItemList
        } ?: mutableListOf<Todo>()
    } as MutableList<Todo>
}

private fun saveTodoItemsToInternalStorage(filename: String, todoItems: List<Todo>): Boolean {
    return try{
        openFileOutput(\"$filename.txt\", MODE_PRIVATE).use { stream ->
            File(filename).printWriter().use { out ->
                for (item in todoItems) {
                    out.println(item.title)
                    out.println(item.isChecked)
                }
            }
        }
        true
    } catch(e: IOException) {
        e.printStackTrace()
        false
    }
}

我希望这是足够的信息来帮助我,请随时询问更多信息,我很乐意提供。

  • 错误消息:不能使用提供的参数调用以下函数。 <init>(AsyncDifferConfig<Todo!>) where T = TypeVariable(T), VH = TypeVariable(VH) for constructor ListAdapter<T : Any!, VH : RecyclerView.ViewHolder!>(config: AsyncDifferConfig<T!>) 定义在 androidx.recyclerview.widget.ListAdapter <init>(DiffUtil.ItemCallback<Todo!>) where T = TypeVariable(T), VH = TypeVariable(VH) for constructor ListAdapter<T: Any!, VH: RecyclerView.ViewHolder!> (diffCallback: DiffUtil.ItemCallback<T!>) 定义在 androidx.recyclerview.widget.ListAdapter

标签: android kotlin android-recyclerview android-listadapter


【解决方案1】:

首先,无论您是否使用 ListAdapter,您遇到的一个主要问题是您正在使用适配器来管理您的实际数据。您不得使用适配器作为数据的“主副本”,否则在为屏幕旋转重建 UI 或有人在后台返回应用程序时,您的数据将丢失。您的数据应由 ViewModel 管理,列表实例应由 Activity 或 Fragment 传递给适配器。您修改列表的函数(例如 addTodo())应该在您的 ViewModel 中,而不是在您的适配器中。

关于您的具体问题,快速而肮脏的解决方案是从 RecyclerView.Adapter 而不是 ListAdapter 继承:

class TodoAdapter (
    private val todos: MutableList<Todo>
) : RecyclerView.Adapter<TodoAdapter.TodoViewHolder>() {

ListAdapter 需要做更多的设置工作,但它的优点是当您更新列表内容时,它会在后台线程上进行自动比较,因此它可以自动准确地找到已更改的内容并为您的列表设置动画更改。如果你想使用 ListAdapter,你必须为它定义一个 DiffUtil.ItemCallback 并将它传递给它的构造函数。通常,您的 Todo 类将被定义为不可变(无 vars)data class,然后您可以定义回调,如:

// Inside your Todo class define: 
object DiffCallback: DiffUtil.ItemCallback<Todo>() {
    override fun areItemsTheSame(oldItem: Todo, newItem: Todo) =
        oldItem.id == newItem.id

    override fun areContentsTheSame(oldItem: Todo, newItem: Todo) =
        oldItem == newItem
}

使用 ListAdapter,您无需使用自己的列表属性。您必须通过submitList 将只读列表传递给它。所以类定义看起来像:

class TodoAdapter: ListAdapter<TodoAdapter.TodoViewHolder>(Todo.DiffCallback) {

要正确使用 ListAdapter,您需要了解更多内容,因此如果您想使用它,您应该阅读教程。例如:您不得向其提交可变列表。每次调用 submitList() 时它都需要一个新的列表实例,以便它可以将新版本与以前的版本进行比较。您的 Todo 类也不能有任何可变属性。您的行 val curTodo = todos[position] 需要更改为 val curTodo = item

【讨论】:

    猜你喜欢
    • 2015-06-20
    • 1970-01-01
    • 2010-10-20
    • 1970-01-01
    • 2014-09-09
    • 2021-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    相关资源
    最近更新 更多