androidkotlinandroid-recyclerviewnestedrecyclerview

Why am I getting Null pointer Exception In my Adapter class?


I am using Recycler view with multiple views types in my project. Now when i scroll up and down the recycler view, my app crashes with a Null Pointer Exception. Here is my code of Main Adapter class.

 class MainFragmentAdapter(private val myContext: MainFragment, myCallBack: MainFragment): RecyclerView.Adapter<RecyclerView.ViewHolder>(){

var gold = mutableListOf<Item>()
var silver = mutableListOf<Item>()
var advertiseBanner = mutableListOf<Window>()
var category = mutableListOf<Window>()
var mRate = mutableListOf<Rate>()
var womenSection = mutableListOf<Window>()
var menSection = mutableListOf<Window>()
var myDiscountBanner : Window ?= null
var myTrendingBanner : Window ?= null
var myFestivalBanner : Window ?= null
private val viewPool = RecyclerView.RecycledViewPool()

private val methodCallBack : MethodCallBack
init {
    methodCallBack = myCallBack
}


override fun getItemViewType(position: Int): Int {
    return position
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
   return when(viewType){
        11-> {
           val binding = NewRecyclerviewBinding.inflate(LayoutInflater.from(parent.context),parent,false)
           NewRecyclerViewBindingHolder(binding)
       }
        10-> {
           val binding = AdvertiseBinding.inflate(LayoutInflater.from(parent.context),parent,false)
           AdvertiseHolder(binding)
        }
        9-> {
           val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
           DiamondHolder(binding)
        }
        8 -> {
            val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            DiamondHolder(binding)
        }
        7 -> {
            val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            DiamondHolder(binding)
        }
        6 -> {
            val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            DiamondHolder(binding)
        }
        5->{
            val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            DiamondHolder(binding)
        }
        4->{
            val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            DiamondHolder(binding)
        }
        3 ->{
            val binding = DiamondBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            DiamondHolder(binding)
        }
        2 ->{
           val binding = JewelleryPromisesBinding.inflate(LayoutInflater.from(parent.context),parent,false)
           JewelleryPromisesHolder(binding)
        }
        1 ->{
            val binding = JewelleryPromisesBinding.inflate(LayoutInflater.from(parent.context),parent,false)
            JewelleryPromisesHolder(binding)
        }
       else ->{
           val binding = JewelleryPromisesBinding.inflate(LayoutInflater.from(parent.context),parent,false)
           JewelleryPromisesHolder(binding)
       }
   }
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    when (position) {
        11 ->{
            with(holder as NewRecyclerViewBindingHolder){
                val categoryAdapter = CategoryAdapter()
                binding.newRecyclerview.apply {
                    adapter = categoryAdapter
                    setHasFixedSize(true)
                    setRecycledViewPool(viewPool)
                }
                categoryAdapter.submitList(category)
                categoryAdapter.setListener(myContext)
            }
        }
        10 -> {
            with(holder as AdvertiseHolder){
                val monthly = advertiseBanner
                val monthlyAdapter = MonthlyAdapter(myContext.requireContext())
                monthlyAdapter.monthlyList = monthly
                binding.myPager.adapter = monthlyAdapter
                methodCallBack.addDotsIndicator(0,binding.linearDot,monthly.size)
                binding.myPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{
                    override fun onPageScrolled(
                        position: Int,
                        positionOffset: Float,
                        positionOffsetPixels: Int
                    ) {

                    }

                    override fun onPageSelected(position: Int) {
                        methodCallBack.addDotsIndicator(
                            position,
                            binding.linearDot,
                            monthly.size
                        )
                    }

                    override fun onPageScrollStateChanged(state: Int) {

                    }
                })
            }
          }

          9-> {
              with(holder as DiamondHolder){
                  if (gold.isNotEmpty()){
                      val goldAdapter = ProductAdapter(myContext)
                      goldAdapter.setListener(myContext)
                      if (mRate.isNotEmpty())
                          goldAdapter.rate = mRate[0].value!!
                      binding.diamondText.text = myContext.getText(R.string.gold)
                      binding.diamondRecycler.apply {
                          setHasFixedSize(true)
                          adapter = goldAdapter
                          setRecycledViewPool(viewPool)
                      }
                      goldAdapter.submitList(gold)
                  }
                  binding.viewMoreButton.setOnClickListener {
                      methodCallBack.click(gold)
                  }
              }
        }

        8 -> {
            with(holder as DiamondHolder){
                binding.diamondText.visibility = View.GONE
                binding.containerName.visibility = View.GONE
                binding.diamondRecycler.visibility = View.GONE
                binding.viewMoreButton.visibility = View.GONE
                binding.showAll.visibility = View.GONE
                binding.jewelleryBanner.visibility = View.VISIBLE
                binding.bannerImage.loadImage(myDiscountBanner?.url)
            }
        }

        7->{
            with(holder as DiamondHolder){
                if (womenSection.isNotEmpty()){
                    val genderAdapter = SectionAdapter()
                    genderAdapter.genderType = "women"
                    binding.viewMoreButton.visibility = View.GONE
                    binding.containerName.text = myContext.getText(R.string.women_section)
                    binding.containerName.visibility = View.VISIBLE
                    binding.showAll.visibility = View.VISIBLE
                    binding.diamondRecycler.apply {
                        layoutManager = GridLayoutManager(myContext.requireContext(), 3)
                        adapter = genderAdapter
                        setHasFixedSize(true)
                        setRecycledViewPool(viewPool)
                    }
                    genderAdapter.submitList(womenSection)
                    genderAdapter.setListener(myContext)
                }
            }
        }

        6 -> {
            with(holder as DiamondHolder){
                binding.diamondText.visibility = View.GONE
                binding.containerName.visibility = View.GONE
                binding.diamondRecycler.visibility = View.GONE
                binding.viewMoreButton.visibility = View.GONE
                binding.showAll.visibility = View.GONE
                binding.jewelleryBanner.visibility = View.VISIBLE
                binding.bannerImage.loadImage(myTrendingBanner?.url)
            }
        }

        5->{
            with(holder as DiamondHolder){
                if (menSection.isNotEmpty()){
                    val genderAdapter = SectionAdapter()
                    genderAdapter.genderType = "men"
                    binding.viewMoreButton.visibility = View.GONE
                    binding.containerName.text = myContext.getText(R.string.men_section)
                    binding.containerName.visibility = View.VISIBLE
                    binding.showAll.visibility = View.VISIBLE
                    binding.diamondRecycler.apply {
                        layoutManager = GridLayoutManager(myContext.requireContext(), 3)
                        adapter = genderAdapter
                        setHasFixedSize(true)
                        setRecycledViewPool(viewPool)
                    }
                    genderAdapter.submitList(menSection)
                    genderAdapter.setListener(myContext)
                }
            }
        }

         4 -> {
             with(holder as DiamondHolder){
                 binding.diamondText.visibility = View.GONE
                 binding.containerName.visibility = View.GONE
                 binding.diamondRecycler.visibility = View.GONE
                 binding.viewMoreButton.visibility = View.GONE
                 binding.showAll.visibility = View.GONE
                 binding.jewelleryBanner.visibility = View.VISIBLE
                 binding.bannerImage.loadImage(myFestivalBanner?.url)
             }
        }

         3->{
             with(holder as DiamondHolder){
                 if (silver.isNotEmpty()){
                     val goldAdapter = ProductAdapter(myContext)
                     goldAdapter.setListener(myContext)
                     if (mRate.isNotEmpty())
                         goldAdapter.rate = mRate[1].value!!
                     binding.diamondText.text = myContext.getText(R.string.silver)
                     binding.diamondRecycler.apply {
                         setHasFixedSize(true)
                         adapter = goldAdapter
                         setRecycledViewPool(viewPool)
                     }
                     goldAdapter.submitList(silver)
                 }
                 binding.viewMoreButton.setOnClickListener {
                     methodCallBack.click(silver)
                 }
             }
        }
        2->{
            with(holder as JewelleryPromisesHolder){
                binding.headerTitle.visibility = View.VISIBLE
                binding.table.visibility = View.VISIBLE
                binding.socialFeed.visibility = View.GONE
                binding.instagramIcon.visibility = View.GONE
                binding.whatsappIcon.visibility = View.GONE
                binding.youtubeIcon.visibility = View.GONE
                binding.facebookIcon.visibility = View.GONE
            }
        }
        1 ->{
            with(holder as JewelleryPromisesHolder){
                binding.headerTitle.visibility = View.GONE
                binding.table.visibility = View.GONE
                binding.socialFeed.visibility = View.VISIBLE
                binding.instagramIcon.visibility = View.VISIBLE
                binding.whatsappIcon.visibility = View.VISIBLE
                binding.youtubeIcon.visibility = View.VISIBLE
                binding.facebookIcon.visibility = View.VISIBLE
            }
        }
        else -> {
            with(holder as JewelleryPromisesHolder){
                binding.headerTitle.visibility = View.GONE
                binding.table.visibility = View.GONE
                binding.socialFeed.visibility = View.GONE
                binding.instagramIcon.visibility = View.GONE
                binding.whatsappIcon.visibility = View.GONE
                binding.youtubeIcon.visibility = View.GONE
                binding.facebookIcon.visibility = View.GONE
                binding.whatsappBanner.visibility = View.VISIBLE
            }
        }
    }
}

