267

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
    }
}
`
Otziii
  • 2,304
  • 1
  • 25
  • 34
solidak
  • 5,033
  • 3
  • 31
  • 33
  • 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` – Makotosan Oct 21 '16 at 21:33
  • Thanks @Makotosan your answer worked for me. – Mahdi Bagjani Jul 18 '18 at 12:19
  • Clean, rebuild and restart Android studio worked for me – Otziii Oct 26 '18 at 06: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. – solidak Oct 26 '18 at 07: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. – Otziii Oct 26 '18 at 07:13

9 Answers9

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"); }
}
Egor Neliuba
  • 14,784
  • 7
  • 59
  • 77
  • 3
    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()` – solidak Jan 01 '16 at 00:28
  • 2
    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? – milosmns Oct 30 '16 at 17:26
  • 7
    You can also access it like `rootView.btn_K` if you have a view (and not just in fragments , this can be done everywhere) – Adib Faramarzi Feb 18 '17 at 14:42
  • It works ! However It should be more underlined from Kotlin documentation. I didn't notice this method until this post.. Thanks anyway ! – sokarcreative Nov 11 '17 at 16:12
  • it works. also kotterknife works this way not in Java where you can access it @ onCreateView of fragment. Good find though. Thanks – ralphgabb Apr 13 '18 at 06:00
  • 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 null – Loves2Develop Mar 28 '19 at 21:46
  • 10
    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. – Arie Agung Apr 24 '19 at 12:15
  • Thanks a lot for the precise and informative answer! – Langkiller May 09 '19 at 20:34
  • @ArieAgung i have same problems that got worse lately. How to fix this case? – DixieFlatline Jul 27 '20 at 08:28
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{}
}
Thomas Rollet
  • 1,573
  • 4
  • 19
  • 33
Kashish luthra
  • 121
  • 1
  • 5
  • 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. – NathanL Mar 23 '18 at 10:43
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.*.

onmyway133
  • 45,645
  • 31
  • 257
  • 263
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
}
Rhusfer
  • 571
  • 1
  • 9
  • 14
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()

  }
abhilasha Yadav
  • 217
  • 1
  • 9
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.

ZzZombo
  • 1,082
  • 1
  • 11
  • 26
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
}
Rahul ShaRma
  • 99
  • 4
  • 8
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.

Aziz
  • 1,976
  • 20
  • 23
-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

**

Amit Walke
  • 293
  • 3
  • 6