GenericDialog did not return a View from onCreateView() or this was called before onCreateView()

Viewed 33

I was developing an app in kotlin, and I try to make a custom dialog, which extends of my parent class BaseDialogFragment, and when I try to instace my GenericDialog I get the following error:

 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: es.renaultbank.andr, PID: 21498
    java.lang.IllegalStateException: Fragment GenericDialog{a0c2f1e} (ffc56cd1-3856-466f-8705-c66209c24963 tag=sucess_otp_dialog) did not return a View from onCreateView() or this was called before onCreateView().
        at androidx.fragment.app.Fragment.requireView(Fragment.java:1964)
        at es.renaultbank.andr.utils.GenericDialog.configureEvents(GenericDialog.kt:39)
        at es.renaultbank.andr.ui.customviews.dialog.BaseDialogFragment.onCreateView(BaseDialogFragment.kt:17)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
        at androidx.fragment.app.DialogFragment.performCreateView(DialogFragment.java:489)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)

My GenericDialog class is the following:

package es.renaultbank.andr.utils

import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import es.renaultbank.andr.R
import es.renaultbank.andr.ui.customviews.dialog.BaseDialogFragment
import es.renaultbank.andr.ui.extensions.find

class GenericDialog(
    context: Context,
    private val icon: Int? = null,
    private val title: String? = null,
    private val subtitle: String? = null,
    private val buttonText: String? = null,
    private var clickListener: View.OnClickListener? = null,
    private val cancelable: Boolean = true

) : BaseDialogFragment() {

    private var iv_icon: ImageView = ImageView(context)
    private var tv_title: TextView = TextView(context)
    private var tv_subtitle: TextView = TextView(context)
    private var btn_action: Button = Button(context)
    private var iv_close: ImageView = ImageView(context)

    override fun configureView(view: View?) {
        dialog?.setCancelable(true)
        dialog?.setCanceledOnTouchOutside(true)
    }

    override fun configureEvents(view: View?) {
        iv_icon = requireView().findViewById(R.id.iv_icon)
        tv_title = requireView().findViewById(R.id.tv_title)
        tv_subtitle = requireView().findViewById(R.id.tv_subtitle)
        btn_action = requireView().findViewById(R.id.btn_action)
        iv_close = requireView().findViewById(R.id.iv_close)
    }

    override fun getLayout(): Int {
        return R.layout.dialog_generic
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setView()
        setListeners()
    }

    fun setIcon(icon: Int) {
        iv_icon.setImageDrawable(requireContext().getDrawable(icon))
        iv_icon.visibility = View.VISIBLE
        iv_icon.setColorFilter(R.color.black)
    }

    fun setTitle(title: String) {
        tv_title.text = title
        tv_title.visibility = View.VISIBLE
    }

    fun setSubtitle(subtitle: String) {
        tv_subtitle.text = subtitle
        tv_subtitle.visibility = View.VISIBLE
    }

    fun setButtonText(text: String) {
        btn_action.text = text
        btn_action.visibility = View.VISIBLE
    }

    fun setOnClickListener() {
        btn_action.setOnClickListener { clickListener }
    }

    override fun setCancelable(flag: Boolean) {
        super.setCancelable(flag)
        if (flag) {
            iv_close.visibility = View.VISIBLE
        } else {
            iv_close.visibility = View.GONE
        }
    }

    private fun setView() {

        if (icon != null) {
            setIcon(icon)
        } else {
            iv_icon.visibility = View.GONE
        }
        if (title != null) {
            setTitle(title)
        } else {
            tv_title.visibility = View.GONE
        }
        if (subtitle != null) {
            setSubtitle(subtitle)
        } else {
            tv_subtitle.visibility = View.GONE
        }
        if (buttonText != null) {
            setButtonText(buttonText)
        } else {
            btn_action.visibility = View.GONE
        }
        setCancelable(cancelable)
    }

    fun dismissDialog(){
        this.dismiss()
    }

    private fun setListeners() {
        iv_close.setOnClickListener {
            dismiss()
        }
        if (clickListener != null) {
            btn_action.setOnClickListener(clickListener)
        } else {
            btn_action.setOnClickListener {
                dismiss()
            }
        }
    }
}

and their parent class is this:

BaseDialogFragment.kt

package es.renaultbank.andr.ui.customviews.dialog

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.fragment.app.DialogFragment

abstract class BaseDialogFragment : DialogFragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val rootView: View = inflater.inflate(getLayout(), container, false)

        configureView(rootView)
        configureEvents(rootView)

        return rootView
    }

    fun hideKeyboard(view: View?) {
        view?.let {
            val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
            imm.hideSoftInputFromWindow(view.windowToken, 0)
        }
    }


    /**
     * Configuracion de los findViewById  la vista, colores, animaciones...
     */
    protected abstract fun configureView(view: View?)

    /**
     * Configuracion de listeners
     */
    protected abstract fun configureEvents(view: View?)

    /**
     * Setea el layout del dialog
     */
    protected abstract fun getLayout(): Int

}