override fun getItemCount() = 12

inner class DiamondHolder (val binding : DiamondBinding) : RecyclerView.ViewHolder(binding.root)

inner class NewRecyclerViewBindingHolder (val binding : NewRecyclerviewBinding): RecyclerView.ViewHolder(binding.root)

inner class AdvertiseHolder(val binding : AdvertiseBinding) : RecyclerView.ViewHolder(binding.root)

inner class JewelleryPromisesHolder(val binding : JewelleryPromisesBinding) : RecyclerView.ViewHolder(binding.root)



interface MethodCallBack{
    fun click(productList : List<Item>)
    fun click()
    fun addDotsIndicator(position: Int, linearDot: LinearLayout, size: Int)
}

}

Here are my Adapter Classes codes :-

Category Adapter

 class CategoryAdapter : RecyclerView.Adapter<CategoryAdapter.HorizontalHolder>() {
private var oldList = mutableListOf<Window>()
private var childListener : CategoryInterface ?= null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HorizontalHolder {
    val binding = HorizontalCategoryBinding.inflate(LayoutInflater.from(parent.context),parent,false)
    return HorizontalHolder(binding)
}

override fun onBindViewHolder(holder: HorizontalHolder, position: Int) {
    with(holder){
        if(oldList.isNotEmpty()) {
            with(oldList[position]) {
                binding.categoryImage.loadImage(this.url)
                binding.categoryName.text = this.sub_type
                binding.categoryImage.setOnClickListener {
                    childListener?.onClick(this.sub_type!!, "all")
                }
            }
        }
    }
}

fun setListener(listener : MainFragment){
    childListener = listener
}

fun submitList(newList : MutableList<Window>){
    val diffUtil = WindowDiffUtil(oldList ,newList)
    val diffResult = DiffUtil.calculateDiff(diffUtil)
    oldList = newList
    diffResult.dispatchUpdatesTo(this)
}


override fun getItemCount() = oldList.size

inner class HorizontalHolder(val binding : HorizontalCategoryBinding) : RecyclerView.ViewHolder(binding.root)
}

