ArrayindexOutOfBounds Exception странно пытался много раз рефакторить проблему Kotlin

В приложении есть нижняя навигация по двум вкладкам: одна для задач, а другая для заметок.

Я не получаю ошибки при запуске, так как он начинается с загрузки задач. но когда я нажимаю вкладку заметки, он вылетает.

ArrayindexOutOfBounds Exception странно пытался много раз рефакторить проблему Kotlin

вычли 1 из позиции мастер-листа, поскольку я добавил кнопку вверху (1-я позиция в массиве), думая, что это исправит ее.

Класс BaseRecyclerAdapter

abstract class BaseRecyclerAdapter<T>(
    protected val masterList: MutableList<T> = mutableListOf()
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {


    override fun getItemCount(): Int = masterList.size + 1

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is TaskAdapter.AddButtonViewHolder) {
            holder.onBind(Unit)
        } else {
            (holder as BaseViewHolder<T>).onBind(masterList[position - 1])
        }
    }


    override fun getItemViewType(position: Int): Int =
        if (position == 0) {
            TYPE_ADD_BUTTON
        } else {
            TYPE_INFO
        }

    abstract class BaseViewHolder<E>(val view: View) : RecyclerView.ViewHolder(view) {
        abstract fun onBind(data: E)
    }

    abstract class AddButtonViewHolder(view: View) : BaseViewHolder<Unit>(view)

    companion object {
        const val TYPE_ADD_BUTTON = 0
        const val TYPE_INFO = 1
    }
}

ПримечанияАдаптер

class NoteAdapter(notesList: MutableList<Note> = mutableListOf()) : BaseRecyclerAdapter<Note>(notesList) {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =

        if (viewType == TYPE_ADD_BUTTON){
            AddButtonViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.view_add_button, parent, false))
        } else {
            NoteViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_note, parent, false))

        }

    override fun getItemViewType(position: Int): Int =
        if (position == 0){
            TYPE_ADD_BUTTON
        }else {
            TYPE_INFO
        }


    override fun getItemCount(): Int = masterList.size + 1


    class NoteViewHolder(view: View) :BaseViewHolder<Note>(view){

        override fun onBind(data: Note) {
            (view as NoteView).initView(data)
        }
    }

    class AddButtonViewHolder(view: View): BaseRecyclerAdapter.AddButtonViewHolder(view){
        override fun onBind(data: Unit) {
            view.buttonText.text = view.context.getText(R.string.add_button_note)
        }

    }
}

NoteListFragment

class NotesListFragment : Fragment() {

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

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_notes_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        recyclerViewNote.layoutManager = LinearLayoutManager(context)
        val adapter = NoteAdapter(mutableListOf(
            Note("Ben Mohammad", null),
            Note("Joesph and and Maria", null)
        ))
        recyclerViewNote.adapter = adapter
    }

    companion object {

        fun newInstance() = NotesListFragment()
    }

}

NoteView

class NoteView @JvmOverloads constructor (context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 1) :
    ConstraintLayout(context, attrs, defStyleAttr){

    fun initView(note: Note){
        description.text = note.description
  }
}

Я использовал вложенные пользовательские представления. Это то, что я пытаюсь сделать ... очевидно, пытаясь сохранить стиль ООП, застрявший с ним ...

class TaskAdapter(
    tasksList: MutableList<Task> = mutableListOf()
) : BaseRecyclerAdapter<Task>(tasksList) {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =

        if (viewType == TYPE_INFO) {
            TaskViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_task, parent, false))
        } else {
            AddButtonViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.view_add_button, parent, false))
        }

    class TaskViewHolder(view: View) : BaseViewHolder<Task>(view) {

        override fun onBind(data: Task) {
            (view as TaskView).initView(data)

        }
    }


    class AddButtonViewHolder(view: View) : BaseRecyclerAdapter.AddButtonViewHolder(view) {
        override fun onBind(data: Unit) {
            view.buttonText.text = view.context.getText(R.string.add_button_task)
        }

    }


}

TasksListfragment

class TasksListFragment : Fragment() {

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

    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_tasks_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        recyclerView.layoutManager = LinearLayoutManager(context)
        val adapter = TaskAdapter(
            mutableListOf(
                Task(
                    "Testing one!!",
                    mutableListOf(
                        Todo("Test 1!!"),
                        Todo("Test 2!!", true)


                    )
                ),
                Task("Testing two!!")
            )
        )
        recyclerView.adapter = adapter
    }

    companion object {


        fun newInstance() = TasksListFragment()
    }

}

Вид задач

class TaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 1) :
    ConstraintLayout(context, attrs, defStyleAttr) {


    lateinit var task: Task

    fun initView(task: Task) {
        this.task = task

        titleView.text = task.title
        task.todos.forEach {

                todo ->
            val todoView =
                (LayoutInflater.from(context).inflate(R.layout.view_todo, todoContainer, false) as TodoView).apply {
                    initView(todo) {
                        if (isTaskcomplete()) {
                            createstrikeThrough()
                        } else {
                            removeStrikeThrough()
                        }

                    }
                }


            todoContainer.addView(todoView)

        }
    }

    fun isTaskcomplete(): Boolean = task.todos.filter { !it.isComplete }.isEmpty()

    private fun createstrikeThrough() {
        titleView.apply {
            paintFlags = paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
        }


    }

    private fun removeStrikeThrough() {
        titleView.apply {
            paintFlags = paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
        }

    }
}

Для дальнейших занятий весь код отправляется на GitHub.

Пожалуйста, прочтите минимальный воспроизводимый пример с акцентом на часть минимальный. Люди не очень хотят рыться в сотнях строк чужого кода, распределенного по разным файлам, для его отладки. Сделайте нас, добровольцев, счастливыми и готовыми помочь вам, постаравшись написать наименьший код, необходимый для воспроизведения этой проблемы.

Adriaan 27.05.2019 15:44
2
2
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема здесь:

if (holder is TaskAdapter.AddButtonViewHolder) { // <--- problematic line
    holder.onBind(Unit)
} else {
    (holder as BaseViewHolder<T>).onBind(masterList[position - 1])
}

У вас есть два разных подкласса BaseRecyclerAdapter: ваш TaskAdapter и ваш NotesAdapter. Когда это вызывается для позиции 0 для вашего NotesAdapter, проверка if (holder is ...) завершится ошибкой, потому что тип NotesAdapter.AddButtonViewHolder.

Это приведет к тому, что выполнение попадет в случай else, и ваша логика masterList[position - 1] завершит запрос индекса -1.

Чтобы обойти это, используйте тип представления вместо проверки подкласса с помощью is. Базовый класс RecyclerView.ViewHolder имеет метод getItemViewType(), который вернет вам все, что вы вернули из своего адаптера. Поскольку логика типа представления определена в вашем базовом классе адаптера, она будет одинаковой независимо от того, какой конкретный тип реализации используется.

if (holder.itemViewType == TYPE_ADD_BUTTON) {
    (holder as AddButtonViewHolder).onBind(Unit)
} else {
    (holder as BaseViewHolder<T>).onBind(masterList[position - 1])
}

Большое спасибо, я принял к сведению, что вопрос должен быть лучше. еще раз спасибо

beni 27.05.2019 16:16

Другие вопросы по теме