Search code examples
kotlinandroid-recyclerviewspinnerandroid-adapterandroid-viewbinding

App crashes with kotlin.UninitializedPropertyAccessException when I get value from Spinner


My app works without errors and updates the field in the firestore when I hard-coded the value that needs to be updated. However, when I the following code to get the value from the spinner to update a field in the firestore, the app crashes.

I am using kotlin and viewBinding.

Following is the error in the Logcat

--------- beginning of crash 2021-06-07 14:00:12.155 23175-23175/com.trad E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.trad, PID: 23175
kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized
    at com.trad.ui.adapters.OrderStatusListAdapter.access$getBinding$p(OrderStatusListAdapter.kt:32)
    at com.trad.ui.adapters.OrderStatusListAdapter$onBindViewHolder$1.onClick(OrderStatusListAdapter.kt:84)
    at android.view.View.performClick(View.java:8160)
    at android.widget.TextView.performClick(TextView.java:16222)
    at android.view.View.performClickInternal(View.java:8137)
    at android.view.View.access$3700(View.java:888)
    at android.view.View$PerformClick.run(View.java:30236)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:246)
    at android.app.ActivityThread.main(ActivityThread.java:8512)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)

2021-06-07 14:00:12.234 23175-23175/com.trad I/Process: Sending signal. PID: 23175 SIG: 9

Following is the OrderStatusListAdapter.kt class The error that is pointing at line number 32 and 84 are mentioned as a comment in the code.

open class OrderStatusListAdapter(         //LINE # 32 MENTIONED IN THE ERROR
        private val context: Context,
        private var list: ArrayList<OrderStatus>,
    
        ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
        lateinit var newOrderStatus: Spinner
        private lateinit var binding: OrderStatusLayoutBinding
    
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    
            return MyViewHolder(
                LayoutInflater.from(context).inflate(
                    R.layout.order_status_layout,
                    parent,
                    false
                )
            )
        }
    
        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
            val model = list[position]
    
            if (holder is MyViewHolder) {
    
                GlideLoader(context).loadProductPicture(
                    model.image,
                    holder.itemView.iv_order_status_item_image
                )
    
                val dateFormat = "dd MMM yyyy HH:mm"
                
                val formatter = SimpleDateFormat(dateFormat, Locale.getDefault())
    
                val calendar: Calendar = Calendar.getInstance()
                calendar.timeInMillis = model.order_datetime
    
                val orderDateTime = formatter.format(calendar.time)
                holder.itemView.tv_order_status_order_date.text = orderDateTime
    
    
                holder.itemView.tv_order_status_item_name.text = model.items[0].title
                holder.itemView.tv_order_status_item_price.text = "$${model.total_amount}"
 model.order_datetime.toString()
                holder.itemView.tv_order_status.text = model.order_status
                holder.itemView.tv_order_status_order_id.text = model.id
    
                holder.itemView.btn_order_status_change_status.setOnClickListener {
    
    binding.spnOrderChangeStatus.onItemSelectedListener = // LINE 84 MENTIONED IN THE ERROR
                        object : AdapterView.OnItemSelectedListener {
                            override fun onItemSelected(
                                parent: AdapterView<*>?,
                                view: View?,
                                position: Int,
                                id: Long
                            ) {
                                Toast.makeText(
                                    context,
                                    "Selected value is ${
                                        parent?.getItemAtPosition(position).toString()
                                    }",
                                    Toast.LENGTH_SHORT
                                ).show()
    
                                val newstat = parent?.getItemAtPosition(position).toString()
                                FirestoreClass().updateOrderStatus(model.id, newstat)
    
                            }
    
                            override fun onNothingSelected(parent: AdapterView<*>?) {
    
                            }
                        }
                }
    
                holder.itemView.ib_order_status_delete_product.visibility = View.GONE
    
                holder.itemView.setOnClickListener {
                    val intent = Intent(context, SoldProductDetailsActivity::class.java)
                    intent.putExtra(Constants.EXTRA_SOLD_PRODUCT_DETAILS, model)
                    context.startActivity(intent)
                }
    
    
            }
    
        }
    
    
        override fun getItemCount(): Int {
            return list.size
        }
    
    
        class MyViewHolder(view: View) : RecyclerView.ViewHolder(view)
    }

Solution

  • kotlin.UninitializedPropertyAccessException: lateinit property binding has not been initialized

    Means that you tried to use the binding property before it being initialized.

    binding should be declared in the place where you define the layout; and this is typically in onCreateViewHolder() callback at which you defined the default inflater without using data binding, to fix that:

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    
        val inflater = LayoutInflater.from(parent.context)
    
        // initialize the binding either use:
        binding = DataBindingUtil.inflate(inflater, R.layout.order_status_layout, parent, false) 
        // or use:
        // binding = OrderStatusLayoutBinding.inflate(inflater)
    
        return MyViewHolder(binding)
    }
    

    The second issue: you accept a View as constructor parameter in MyViewHolder while it should accept the item binding type which is OrderStatusLayoutBinding

    class MyViewHolder(view: View) : RecyclerView.ViewHolder(view)

    To fix that:

    class MyViewHolder(binding: OrderStatusLayoutBinding) : RecyclerView.ViewHolder(binding.root)
    

    UPDATE

    I got the error Unresolved reference: DataBindingUtil alsoDataBindingUtil and inflater in binding = DataBindingUtil.inflate(inflater, R.layout.order_status_layout, parent, false) are red.

    You need to enable dataBinding in build.gradle (Module level) and this requires to have kotlin-kapt plugin:

    plugins {
        …
        id 'kotlin-kapt'
    }
    android {
        …
        buildFeatures {
            dataBinding true
        }
    }