Product Adapter

class ProductAdapter(private val context : MainFragment) : RecyclerView.Adapter<ProductAdapter.ProductHolder>(){
var itemList = mutableListOf<Item>()
var rate = 0
private var childListener : ChildInterface ?= null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductHolder {
    val binding = SilverCardsBinding.inflate(LayoutInflater.from(parent.context),parent,false)
    return ProductHolder(binding)
}


override fun onBindViewHolder(holder: ProductHolder, position: Int) {
    with(holder){
        if (itemList.isNotEmpty()) {
            with(itemList[position]) {
                if (rate != 0) {
                    binding.productImage.loadImage(this.images?.get(0))
                    binding.itemName.text = this.name
                    var weight = this.net_weight
                    if (weight != null) {
                        weight *= (rate)
                    }
                    val newWeight = weight?.toInt()?.plus(this.labour!!)
                    binding.price.text =
                        context.resources.getString(R.string.Rs, newWeight?.toString())
                    holder.itemView.setOnClickListener {
                        childListener?.onClick(this, "SelectedProduct")
                    }
                }
            }
        }
    }
}

fun submitList(newList : MutableList<Item>){
    val diffUtil = ItemDiffUtil(itemList ,newList)
    val diffResult = DiffUtil.calculateDiff(diffUtil)
    itemList = newList
    diffResult.dispatchUpdatesTo(this)
}



fun setListener(listener : MainFragment){
    childListener = listener
}

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



inner class ProductHolder(val binding : SilverCardsBinding): RecyclerView.ViewHolder(binding.root)


}

