NullPointerException when trying to access views in a Kotlin fragment
Asked Answered
P

9

269

How to use Kotlin Android Extensions with Fragments? If I use them inside onCreateView(), I get this NullPointerException exception:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference

Here is the fragment code:

package com.obaied.testrun.Fragment

import android.os.Bundle
import android.support.v4.app.Fragment
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.obaied.acaan.R
import kotlinx.android.synthetic.main.fragment_card_selector.*

public class CardSelectorFragment : Fragment() {
    val TAG = javaClass.canonicalName

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

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        var rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)
        btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }

        return rootView
    }
}
`
Pharyngo answered 31/12, 2015 at 5:41 Comment(4)
If you want to do it in onCreateView, btn_K will be a property on rootView as well. You could do rootView.btn_K.setOnClickListenerChthonian
Clean, rebuild and restart Android studio worked for meKerb
@Kerb This thread was first written in 2015. The first answer has 259 votes and was accepted. I don't think its necessary to add more answers.Pharyngo
@Solidak I had this problem recently, tried all the answers and the only thing that made it work was what I now commented. I had an answer on this thread, but it just got downvoted, so I changed it to a comment. Seems like people are still having this issue, and no one mentioned to clean and restart.Kerb
O
490

Kotlin synthetic properties are not magic and work in a very simple way. When you access btn_K, it calls for getView().findViewById(R.id.btn_K).

The problem is that you are accessing it too soon. getView() returns null in onCreateView. Try doing it in the onViewCreated method:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
}
Orrery answered 31/12, 2015 at 8:1 Comment(9)
It worked!! Thanks. Just a quick heads-up for future reference. I had another exception, and I dug a little deeper and it turns out that the Null Reference Exception was coming from an async callback to the UI thread where it would try to access the synthetic property, but it was already null at the time. Make sure to use the Safe Call operator (?.) or some other null safety operator. It would also help to keep a class reference of the view and not rely on synthetic properties outside of onViewCreated()Pharyngo
One question though - it generates different code for Activity and Fragment? If we use another structure that doesn't contain getView() or it cannot invoke findViewById(), is there a way to work around it? For example, teach it which function will return my layout?Polity
You can also access it like rootView.btn_K if you have a view (and not just in fragments , this can be done everywhere)Trotta
It works ! However It should be more underlined from Kotlin documentation. I didn't notice this method until this post.. Thanks anyway !Readjust
it works. also kotterknife works this way not in Java where you can access it @ onCreateView of fragment. Good find though. ThanksCrabby
You're my superman! I'm was just trying to build a simple app with some trial and error and missed this simple answer while I was banging my head trying to understand why all elements were nullNativity
I always used it in onViewCreated, but still on some device (I got the report from Crashlytics) it got "must not be null" exception. The view is there. I inflate correct layout, it works on my device. It just strange not working on random device.Codycoe
Thanks a lot for the precise and informative answer!Gothic
@ArieAgung i have same problems that got worse lately. How to fix this case?Branchiopod
T
11

You are calling this btn_K too soon as at that time it returns a null and is giving you Null Pointer Exception.

You can use these views by this synthetic plugin in onActivityCreated() method which is called just after onCreateView() of Fragment lifecycle.

onActivityCreated()
{
        super.onActivityCreated(savedInstanceState)
        btn_K.setOnClickListener{}
}
Tiffaneytiffani answered 20/11, 2017 at 11:57 Comment(1)
I just want to point out that for some reasons, this answer worked for me while the accepted answer did not. My views are null in onViewCreated but then defined in onActivityCreated. Don't know why though.Circulate
C
8

Synthetic properties generated by Kotlin Android Extensions plugin needs a view for Fragment/Activity to be set before hand.

In your case, for Fragment, you need to use view.btn_K in onViewCreated

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    super.onCreateView(inflater, container, savedInstanceState)
    val view = inflater.inflate(R.layout.fragment_card_selector, container, false)
    view.btn_K.setOnClickListener{} // access with `view`
    return view
}

Or better, you should only access synthetic properties in onViewCreated

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

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    btn_K.setOnClickListener{} // access without `view`
}

Please notice that savedInstanceState parameter should be nullable Bundle?, and also check Importing synthetic properties

It is convenient to import all widget properties for a specific layout in one go:

import kotlinx.android.synthetic.main.<layout>.*

Thus if the layout filename is activity_main.xml, we'd import kotlinx.android.synthetic.main.activity_main.*.

If we want to call the synthetic properties on View, we should also import kotlinx.android.synthetic.main.activity_main.view.*.

Congratulate answered 3/8, 2018 at 14:3 Comment(0)
C
5

the only thing you need to do is:

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    var rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)
    rootView.btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }

    return rootView
}
Cumbersome answered 16/7, 2018 at 5:19 Comment(3)
Yes, just use val view = inflater.inflate() view.button.text = "caption".Egwin
This is the best answer - neatest solution for sure!Distortion
@Distortion no, because it's still boilerplate code rootView.subView.doSomething. It's better to use views starting from onViewCreatedCq
P
1

In Fragments please write your code in onActivityCreated:-

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

    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        callbackManager = CallbackManager.Factory.create()
        initialization()
        onClickLogin()
        onClickForgot()
        onClickSocailLogIn()

  }
Panacea answered 23/5, 2018 at 7:17 Comment(1)
Why? Where do have this knowledge? Why not onViewCreated instead?Fennelly
C
0

In my case nothing worked until I followed the advice from Otziii in the comments. Clean, rebuild (no restart needed), re-run the app. I also didn't need to go with onActivityCreated and just onCreateView did the trick.

One time I also made the error of inflating wrong layout, thus not getting the expected controls obviously.

Crinkly answered 16/11, 2018 at 7:7 Comment(1)
i have seen it happening in onActivityCreated tooWrongheaded
D
0

no need to define companion object just call every id by a view like

 lateinit var mView: View
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    mView=inflater.inflate(R.layout.product_list,container,false)

    mView.addProduct.setOnClickListener {

        val intent=Intent(activity,ProductAddActivity::class.java)
        startActivity(intent)
    }     return mView
}
Delftware answered 26/6, 2019 at 8:15 Comment(0)
U
0

Adding it to @Egor Neliuba's answer, Yes whenever you call a view without reference, kotlinex looks for a rootView, and since you are inside a fragment and fragment doesn't have getView() method. Therefore it might throw NullPointerException

There are two ways to overcome this,

  • Either you override onViewCreated() as mentioned
  • Or If you want to bind views in some other class(say anonymous), you can simply create an extension function like this,

    fun View.bindViews(){...}

The second approach is helpful, when you have a single fragment with multiple behaviour.

Undertrump answered 16/8, 2019 at 7:24 Comment(0)
T
-3
class CardSelectorFragment : Fragment() {


val TAG = javaClass.canonicalName

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

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    var rootView = inflater?.inflate(R.layout.fragment_card_selector, container, false)

    rootView?.findViewById<TextView>(R.id.mTextView)?.setOnClickListener{
        Log.d(TAG, "onViewCreated(): hello world");
    }
    //btn_K.setOnClickListener { Log.d(TAG, "onViewCreated(): hello world"); }
    return rootView
}

}

**Here you are using btn_K.setOnClickListener before finding -You have to find the element form xml to your java/kotlin code by using findViewById then and then only you can perform operation on that view or element.

-So that's why null pointer execption you got

**

Termitarium answered 10/4, 2018 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.