-1

I'm building an Android app that has different pages that mainly have some EditText. My goal is to handle the click on the EditText and shows a DialogAlert with an EditText, then the user can put the text, click "save" and the related field in the database (I'm using Room and I've tested the queries and everything works) will be updated. Now I was able to handle the text from the DialogFragment using interface but I don't know how to say that the text retrieved is related to the EditText that I've clicked. What is the best approach to do this? Thanks in advance for your help.

Let's take this fragment as example:

class StaticInfoResumeFragment : Fragment(), EditNameDialogFragment.OnClickCallback {

private val wordViewModel: ResumeStaticInfoViewModel by viewModels {
    WordViewModelFactory((requireActivity().application as ManagementCinemaApplication).resumeStaticInfoRepo)
}

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?,
): View? {
    val root = inflater.inflate(R.layout.fragment_static_info_resume, container, false)

    wordViewModel.resumeStaticInfo.observe(viewLifecycleOwner) { words ->
        println("test words: $words")
    }

    val testView = root.findViewById<TextInputEditText>(R.id.textInputEditText800)


    testView.setOnClickListener{
        val fm: FragmentManager = childFragmentManager
        val editNameDialogFragment = EditNameDialogFragment.newInstance("Some Title")
        editNameDialogFragment.show(fm, "fragment_edit_name")
    }

    resumeStaticInfoViewModel.firstName.observe(viewLifecycleOwner, Observer {
        testView.setText(it)
    })

    return root
}

override fun onClick(test: String) {
    println("ciao test: $test")
    wordViewModel.updateFirstName(testa)
}}

Then I've the ViewModel:

class ResumeStaticInfoViewModel(private val resumeStaticInfoRepo: ResumeStaticInfoRepo): ViewModel() {

val resumeStaticInfo: LiveData<ResumeStaticInfo> = resumeStaticInfoRepo.resumeStaticInfo.asLiveData()

fun updateFirstName(resumeStaticInfoFirstName: String) = viewModelScope.launch {
    resumeStaticInfoRepo.updateFirstName(resumeStaticInfoFirstName)
}
....

And the DialogFragment:

class EditNameDialogFragment : DialogFragment() {

private lateinit var callback: OnClickCallback

interface OnClickCallback {
    fun onClick(test: String)
}

override fun onAttach(context: Context) {
    super.onAttach(context)
    try {
        callback = parentFragment as OnClickCallback
    } catch (e: ClassCastException) {
        throw ClassCastException("$context must implement UpdateNameListener")
    }
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val title = requireArguments().getString("title")
    val alertDialogBuilder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
    alertDialogBuilder.setTitle(title)
    val layoutInflater = context?.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    val alertCustomView = layoutInflater.inflate(R.layout.alert_dialog_edit_item, null)
    val editText = alertCustomView.findViewById<EditText>(R.id.alert_edit)
    alertDialogBuilder.setView(alertCustomView)

    alertDialogBuilder.setPositiveButton(
        "Save",
        DialogInterface.OnClickListener { dialog, which ->
            callback.onClick(editText.text.toString())
        })
    alertDialogBuilder.setNegativeButton("No") { _: DialogInterface, _: Int -> }
    return alertDialogBuilder.create()
}

companion object {
    fun newInstance(title: String?): EditNameDialogFragment {
        val frag = EditNameDialogFragment()
        val args = Bundle()
        args.putString("title", title)
        frag.arguments = args
        return frag
    }
}

}

patana93
  • 73
  • 1
  • 8

1 Answers1

1

Do you mean you just want to show a basic dialog for entering some text, and you want to be able to reuse that for multiple EditTexts? And you want a way for the dialog to pass the result back, but also have some way of identifying which EditText it was created for in the first place?

The thing about dialogs is they can end up being recreated (like if the app is destroyed in the background, and then restored when the user switches back to it) so the only real configuration you can do on it (without getting into some complexity anyway) is through its arguments, like you're doing with the title text.

So one approach you could use is send some identifier parameter to newInstance, store that in the arguments, and then pass it back in the click listener. So you're giving the callback two pieces of data in onClick - the text entered and the reference ID originally passed in. That way, the activity can handle the ID and decide what to do with it.

An easy value you could use is the resource ID of the EditText itself, the one you pass into findViewById - it's unique, and you can easily use it to set the text on the view itself. You're using a ViewModel here, so it should be updating automatically when you set a value in that, but in general it's a thing you could do.


The difficulty is that you need to store some mapping of IDs to functions in the view model, so you can handle each case. That's just the nature of making the dialog non-specific, but it's easier than making a dialog for each property you want to update! You could make it a when block, something like:

// you don't need the @ResId annotation but it can help you avoid mistakes!
override fun onClick(text: String, @ResId id: Int) {
    when(id) {
        R.id.coolEditText -> viewModel.setCoolText(text)
        ...
    }
} 

where you list all your cases and what to call for each of them. You could also make a map like

val updateFunctions = mapOf<Int, (String) -> Unit>(
    R.id.coolEditText to viewModel::setCoolText
)

and then in your onClick you could call updateFunctions[id]?.invoke(text) to grab the relevant function for that EditText and call it with the data. (Or use get which throws an exception if the EditText isn't added to the map, which is a design error you want to get warned about, instead of silently ignoring it which is what the null check does)

cactustictacs
  • 17,935
  • 2
  • 14
  • 25
  • Thank you for your answer. While I was waiting for the reply, I've done what you suggest. I have added another parameter to the newInstance function passing the id of the view. Then in the Fragment I have used a when to switch among the various editText. I have also added a live data: `val myVM: LiveData = resumeStaticInfoRepo.resumeStaticInfo.asLiveData()` in the ViewModel and in the fragment I have added the observer: `myVM.getAllData.observe(viewLifecycleOwner, editTest.setText(it.first) editTest2.setText(it.last) } )` – patana93 May 13 '21 at 00:23