How do I safely re-create my activity when I switch between dark and light mode when using Fragment dialog?

250 views Asked by At

I have a dialog fragment that displays an image whose bytecode is passed to the constructor.

When the dialog is appeared and I am in dark mode and try to change the mode to light one and I come back to my screen, the screen appears all white and I get the error below :

 Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment *.*.*.PictureLoaderDialogFragment: could not find Fragment constructor

I instantiate my fragment dialog as below:

class PictureLoaderDialogFragment(
    private var bitmapByteArray: ByteArray?
) : DialogFragment(){

   companion object {

        @JvmStatic
        fun newInstance(
            bitmapByteArray: ByteArray? = null
        ) = PictureLoaderDialogFragment(bitmapByteArray)
    }
}

Any idea how it should be fixed ?

2

There are 2 answers

0
Viktor Skliarov On BEST ANSWER

This error is caused by passing an argument to your fragment's constructor.

This has to do with the life cycle of fragments and their interaction with the activity. Fragments in Android are designed to be reusable and can be created and destroyed at different points in time depending on the activity's lifecycle. When creating fragments, the Android system uses an empty constructor with no arguments to create a new instance of the fragment after the device configuration changes (in this case, the device theme changes).

In order to solve this problem, there are 3 solutions:

  1. Save arguments in a Bundle
  2. Use FragmentFactory
  3. Use Dependency Injection (DI)

Since you used the newInstance method in your code, the following example shows how to use it, following the first way and saving arguments in a Bundle:

class PictureLoaderDialogFragment : DialogFragment() {

    //...

    companion object {
        fun newInstance(bitmapByteArray: ByteArray? = null): PictureLoaderDialogFragment {
            val fragment = PictureLoaderDialogFragment()
            val args = Bundle()
            args.putByteArray("bitmap", bitmapByteArray)
            fragment.arguments = args
            return fragment
        }
    }
}

You can access the passed arguments inside the PictureLoaderDialogFragment:

val bitmapByteArray = arguments?.getByteArray("bitmap")
bitmapByteArray?.let {
    val image = BitmapFactory.decodeByteArray(it, 0, it.size)
    imageView.setImageBitmap(image)
}

You can show your DialogFragment:

PictureLoaderDialogFragment.newInstance(bitmapByteArray).show(manager, tag)

This article explains how you can use FragmentFactory by following the second solution way

The third way to solvehere, you can learn how to use dependency injection using Hilt

0
Debjit Mandal On

The error message suggests that there is an issue with the constructor of your PictureLoaderDialogFragment. The newInstance method you've implemented is correct for passing arguments to a DialogFragment. However, the issue might lie in the constructor itself.

To fix the problem, make sure that your PictureLoaderDialogFragment class has a constructor that takes no arguments. The code can be like this:

class PictureLoaderDialogFragment : DialogFragment() {

    private var bitmapByteArray: ByteArray? = null

    companion object {

        @JvmStatic
        fun newInstance(bitmapByteArray: ByteArray? = null): PictureLoaderDialogFragment {
            val fragment = PictureLoaderDialogFragment()
            fragment.bitmapByteArray = bitmapByteArray
            return fragment
        }
    }

    // Rest of your fragment implementation...
}

I just have removed the constructor parameter from the PictureLoaderDialogFragment class and instead assign the bitmapByteArray value through a separate setter method. The newInstance factory method is responsible for creating a new instance of the fragment and setting the argument value appropriately.

Make sure to update the rest of your fragment implementation to access the bitmapByteArray variable correctly.