Search code examples
androidkotlinandroid-recyclerviewheader

Android - Implementing Headers in RecyclerView


I am trying to implement headers for sections with RecyclerView. I started with a Ray Wenderlich tutorial by Kevin Moore which I am trying to adapt to a basic RecyclerView that I had working. The issue is that the app crashes when calling the ItemHolder from onCreateViewHolder with the logcat message

'java.lang.IllegalStateException: view.findViewById(R.id.item_name) must not be null'.

 class MenuFragment : Fragment() {

    private lateinit var menuRecyclerView: RecyclerView
    private var adapter: MenuAdapter? = null

    // private lateinit var menuViewModel: MenuViewModel // original ref to model
    private val menuViewModel: MenuViewModel by lazy {
        ViewModelProviders.of(this).get(MenuViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       Log.d("TAG", "Menu Goes Here!")
    }

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

       menuRecyclerView = view.findViewById(R.id.menu_recycler_view) as RecyclerView
       menuRecyclerView.layoutManager = LinearLayoutManager(context)

       updateUI()

       return view
    }

    private fun updateUI() {
       val menuItems = menuViewModel.menuRowItems
       adapter = MenuAdapter(menuItems)
       menuRecyclerView.adapter = adapter
    }

    companion object {
       fun newInstance(): MenuFragment {
           return MenuFragment()
       }
    }

    private inner class ItemHolder(view: View)
       :RecyclerView.ViewHolder(view) {

      val itemTitleTextView: TextView = view.findViewById(R.id.item_name)
      val headerTitleTextView: TextView = view.findViewById(R.id.header_text)

   }

   private inner class MenuAdapter(var menuRowItems: List<MenuRow>) : RecyclerView.Adapter<ItemHolder>() {

      override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {

          // val layoutInflater = LayoutInflater.from(parent.context)

            val layoutInflater = LayoutInflater.from(context)


            val inflatedView : View = when (viewType) {
                MenuItem.NAME.ordinal -> layoutInflater.inflate(R.layout.list_item_menu, parent,false)
                else -> layoutInflater.inflate(R.layout.header_item, parent,false)
            }
            return ItemHolder(inflatedView)
       }

       override fun getItemCount() = menuRowItems.size

       override fun onBindViewHolder(holder: ItemHolder, position: Int) {
           val menuRow: MenuRow = menuRowItems[position]
           if (menuRow.type == MenuItem.HEADER) {
               holder.apply { headerTitleTextView.text = menuRow.itemName }
           } else {
               holder.apply {
                itemTitleTextView.text = menuRow.itemName
               }
           }
       }

       override fun getItemViewType(position: Int) = menuRowItems[position].type.ordinal
     }
   }

   enum class MenuItem {
     NAME,
     HEADER
   }
   data class MenuRow(var type: MenuItem, var itemName: String)

Solution

  • You don't need to use findViewById in kotlin:

    Look at this simple example of RecyclerView adapter,

    class AnimalAdapter(val items : ArrayList<String>, val context: Context) : RecyclerView.Adapter<ViewHolder>() {
    
      // Gets the number of animals in the list
      override fun getItemCount(): Int {
          return items.size
      }
    
      // Inflates the item views
      override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder 
      {
          return ViewHolder(LayoutInflater.from(context).inflate(R.layout.animal_list_item, parent, false))
      }
    
      // Binds each animal in the ArrayList to a view
      override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
          holder?.tvAnimalType?.text = items.get(position)
      }
    }
    
    class ViewHolder (view: View) : RecyclerView.ViewHolder(view) {
        // Holds the TextView that will add each animal to
        val tvAnimalType = view.tv_animal_type
    }