0

I am trying to call a function in my fragment via expression binding from my XML file in "android:onclick...", but it will not work. The error is that the fragment is not attached to a context.

It is the

MaterialAlertDialogBuilder(requireContext())

which gives me headache.

How do I give the context to the fragment?

I have seen similar questions regarding that topic, but none that helped me.

Any help is much appreciated.

ItemDetailFragment.kt:

class ItemDetailFragment : Fragment() {

private lateinit var item: Item

private val viewModel: InventoryViewModel by activityViewModels {
    InventoryViewModelFactory(
        (activity?.application as InventoryApplication).database.itemDao()
    )
}

private val navigationArgs: ItemDetailFragmentArgs by navArgs()

private var _binding: FragmentItemDetailBinding? = null
private val binding get() = _binding!!

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    _binding = FragmentItemDetailBinding.inflate(inflater, container, false)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val id = navigationArgs.itemId

    binding.viewModel = viewModel

    binding.fragment = ItemDetailFragment()
}

/**
 * Displays an alert dialog to get the user's confirmation before deleting the item.
 */
fun showConfirmationDialog() {
    MaterialAlertDialogBuilder(requireContext())
        .setTitle(getString(android.R.string.dialog_alert_title))
        .setMessage(getString(R.string.delete_question))
        .setCancelable(false)
        .setNegativeButton(getString(R.string.no)) { _, _ -> }
        .setPositiveButton(getString(R.string.yes)) { _, _ ->
            deleteItem()
        }
        .show()
}

/**
 * Called when fragment is destroyed.
 */
override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

}

fragment_item_detail.kt:

<?xml version="1.0" encoding="utf-8"?><!--
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <variable
        name="viewModel"
        type="com.example.inventory.InventoryViewModel" />

    <variable
        name="fragment"
        type="com.example.inventory.ItemDetailFragment" />

</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="@dimen/margin"
    tools:context=".ItemDetailFragment">

    <Button
        android:id="@+id/delete_item"
        style="?attr/materialButtonOutlinedStyle"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/margin"
        android:onClick="@{()->fragment.showConfirmationDialog()}"
        android:text="@string/delete"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/sell_item" />

</androidx.constraintlayout.widget.ConstraintLayout>

That is the error i am getting:

java.lang.IllegalStateException: Fragment ItemDetailFragment{e562873} (c6ab2144-3bdc-410b-91eb-e5668e8b617a) not attached to a context.

1 Answer 1

1

You should not pass your fragment instance as a data binding variable.
You could define a Boolean mutable live data variable in your InventoryViewModel and show the dialog when it changes:

private val _showConfirmation = MutableLiveData(false)
val showConfirmation
    get() = _showConfirmation

fun onShowConfirmation() {
    _showConfirmation.value = true
}

fun onConfirmationShown() {
    _showConfirmation.value = false
}

Then, define an observer for this property in your ItemDetailFragment:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    val id = navigationArgs.itemId

    binding.viewModel = viewModel
    binding.executePendingBindings()
    
    viewModel.showConfirmation.observe(viewLifecycleOwner) {
        if (it) {
            showConfirmationDialog()
            viewModel.onConfirmationShown()
        }
    }
}

Finally, remove the fragment variable from the XML and change your Button's onClick as:

<Button
    ...
    android:onClick="@{() -> viewModel.onShowConfirmation()}"
    />
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.