Ian
Ian

Reputation: 334

Android Kotlin: Interface override function not being called

I have FragmentA and FragmentB. I am trying to update the values of FragmentA from FragmentB

I have an interface:

interface FragmentCallback {
    fun onDataSent(sendUpdatedData: String, position: Int?)
}

In FragmentA, I override the interface function. I also instantiate FragmentB and call setFragmentCallback function

class FragmentA: Fragment(), FragmentCallback {

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        FragmentB().setFragmentCallback(this)
    }

    //This function is not being called from FragmentB...
    override fun onDataSent(sendUpdatedData: String, position: Int?) {
            updateRecyclerView(sendUpdatedData, position!!)
    }

In FragmentB (which is on top of FragmentA) I instantiate the FragmentCallback interface. When I am done editing and pop the fragment(backstack), I call the overridden function from FragmentA(onDataSent).

class FragmentB: Fragment(){
    private var fragmentCallback: FragmentCallback? = null

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    
        btn_save.setOnClickListener{
            fragmentCallback?.onDataSent(et_new_text.text.toString(), position )
            fragmentManager?.popBackStack()
        }
    }

    fun setFragmentCallback(callback: FragmentCallback?) {
        fragmentCallback = callback
    }
}

For some reason, when I pop FragmentB and call onDataSent, through the line:

fragmentCallback?.onDataSent(et_new_text.text.toString(), position)

onDataSent will actually NOT be called.

Upvotes: 0

Views: 1208

Answers (2)

Bram Stoker
Bram Stoker

Reputation: 1242

Use a FragmentResultListener in FragmentA:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setFragmentResultListener("requestKey") { key, bundle ->
        val sendUpdatedData = bundle.getString("sendUpdatedData")
        // Do something with the result...
    }
}

Then in FragmentB set the result:

btn_save.setOnClickListener{
    val result = et_new_text.text.toString()
    setFragmentResult("requestKey", bundleOf("sendUpdatedData" to result))
    ...
}

Have a look at the official documentation.

The old-fashioned way uses the Activity to communicate between two fragments. You could also use a shared ViewModel to share data.

Upvotes: 2

Parth
Parth

Reputation: 791

You can try this too.

Let your activity implements your interface.

class YourActivity : FragmentCallback{
    override fun onDataSent(sendUpdatedData: String, position: Int?) {
       // Get Fragment A
       val fraga: FragmentA? = 
              supportFragmentManager.findFragmentById(R.id.fragment_a) as FragmentA?
            fraga.updateRecyclerView(sendUpdatedData,position)
          }
}

In your fragmentB, send the data like this.

class FragmentB : Fragment() {
    var mCallback: FragmentCallback? = null

    override fun onAttach(activity: Activity) {
        super.onAttach(activity)

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        mCallback = try {
            activity as FragmentCallback
        } catch (e: ClassCastException) {
            throw ClassCastException(
                activity.toString()
                        + " must implement TextClicked"
            )
        }
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        mCallback.onDataSent(et_new_text.text.toString(), position)
        fragmentManager?.popBackStack()
    }
    
    override fun onDetach() {
        mCallback = null // => avoid leaking, thanks @Deepscorn
        super.onDetach()
    }
}

In your FragmentA.

class FragmentA : Fragment() {
    fun updateRecyclerView(sendUpdatedData: String, position: Int?) {
        // Here you'll have it
    }
}

Upvotes: 0

Related Questions