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
    }
}
`
4
  • 5
    If you want to do it in onCreateView, btn_K will be a property on rootView as well. You could do rootView.btn_K.setOnClickListener Commented Oct 21, 2016 at 21:33
  • Clean, rebuild and restart Android studio worked for me Commented Oct 26, 2018 at 6:40
  • @Otziii 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. Commented Oct 26, 2018 at 7:00
  • 2
    @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. Commented Oct 26, 2018 at 7:13

9 Answers 9

488

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"); }
}
Sign up to request clarification or add additional context in comments.

9 Comments

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()
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?
You can also access it like rootView.btn_K if you have a view (and not just in fragments , this can be done everywhere)
It works ! However It should be more underlined from Kotlin documentation. I didn't notice this method until this post.. Thanks anyway !
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.
|
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{}
}

1 Comment

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.
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.*.

Comments

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
}

3 Comments

Yes, just use val view = inflater.inflate() view.button.text = "caption".
This is the best answer - neatest solution for sure!
@CacheMeOutside no, because it's still boilerplate code rootView.subView.doSomething. It's better to use views starting from onViewCreated
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()

  }

1 Comment

Why? Where do have this knowledge? Why not onViewCreated instead?
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.

1 Comment

i have seen it happening in onActivityCreated too
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
}

Comments

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.

Comments

-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

**

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.