简体   繁体   中英

Error: lateinit property recyclerViewAdapter has not been initialized

I'm try to initialize recyclerView and ViewModel in onCreate in fragment and get error lateinit property recyclerViewAdapter has not been initialized

class ListFragment : Fragment() {

    private val recyclerView: RecyclerView? = activity?.findViewById(R.id.recyclerView)

    private lateinit var recyclerViewAdapter: RecyclerViewAdapter

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

        initRecyclerView()
        initViewModel()
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_list, container, false)
    }

    private fun initRecyclerView() {
        recyclerView?.apply {
            layoutManager = LinearLayoutManager(context)
            val decoration = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
            addItemDecoration(decoration)
            recyclerViewAdapter = RecyclerViewAdapter()
            adapter = recyclerViewAdapter
        }
    }

    private fun initViewModel() {
        val viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]
        lifecycleScope.launchWhenCreated {
            viewModel.getListData().collectLatest {
                recyclerViewAdapter.submitData(it)
            }
        }
    }
}

Here's where you went wrong:

private val recyclerView: RecyclerView? = activity?.findViewById(R.id.recyclerView)

The above property will be null because activity is null at instantiation time of the Fragment, and you are using activity?. so you are initializing it as null .

Then in initRecyclerView() , you are using recyclerView?.apply , so nothing in the lambda will be executed because recyclerView is null. Therefore, the property recyclerViewAdapter is never set, so when it is first accessed (in initViewModel() ), it will throw UnitializedPropertAccessException and crash.

You must not initialize a Fragment's view properties at the declaration site, because a Fragment is initialized before it is has any view or is attached to any Activity. Also, the same Fragment instance might be reused but have a new view and Activity instance, so you should make sure you are re-assigning these properties every time there is a new view.

Also, don't initialize views in onCreate() , because it happens before your onCreateView() , so there will be no view to find. You should rarely ever need to use onCreate() in a Fragment. Use onViewCreated() instead.

And if the recycler view is in your Fragment layout, you should search the Fragment's view for the RecyclerView instead of searching up in the Activity. The Activity still does not contain the Fragment's views in onViewCreated() so it will fail to find the view.

Here's how to fix your code:

class ListFragment : Fragment() {

    private lateinit var recyclerView: RecyclerView

    private lateinit var recyclerViewAdapter: RecyclerViewAdapter

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onCreate(view, savedInstanceState)

        recyclerView = view.findViewById(R.id.recyclerView)

        initRecyclerView()
        initViewModel()
    }

    private fun initRecyclerView() {
        recyclerView.apply {
            layoutManager = LinearLayoutManager(context)
            val decoration = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
            addItemDecoration(decoration)
            recyclerViewAdapter = RecyclerViewAdapter()
            adapter = recyclerViewAdapter 
        }
    }

    private fun initViewModel() {
        //...
    }
}

The above technically leaks your views when the fragment is detached. You could avoid this by using custom getters so you aren't caching references to the views. Also, you can eliminate onCreateView by passing your layout ID directly to the Fragment super-constructor. So here's how I would write this class:

class ListFragment : Fragment(R.layout.fragment_list) {

    private val recyclerView: RecyclerView
        get() = requireView().findViewById(R.id.recyclerView)

    private val recyclerViewAdapter: RecyclerViewAdapter
        get() = recyclerView.adapter as RecyclerViewAdapter

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onCreate(view, savedInstanceState)

        initRecyclerView()
        initViewModel()
    }

    private fun initRecyclerView() {
        recyclerView.apply {
            layoutManager = LinearLayoutManager(context)
            val decoration = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
            addItemDecoration(decoration)
            adapter = RecyclerViewAdapter()
        }
    }

    private fun initViewModel() {
        //...
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM