Android Studio Kotlin, notifyItemChanged not calling OnBindViewHolder

1.4k views Asked by At

I am trying to make a game of life grid based game in Android Studio using kotlin. Essentially, every second the grid will update based off a set of rules. However, I can not for the life of me get the grid to update.

For some reason, notifyItemChanged() is not calling onBindViewHolder to properly update the grid visually. I was able to find a work around for some cases, that being calling onBindViewHolder specifically and passing through the current cellViewHolder and it's position, but this won't get me the result I am looking for. Specifically, in the function I declared testUpdate(), I need to be able to change an array of data that the grid is based on, and then from that function notify the grid that changes were made and it should update. However, notifyItemChanged() as well as notifyDatasetChanged() both do not call onBindViewHolder(). I am at a loss, any help would be much appreciated!

I am very new to Kotlin, so I apologize if this code is horrible.

Here is my code:

package com.example.project2

import android.graphics.Color
import android.os.Bundle
import android.text.Layout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.os.Handler;

class BoardFragment : Fragment() {
    private lateinit var recyclerView: RecyclerView
    var gridArray = IntArray(400)
    val handler = Handler()
    var isRunning: Boolean = false
    val colorRed = Color.RED
    val colorBlue = Color.BLUE

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

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.board_fragment, container, false)

        val startButton = view.findViewById(R.id.start_button) as Button

        val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView
        recyclerView.layoutManager = GridLayoutManager(activity, 20)
        recyclerView.adapter = GridAdapter()

        startButton.setOnClickListener {
            isRunning = !isRunning
            if (isRunning) {
                testUpdate()
            }
        }

        return view
    }

    fun testUpdate() {
        handler.postDelayed({
            if (gridArray[3] == 0) {
                gridArray[3] = 1
            } else {
                gridArray[3] = 0
            }

            if (isRunning) {
                testUpdate()
            }
            GridAdapter().notifyItemChanged(3)
        }, 1000)
    }

    override fun onStart() {
        super.onStart()
    }

    private inner class CellViewHolder(cellView: View): RecyclerView.ViewHolder(cellView) {
        val button: ImageButton

        init {
            button = cellView.findViewById(R.id.cell_button)

            button.setColorFilter(null)
            button.setOnClickListener {
                if (gridArray[position] == 0) {
                    gridArray[position] = 1
                } else {
                    gridArray[position] = 0
                }
                GridAdapter().notifyItemChanged(position)
                GridAdapter().onBindViewHolder(this, position)
            }
        }

        fun display(position: Int) {

        }
    }

    private inner class GridAdapter: RecyclerView.Adapter<CellViewHolder>() {

        final val NUM_CELLS: Int = 400

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CellViewHolder {
            val inflater: LayoutInflater = LayoutInflater.from(parent.context)
            val cellView: View = inflater.inflate(R.layout.board_cell, parent, false)
            return CellViewHolder(cellView)
        }

        override fun onBindViewHolder(holder: CellViewHolder, position: Int) {
            holder.display(position)
            if (gridArray[position] == 0) {
                holder.button.setColorFilter(null)
            } else {
                holder.button.setColorFilter(Color.RED)
            }

        }

        override fun getItemCount(): Int {
            return NUM_CELLS
        }
    }

}
1

There are 1 answers

1
Meggrain On

I don't know what you're trying to achieve but a few tip

  1. notifyItemChange() should be called from the Adapter class.
  2. You're creating a new instances of GridAdapter GridAdapter().notifyItemChanged(position)

change

GridAdapter().notifyItemChanged(position)

to

recyclerView.adapter.notifyItemChanged(position)

modify your class like this

package com.example.project2

import android.graphics.Color
import android.os.Bundle
import android.text.Layout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.os.Handler;

class BoardFragment : Fragment() {
    private lateinit var recyclerView: RecyclerView
    var gridArray = IntArray(400)
    val handler = Handler()
    var isRunning: Boolean = false
    val colorRed = Color.RED
    val colorBlue = Color.BLUE

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

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.board_fragment, container, false)

        val startButton = view.findViewById(R.id.start_button) as Button

        val recyclerView = view.findViewById(R.id.recycler_view) as RecyclerView

        recyclerView.layoutManager = GridLayoutManager(activity, 20)
        
        recyclerView.adapter = GridAdapter()

        startButton.setOnClickListener {
            isRunning = !isRunning
            if (isRunning) {
                testUpdate()
            }
        }

        return view
    }

    fun testUpdate() {
        handler.postDelayed({
            if (gridArray[3] == 0) {
                gridArray[3] = 1
            } else {
                gridArray[3] = 0
            }

            if (isRunning) {
                testUpdate()
            }
            recyclerView.adapter.notifyItemChanged(3)
        }, 1000)
    }

    override fun onStart() {
        super.onStart()
    }

    private inner class CellViewHolder(cellView: View): RecyclerView.ViewHolder(cellView) {
        val button: ImageButton

        init {
            button = cellView.findViewById(R.id.cell_button)

            button.setColorFilter(null)
            button.setOnClickListener {
                if (gridArray[position] == 0) {
                    gridArray[position] = 1
                } else {
                    gridArray[position] = 0
                }

            }
        }

        fun display(position: Int) {

        }
    }

    private inner class GridAdapter: RecyclerView.Adapter<CellViewHolder>() {

        final val NUM_CELLS: Int = 400

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CellViewHolder {
            val inflater: LayoutInflater = LayoutInflater.from(parent.context)
            val cellView: View = inflater.inflate(R.layout.board_cell, parent, false)
            return CellViewHolder(cellView)
        }

        override fun onBindViewHolder(holder: CellViewHolder, position: Int) {
            
            if (gridArray[position] == 0) {
                holder.button.setColorFilter(null)
            } else {
                holder.button.setColorFilter(Color.RED)
            }

      holder.display(position)
recyclerView.adapter.notifyItemChanged(3)
        }

        override fun getItemCount(): Int {
            return NUM_CELLS
        }
    }

}