Finally the used of the GenericDialog is a simple network call, and if sucess, show the dialog

 if(it.errorBase.getErrorType() == ErrorBase.ErrorType.NON_ERROR) {
                        (it.responseBase).let {resp ->
                            if ((resp as ValidatePassOTPResponse).response == "200 OK") {
                                val sucessDialog = GenericDialog(
                                    context = requireContext(),
                                    icon = R.drawable.ic_tick_green,
                                    title = getString(R.string.change_password_title),
                                    subtitle = getString(R.string.password_change_sucess),
                                    buttonText = getString(R.string.understand),
                                    cancelable = true,
                                    clickListener = { (activity as DashboarActivity).redirectToLogin() }
                                )
                                sucessDialog.show(requireFragmentManager(), "sucess_otp_dialog")
                            } else {
                                showOtpError().also {
                                    (activity as DashboarActivity).redirectToLogin()
                                }
                            }
                        }
                    }else{
                        (activity as BaseActivity<*>).showError(it.errorBase, null)
                        dialog?.dismiss().also {
                            (activity as DashboarActivity).redirectToLogin()
                        }
                    }
                })

I hope you can help, in case you've faced some similar problem.

Take thanks in advance !

[EDIT]

I've taken to show the dialog replacing requireView to view, in the configureEvents method. but althought is shown and debugging I see that the set method in the dialog are invoked, the dialog show like that:

enter image description here

As you can check is empty although the params which I'm passing are not empty.

1 Answers

Call setView() and setListener() method inside configureEvents() method after initializing the views.

Refer the following code

class GenericDialog : BaseDialogFragment() {

    private lateinit var iv_icon: ImageView
    private lateinit var tv_title: TextView
    private lateinit var tv_subtitle: TextView
    private lateinit var btn_action: Button
    private lateinit var iv_close: ImageView

    override fun configureView(view: View?) {
        dialog?.setCancelable(true)
        dialog?.setCanceledOnTouchOutside(true)
    }

    override fun configureEvents(view: View?) {
        iv_icon = requireView().findViewById(R.id.iv_icon)
        tv_title = requireView().findViewById(R.id.tv_title)
        tv_subtitle = requireView().findViewById(R.id.tv_subtitle)
        btn_action = requireView().findViewById(R.id.btn_action)
        iv_close = requireView().findViewById(R.id.iv_close)
        setView()
        setListeners()
    }

    override fun getLayout(): Int {
        return R.layout.dialog_generic
    }

    private fun setIcon(icon: Int) {
        iv_icon.setImageDrawable(requireContext().getDrawable(icon))
        iv_icon.visibility = View.VISIBLE
        iv_icon.setColorFilter(R.color.black)
    }

    private fun setTitle(title: String) {
        tv_title.text = title
        tv_title.visibility = View.VISIBLE
    }

    private fun setSubtitle(subtitle: String) {
        tv_subtitle.text = subtitle
        tv_subtitle.visibility = View.VISIBLE
    }

    private fun setButtonText(text: String) {
        btn_action.text = text
        btn_action.visibility = View.VISIBLE
    }

    private fun setView() {
        val icon = requireArguments().getInt(ICON, -1)
        val title = requireArguments().getString(TITLE)
        val subtitle = requireArguments().getString(SUB_TITLE)
        val buttonText = requireArguments().getString(BUTTON_TEXT)
        val cancelable = requireArguments().getBoolean(CANCELABLE)

        if (icon != -1) {
            setIcon(icon)
        } else {
            iv_icon.visibility = View.GONE
        }
        if (title != null) {
            setTitle(title)
        } else {
            tv_title.visibility = View.GONE
        }
        if (subtitle != null) {
            setSubtitle(subtitle)
        } else {
            tv_subtitle.visibility = View.GONE
        }
        if (buttonText != null) {
            setButtonText(buttonText)
        } else {
            btn_action.visibility = View.GONE
        }
        setCancelable(cancelable)
    }

    fun dismissDialog() {
        this.dismiss()
    }

    private fun setListeners() {
        iv_close.setOnClickListener {
            dismiss()
        }
        
        btn_action.setOnClickListener {
            val clickListener = getActionListener()
            if (clickListener != null) {
                clickListener.onAction()
            } else {
                dismiss()
            }
        }
    }

    private fun getActionListener(): ActionListener? {
        val activity = this.activity
        return if (activity is ActionListener) activity
        else null
    }

    companion object {
        private const val ICON = "ICON"
        private const val TITLE = "TITLE"
        private const val SUB_TITLE = "SUB_TITLE"
        private const val BUTTON_TEXT = "BUTTON_TEXT"
        private const val CANCELABLE = "cancelable"
        fun newInstance(
            icon: Int? = null,
            title: String? = null,
            subtitle: String? = null,
            buttonText: String? = null,
            cancelable: Boolean = true) = GenericDialog().apply {
            arguments = Bundle().apply {
                putString(TITLE, title)
                putString(SUB_TITLE, subtitle)
                putString(BUTTON_TEXT, buttonText)
                if (icon != null) {
                    putInt(ICON, icon)
                }
                putBoolean(CANCELABLE, cancelable)
            }
        }
    }

    interface ActionListener {
        fun onAction()
    }
}
Related