-1

I'm trying to inflate a custom dialog in my "CreateShoppingListMenuFragment" I've followed android's documentation but seem to be having a problem with the Listener, I know FragmentManager() is deprecated and used both parentFragmentManager & child FragmentManager to no success, maybe it's related?

Here is the error message:

logo1200.shoppinglist, PID: 24194
java.lang.ClassCastException: com.camilogo1200.shoppinglist.presentation.MainActivity@11852bbmust implement ShoppingListNameRequestListener
    at com.camilogo1200.shoppinglist.presentation.fragments.ShoppingListNameRequestDialog.onAttach(ShoppinListNameRequestDialog.kt:68)
    at androidx.fragment.app.Fragment.performAttach(Fragment.java:2922)
    at androidx.fragment.app.FragmentStateManager.attach(FragmentStateManager.java:464)
    at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:275)
    at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)

This is my DialogFragment:

class ShoppingListNameRequestDialog : DialogFragment() {

private lateinit var listener: ShoppingListNameRequestListener



override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val builder = AlertDialog.Builder(it)
        val inflater: LayoutInflater = requireActivity().layoutInflater
        val requestNameView = inflater.inflate(R.layout.shopping_list_name_request_dialog, null)
        val nameInput = requestNameView.findViewById<TextView>(R.id.shopping_list_dialog_input)
        var listName = ""

        builder.setView(requestNameView)
            .setPositiveButton(R.string.save_shopping_list,
                DialogInterface.OnClickListener {dialog, id ->

                    if(nameInput.text.toString() != "")
                       listName = nameInput.text.toString()
                       listener.onDialogPositiveClick(this,listName);

                })
            .setNegativeButton(R.string.cancel,
                DialogInterface.OnClickListener{dialog, id ->
                    listener.onDialogNegativeClick(this)
                })

        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}


interface ShoppingListNameRequestListener {
    fun onDialogPositiveClick(dialog: DialogFragment,listName:String)
    fun onDialogNegativeClick(dialog: DialogFragment)
}


override fun onAttach(context: Context) {
    super.onAttach(context)
   
    try {
       
        listener = context as ShoppingListNameRequestListener
    } catch (e: ClassCastException) {
       
        throw ClassCastException((context.toString() +
                "must implement ShoppingListNameRequestListener"))
    }
}

This is my "CreateShoppingListMenuFragment" (the host fragment where I'm inflating the dialog):

class CreateShoppingListMenuFragment : Fragment(),
ShoppingListNameRequestDialog.ShoppingListNameRequestListener {

private lateinit var binding: FragmentCreateShoppingListMenuBinding
private val viewModel: CreateShoppingListMenuViewModel by activityViewModels()
private val args: CreateShoppingListMenuFragmentArgs by navArgs()

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    binding = DataBindingUtil.inflate(
        layoutInflater,
        R.layout.fragment_create_shopping_list_menu,
        container,
        false
    )

    binding.lifecycleOwner = this

    viewModel.createItems()

    viewModel.viewState.observe(viewLifecycleOwner, ::handleViewState)

    val login = args.ownerName
    val listId = args.listId

    viewModel.setOwnerAndList(login, listId)

    binding.createItemButton.setOnClickListener {
        val directionToFragment =
            CreateShoppingListMenuFragmentDirections.actionCreateShoppingListMenuFragmentToCreateItemMenuFragment(
                login,
                listId
            )
        Navigation.findNavController(binding.root).navigate(directionToFragment)
    }

    binding.completeShoppingListButton.setOnClickListener {
        showNoticeDialog()
    }

    return binding.root
}

private fun showNoticeDialog() {

    val dialog = ShoppingListNameRequestDialog()
    dialog.show(parentFragmentManager, "ShoppingListNameRequestDialog")
}


override fun onDialogPositiveClick(dialog: DialogFragment,listName: String) {
    val result = viewModel.saveShoppingList(listName)
    Log.i("shoppingListResult", "$result")
    // travel to final fragment sent shoppinglist as arg
}

override fun onDialogNegativeClick(dialog: DialogFragment) {
    // User touched the dialog's negative button
}


private fun handleViewState(viewState: CreateShoppingListMenuViewState) {
    when (viewState) {
        is CreateShoppingListMenuViewState.ErrorViewState -> showError(viewState.exception as ShoppingException)
        //is RegisterViewState.SuccessViewState ->showSuccess()
        else -> showSuccess(viewState)
    }
}


private fun showSuccess(viewState: CreateShoppingListMenuViewState) {
    val receivedList = viewState as CreateShoppingListMenuViewState.SuccessViewState
    val dataList = receivedList.data

    val adapter = ShoppingListMenuAdapter(dataList, viewModel::changeItemCount)


    binding.itemListArray.adapter = adapter
}


private fun showError(exception: ShoppingException) {

    if (exception.idError as? ItemError == ItemError.NO_ITEMS_CREATED) {
        val message = getString(R.string.no_items_created_error_messages)
        Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
    }

Any help would be greatly appreciated!

1 Answers1

1

The Context in onAttach(Context context) is context Activity fragment does not have its own Context . The problem here is you are casting context to ShoppingListNameRequestListener for this to work your Activity needs to implement the listener .

To solve this problem there are several ways. if we go with your approach we can pass fragment instance as targetFragment and use it as listener inside the DialogFragment .

private fun showNoticeDialog() {
    val dialog = ShoppingListNameRequestDialog()
    dialog.setTargetFragment(this)
    dialog.show(parentFragmentManager, "ShoppingListNameRequestDialog")
}

Then inside dialog you can do something like this .

class ShoppingListNameRequestDialog:DialogFragment(){
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        targetFragment?.let {
            listener = it as ShoppingListNameRequestListener
        }
    }
}

However setTargetFragment is deprecated now. As a Alternate way you can do the same with a shared ViewModel or with the new API FragmentResultListener.

ADM
  • 20,406
  • 11
  • 52
  • 83
  • ADM Thank you for your quick answer!! I have studied FragmentResultListener and I understand it uses when sending back info. from my dialogFragment, surely a more concise and easier way than how I was doing it, however I still dont understand how I can implement it to inflate the dialog, as I understand dialog.show() is needed to do that and a context is needed, how do I go about it? – Felipe Coronado Dec 19 '21 at 14:19
  • well right now i can not provide you the code . so [here you go](https://proandroiddev.com/android-fragments-fragment-result-805a6b2522ea). i think this is the best way to do it . it pretty easy . give it a try man .. – ADM Dec 19 '21 at 14:35