18

I'm trying to build android application using Kotlin for the first time.

I want to declare on some buttons outside the OnCreate method and i can initialize them only Inside this function with findViewById.

Can i declare in simple and clean code like in java?

private Button btnProceed;

Because when converting it to Kotlin it look like:

private var btnProceed: Button? = null

And then when initialize OnClick function need to add ! sign:

btnProceed!!.setOnClickListener

What is the right and cleanest way?

Udi
  • 598
  • 8
  • 19
  • Possible duplicate of ["lateinit" or "by lazy" when defining global android.widget var/val](https://stackoverflow.com/questions/44056988/lateinit-or-by-lazy-when-defining-global-android-widget-var-val) – Daniel Storm May 31 '17 at 14:00
  • Hi Daniel, My question is more basic. You are right that the answers in your reference answering my issue, but if someone not familiar with lateinit or lazy (like i do) will not find this question like i didn't. – Udi Jun 01 '17 at 06:30

3 Answers3

23

This is a good use case for lateinit. Marking a property lateinit allows you to make it non nullable, but not assign it a value at the time that your Activity's constructor is called. It's there precisely for classes like Activities, when initialization happens in a separate initializer method, later than the constructor being run (in this case, onCreate).

private lateinit var btnProceed: Button

If the property is read before a real value is assigned to it, it will throw an exception at runtime - by using lateinit, you're taking the responsibility for initializing it before you access it for the first time.


Otherwise, if you want the compiler to guarantee safe access for you, you can make the Button nullable as the converter does by default. Instead of the unsafe !! operator though, which the converter often uses, you should use the safe call operator where you access the property:

btnProceed?.setOnClickListener { ... }

This will make a regular call if btnProceed is a non-null value, and do nothing otherwise.


On a final note, you can check out Kotlin Android Extensions, which eliminates the need to create properties for your Views altogether, if it works for your project.


Last edit (for now): you should also look at using lazy as described in the other answers. Being lazy is cool.

zsmb13
  • 85,752
  • 11
  • 221
  • 226
12

Instead of using lateinit, you can also do lazy initialization:

private val button by lazy {
    findViewById(R.id.button) as Button
}

The first time you access the button property, it will execute the block once and use the result for future calls. In onCreate for example, you can now directly access it:

fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(bundle)
  setContentView(R.layout.my_view)

  button.setOnClickListener { ... }
}
nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • 2
    This can cause a memory leak in Fragments. https://www.bignerdranch.com/blog/kotlin-when-to-use-lazy-or-lateinit/ – Bryan Bryce Dec 19 '18 at 22:58
  • I try to use `apply` to setOnClickListener inside the lazy block `findViewById(R.id.button).apply{this.setOnClickListener{...}}` . But it does not work...But works inside onCreate. Do you know why? – Yeung Sep 10 '19 at 06:04
1

You can do it with lateinit as @zsmb13 suggest BUT this has the disadvantage that your views will be variable instead of final. If you want them to be final you can use the lazy property delegation

By using lazy you can declare how the value will be initialized when you first try to access it so by declaring

private val btnProceed: Button by lazy {
    findViewById(R.id.yourID)
}

Whenever you access your btnProceed you will have your activity (this example assume you're using an activity) loaded so you can use that method

Alberto S.
  • 7,409
  • 6
  • 27
  • 46