androidkotlinkotlin-lateinit

lateinit var result always uninitialized in Kotlin


I have to use selectAllCheckBox in my override fun notifyAllIsChecked, but the variable selectAllCheckBox appears uninitialized, even though it has already been initialized in the onCreate method.

The problems is in onCreate, in the lateinit declaration and in the notifyAllIsChecked function.

package com.example.lslmbeta

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.lslmbeta.databinding.ActivityMainBinding
import com.example.lslmbeta.datamanagement.dataReader
import com.example.lslmbeta.recyclerview.ItemAdapter
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.checkbox.MaterialCheckBox
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat


class MainActivity : AppCompatActivity(), OnAllIsCheckedListener {
    companion object {
        var isInSelectionMode: Boolean? = null
    }

    private val MAIN_ACTIVITY_TAG = "Main Activity"

    private lateinit var recyclerView: RecyclerView

    private lateinit var closeView: MenuItem
    private lateinit var selectView: MenuItem
    private lateinit var moreView: MenuItem

    private lateinit var itemAdapter: ItemAdapter
    private lateinit var selectAllCheckBox: MaterialCheckBox

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.top_app_bar, menu)

        // Menu Group Selection
        closeView = menu.findItem(R.id.menu_item_close)

        // Menu Group Non Selection
        selectView = menu.findItem(R.id.menu_item_select)
        moreView = menu.findItem(R.id.menu_item_more)

        enableOrDisableSelection()

        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item) {
            closeView -> {
                enableOrDisableSelection()
                true
            }

            selectView -> {
                if (::itemAdapter.isInitialized) enableOrDisableSelection()
                true
            }

            moreView -> {
                // Gestisci il clic sull'elemento "More" (menu a scomparsa)
                true
            }

            else -> super.onOptionsItemSelected(item)
        }
    }

    //TODO togliere il suppress
    @SuppressLint("SimpleDateFormat")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        val topToolBar = binding.topToolBar
        setSupportActionBar(topToolBar)

        WindowCompat.setDecorFitsSystemWindows(window, false)

        val dateString = "19/10/2023"
        val dateFormat = SimpleDateFormat("dd/MM/yyyy")
        val date = dateFormat.parse(dateString)

        recyclerView = binding.homeRecycleView
        selectAllCheckBox = binding.selectAllCheckbox

        Log.d(MAIN_ACTIVITY_TAG, "Select All Checkbox: ${::selectAllCheckBox.isInitialized}")

        selectAllCheckBox.setOnClickListener {
            if (selectAllCheckBox.isChecked) {
                itemAdapter.checkAll()
            } else {
                itemAdapter.clearCheckedItems()
            }
        }

        lifecycleScope.launch {
            val myDataset = dataReader(this@MainActivity, date)
            itemAdapter = ItemAdapter(myDataset)
            recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
            recyclerView.adapter = itemAdapter
            recyclerView.setHasFixedSize(true)

            itemAdapter.setCustomListener(MainActivity())
        }
    }

    override fun notifyAllIsChecked(isAllChecked: Boolean) {
        Log.d(MAIN_ACTIVITY_TAG, "Is Ready: ${::selectAllCheckBox.isInitialized}")

        if (::selectAllCheckBox.isInitialized) selectAllCheckBox.isChecked = isAllChecked
    }

    @SuppressLint("NotifyDataSetChanged")
    private fun enableOrDisableSelection() {
        val topToolBar = findViewById<MaterialToolbar>(R.id.topToolBar)
        val checkBox = findViewById<MaterialCheckBox>(R.id.select_all_checkbox)

        if (isInSelectionMode == null) {
            isInSelectionMode = false
            Log.w("Enable Or Disable Selection", "Is NULL")
        } else {
            isInSelectionMode = !isInSelectionMode!!
        }

        Log.d("Enable Or Disable Selection", isInSelectionMode.toString())

        // Group Selection
        closeView.isVisible = isInSelectionMode!!

        checkBox.isVisible = isInSelectionMode!!

        // Group Non Selection
        selectView.isVisible = !isInSelectionMode!!
        moreView.isVisible = !isInSelectionMode!!

        topToolBar.title = if (isInSelectionMode!!) "" else getString(R.string.app_name)

        if (isInSelectionMode!!) {
            itemAdapter.clearCheckedItems()
        } else {
            recyclerView.adapter?.notifyDataSetChanged()
        }
    }
}

I used logs and noticed that it gets initialized before the call in the notifyAllIsChecked method.

2023-10-29 11:56:16.938 24963-24963 Main Activity           com.example.lslmbeta                 D  Select All Checkbox: true
2023-10-29 12:09:12.864 24963-24963 Main Activity           com.example.lslmbeta                 D  Is Ready: false

Solution

  • I believe the problem is here:

    itemAdapter.setCustomListener(MainActivity())
    

    We should never instantiate activities manually. The activity you passed as a listener is uninitialized and it can't be used for pretty much anything.

    Instead, you should probably pass the current activity: this@MainActivity.