Triggering an Interface in Kotlin for Android

Viewed 5322

There is a specific implementation of interface when it comes to using Kotlin for Android: triggering an interface from a fragment. Consider the common scenario where a Fragment must communicate a UI action to the parent Activity. In Java, we would define the interface, create a "listener" instance of it, and implement/override actions in interface parents. It is the creating a listener instance that is not so straightforward to me. After some googling, I found an implementation example but I do not understand why it works. Is there a better way to do this? Why must it be implemented the way it is below?

    class ImplementingFragment : Fragment(){

  private lateinit var listener: FragmentEvent
  private lateinit var vFab: FloatingActionButton

  //Here is the key piece of code which triggers the interface which is befuddling to me
  override fun onAttach(context: Context?) {
          super.onAttach(context)
          if(context is FragmentEvent) {
              listener = context
          }
      }

    //Notice the onClickListener using our interface listener member variable
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val v: View? = inflater.inflate(R.layout.fragment_layout, container, false)
            vFab = v?.findViewById(R.id.fh_fab)
            vFab.setOnClickListener{listener.SomethingHappened()}
        return v
    }  


}



interface FragmentEvent{
    fun SomethingHappened()
}
2 Answers

In this code lateinit var listener: FragmentEvent should be initialized and can't be null. So onAttach should looks like

override fun onAttach(context: Context?) {
    super.onAttach(context)
    if (context is FragmentEvent) {
        listener = context
    } else {
        throw RuntimeException(context!!.toString() + " must implement FragmentEvent")
    }
}

In this case if you forget to implement FragmentEvent in Activity you'll got an exception, otherwise callback is ready to use.

you could also use a try catch block and get the classic Java pattern

override fun onAttach(context: Context) {
    super.onAttach(context)
    try {
        listener = context as YourInterface
    } catch (e: IllegalStateException) {
        Log.d("TAG", "MainActivity must implement YourInterface")
    }
}

the 'as' keyword can be used in to replicate java explicit typecasting in kotlin

val customItem = arguments?.getSerializable("KEY") as ArrayList<Custom>
Related