SectionAdapter

 class SectionAdapter : RecyclerView.Adapter<SectionAdapter.SectionHolder>(){
private var oldList = mutableListOf<Window>()
var genderType : String ?= null
private var childListener : CategoryInterface?= null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionHolder {
    val binding = SectionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
    return SectionHolder(binding)
}

override fun onBindViewHolder(holder: SectionHolder, position: Int) {
    if (oldList.isNotEmpty()) {
        with(holder){
            with(oldList[position]) {
                binding.sectionImage.loadImage(this.url)
                binding.sectionName.text = this.sub_type
                holder.itemView.setOnClickListener {
                    childListener?.onClick(this.sub_type!!, genderType!!)
                }
            }
        }
    }
}

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

fun setListener(listener : MainFragment){
    childListener = listener
}

fun submitList(newList : MutableList<Window>){
    val diffUtil = WindowDiffUtil(oldList ,newList)
    val diffResult = DiffUtil.calculateDiff(diffUtil)
    oldList = newList
    diffResult.dispatchUpdatesTo(this)
}

inner class SectionHolder (val binding : SectionBinding) : RecyclerView.ViewHolder(binding.root){

 }

}

Now when I scroll fast, I get Null Pointer Exception. These are the exceptions.

java.lang.ClassCastException: com.example.groceryapp.mainscreen.CategoryAdapter$HorizontalHolder cannot be cast to com.example.groceryapp.mainscreen.SectionAdapter$SectionHolder
                                                                                                        at com.example.groceryapp.mainscreen.SectionAdapter.onBindViewHolder(SectionAdapter.kt:14)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7254)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7337)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6194)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6460)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6300)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6296)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)
                                                                                                        at androidx.recyclerview.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:572)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:668)
                                                                                                        at androidx.recyclerview.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:170)
                                                                                                        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4309)
                                                                                                        at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3686)
                                                                                                        at android.view.View.measure(View.java:24530)
                                                                                                        at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811)
                                                                                                        at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:632)
                                                                                                        at androidx.constraintlayout.core.widgets.analyzer.Direct.horizontalSolvingPass(Direct.java:323)
                                                                                                        at androidx.constraintlayout.core.widgets.analyzer.Direct.solveHorizontalMatchConstraint(Direct.java:709)
                                                                                                        at androidx.constraintlayout.core.widgets.analyzer.Direct.horizontalSolvingPass(Direct.java:374)
                                                                                                        at androidx.constraintlayout.core.widgets.analyzer.Direct.solvingPass(Direct.java:144)
                                                                                                        at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.layout(ConstraintWidgetContainer.java:693)
                                                                                                        at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solveLinearSystem(BasicMeasure.java:160)
                                                                                                        at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:291)
                                                                                                        at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:120)
                                                                                                        at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1594)
                                                                                                        at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1708)
                                                                                                        at android.view.View.measure(View.java:24530)
                                                                                                        at androidx.recyclerview.widget.RecyclerView$LayoutManager.measureChildWithMargins(RecyclerView.java:9681)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1657)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1395)
                                                                                                        at androidx.recyclerview.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1136)
                                                                                                        at androidx.recyclerview.widget.RecyclerView.scrollStep(RecyclerView.java:1972)
                                                                                                        at androidx.recyclerview.widget.RecyclerView.scrollByInternal(RecyclerView.java:2071)
                                                                                                        at androidx.recyclerview.widget.RecyclerView.onTouchEvent(RecyclerView.java:3531)
                                                                                                        at android.view.View.dispatchTouchEvent(View.java:13415)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3054)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2741)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
2022-10-25 22:09:51.133 26193-26193 AndroidRuntime          com.example.groceryapp               E      at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
                                                                                                        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)
                                                                                                        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)
                                                                                                        
.....                                                                             



     

Solution

  • This is why it's crashing:

    java.lang.NullPointerException: holder.itemView.category_image must not be null
        at com.example.groceryapp.mainscreen.SectionAdapter.onBindViewHolder(SectionAdapter.kt:27)
    

    At line 27 in onBindViewHolder in your SectionAdapter class, you're referencing holder.itemView.category_image, which is null. It's crashing because you're trying to do something with it, probably where you try to call loadImage() on it (and null doesn't have a loadImage function).

    Since you're just accessing IDs directly on the View (i.e. itemView.someId) instead of using findViewById or View Binding, I'm assuming you're using the (deprecated) Android Extensions (you have something like kotlinx.android.synthetic. in your imports).

    That lets you "magically" access views by their ID without having to actually look them up first. It also means that if there is no view with that ID in the layout, you won't get warned about it until it fails at runtime. So check that R.layout.section actually has a... whatever widget you're using that has a loadImage function, with an ID of category_image.

    If you do have something with that ID in there, and it really does work fine if you scroll slowly, maybe it's a flaw with how the "magical" synthetic binding works - I'm not sure if it would be an issue, but it is a flawed system and that's why they moved away from it. And consider moving to View Binding instead. There's a migration guide here