ybybyb
ybybyb

Reputation: 1739

Why isn't by lazy applied in fragments?

I know there is a lateinit or lazy keyword in Kotlin to prevent indiscriminate initialization and thus minimize wasted resources.

I wanted to use the lazy keyword to use findViewById when necessary events occur.

However, if I use the lazy keyword, nothing happens. It doesn't even cause an error.

Conversely, when findViewId is normally used in onCreateView, click event occurs normally.

Why doesn't lazy work?

class BodyPartDialogFragment : DialogFragment(), View.OnClickListener{
    private val ll: LinearLayout? by lazy { view?.findViewById(R.id.ll_body_part) }
//    private lateinit var button: Button
    private val button: Button? by lazy { view?.findViewById(R.id.start) }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view: View = inflater.inflate(R.layout.fragment_body_part_dialog, container, false)
//        ll = view.findViewById(R.id.ll_body_part)
//        button = view.findViewById(R.id.start)
        ll?.apply { clipToOutline = true }

        button?.setOnClickListener { // Nothing Happened
            Toast.makeText(context, "Noting Selected", Toast.LENGTH_SHORT).show()
        }

        return view
    }

Upvotes: 0

Views: 1558

Answers (4)

Nk P
Nk P

Reputation: 41

You can do this to get access to View in the future using 'by lazy'

private val previewImage by lazy { requireActivity().findViewById<ImageView>(R.id.ivImage) }

Then you can use it like

previewImage.setImageURI(imageUri)

Upvotes: 0

laalto
laalto

Reputation: 152847

getView() that is behind the view property returns whatever you returned from onCreateView(). When you access view inside onCreateView(), it hasn't yet returned anything and hence a null is returned, and your ?. safecall becomes a no-op.

You can use a lazy approach like this after onCreateView(), such as in onViewCreated().

Upvotes: 1

H&#229;kon Schia
H&#229;kon Schia

Reputation: 1001

view (or really getView()) is the view that is returned from onCreateView(). You're trying to access that before you have returned from onCreateView() so it returns null, and your lazy value is then also null. You can make this work by accessing it later, ie. in onViewCreated()

class BodyPartDialogFragment : DialogFragment(), View.OnClickListener{
    private val ll: LinearLayout? by lazy { view?.findViewById(R.id.ll_body_part) }
//    private lateinit var button: Button
    private val button: Button? by lazy { view?.findViewById(R.id.start) }

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

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ll?.apply { clipToOutline = true }
        button?.setOnClickListener { // Nothing Happened
            Toast.makeText(context, "Noting Selected", Toast.LENGTH_SHORT).show()
        }
    }
}

This is more clear if you use requireView() since it returns a non-null View and rather throws an exception, so your app would have crashed with the error message did not return a View from onCreateView() or this was called before onCreateView().

Upvotes: 0

Louis Wasserman
Louis Wasserman

Reputation: 198143

It looks like you may be initializing things in the wrong order.

Consider that renaming a local variable always preserves semantics, so let's modify your code a little:

private val ll: LinearLayout? by lazy { view?.findViewById(R.id.ll_body_part) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    val someRandomView: View = inflater.inflate(R.layout.fragment_body_part_dialog, container, false)
    ll?.apply { clipToOutline = true }

    button?.setOnClickListener { // Nothing Happened
        Toast.makeText(context, "Noting Selected", Toast.LENGTH_SHORT).show()
    }

    return someRandomView
}

Do you see the issue? ll is being initialized with a view that hasn't been assigned yet in onCreateView.

Upvotes: 0

Related Questions