Android Studio - Kotlin doOnLayout never initialised

206 Views Asked by At

trying to create a dialogFragment to show a larger resolution image of the thumbnail that was clicked. I have narrowed it down the the doOnLayout not running using logs as there are no error or issues raised by the IDE. I have tried to include a delay and post{} separately which did not change anything. All the logs in the code show the correct information relating to the image. I feel like i am not creating the layout correctly or similar but do not have the knowledge to check how.

I have a class to create on dialog fragment when a button click to open the relevant image. Sorry the code is quite messy, i am new to this language and have been trying many different things to no avail. If there was an error code or issue i could likely debug it myself over time, however there is no error or issues present, the dialog just never opens. Some tips on debugging or any instances of code that are incorrect would be much appreciated.

class PictureDialogFragment: DialogFragment() {

    companion object {
        private const val ARG_IMAGE = "ARG_IMAGE"

        fun newInstance(photoFile: String?): PictureDialogFragment {
            val args = Bundle().apply { putString(ARG_IMAGE, photoFile) }
            return PictureDialogFragment().apply { arguments = args }
        }
    }

    @RequiresApi(Build.VERSION_CODES.TIRAMISU)

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let { it ->
            val builder = AlertDialog.Builder(it)
            val inflater = requireActivity().layoutInflater
            val view = inflater.inflate(R.layout.dialog_photo, null)
            
            val photoFileName = arguments?.getString(ARG_IMAGE)
            Log.d("debug", "$photoFileName retrieved photofilename")

            val photoFile = photoFileName?.let {
                File(requireContext().applicationContext.filesDir, it)
            }
            Log.d("debug", "$photoFile retrieved photoFile")

            val imageView = view.findViewById(R.id.dialog_photo_viewer) as ImageView
            Log.d("debug", "$imageView retrieved imageview")

            updatePhotoForDialog(photoFile, imageView)
            builder.create()

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

When the updatePhotoForDialog is ran the log files have the correct information relative to the image. The imageView.doOnLayout is never ran. I have tried delaying and post{}.

private fun updatePhotoForDialog(photoFile: File?, imageView: ImageView) {
    Log.d("debug", "updatePhotoForDialog called $imageView, $photoFile")
    if (photoFile != null && photoFile.exists()) {
        Log.d("debug", "photoFile called inside updatePhotoForDialog: $photoFile")
            imageView.doOnLayout {
                Log.d("debug", "doOnLayout: $photoFile")
                val scaledBitmap = getScaledBitmap(
                    photoFile.path,
                    imageView.width,
                    imageView.height
                )
                imageView.setImageBitmap(scaledBitmap)
                Log.d("debug", "setImageBitmap error")
            }
        } else {
            imageView.setImageBitmap(null)
            Log.d("debug", "updatePhotoForDialog error")
        }
    }
}

This is how the code is called in my other file. Creates a newInstance, triggering the onCreateDialog, which uses the updatePhotoForDialog. So everything works until the imageView.doOnLayout.

plantPhoto.setOnClickListener{
                if (photoName?.isNotBlank() == true) {
                    Log.d("Debug", "photoName contains: $photoName")
                    photoName?.let { it1 ->
                        val dialogFragment = PictureDialogFragment.newInstance(it1)
                        dialogFragment.show(parentFragmentManager, "PictureDialogFragmentTag")
                    }
                }
            }

i have a file provider set up in the AndroidManifest connected to a files.xml, and this is my build.gradle

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.devtools.ksp")
    id("androidx.navigation.safeargs")
}

android {
    namespace = "com.example.greenspot"
    compileSdk = 33

    defaultConfig {
        applicationId = "com.example.greenspot"
        minSdk = 25
        targetSdk = 33
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

    }


    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8

    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        viewBinding = true
    }
}

dependencies {
    implementation("com.github.bumptech.glide:glide:4.12.0")
    annotationProcessor("com.github.bumptech.glide:compiler:4.12.0")
    implementation("androidx.core:core-ktx:1.9.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.9.0")
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
    implementation("androidx.fragment:fragment-ktx:1.6.1")
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1")
    implementation("androidx.recyclerview:recyclerview:1.2.1")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    debugImplementation ("androidx.fragment:fragment-testing:1.6.1")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
    implementation("androidx.room:room-runtime:2.5.0")
    implementation("androidx.room:room-ktx:2.5.0")
    ksp("androidx.room:room-compiler:2.5.0")
    implementation("androidx.navigation:navigation-fragment-ktx:2.4.1")
    implementation("androidx.navigation:navigation-ui-ktx:2.4.1")


}
1

There are 1 best solutions below

0
vasberc On

I do not see in your code to set the view that you inflate anywhere in the dialog builder or in the dialog that you are returning. As a result the inflated view and it's childrent are never attached to the view and never layout. But because I see you have the viewBinding feature enabled and you are using DialogFragment, I would suggest an easier way to create your dialog:

  //Pass to the constructor the layout, so the onViewCreated will be called    
  class PictureDialogFragment: DialogFragment(R.layout.dialog_photo) {
    
            var _binding: DialogPhotoBinding? = null
            val bindind get() = _binding!!
        
            companion object {
                private const val ARG_IMAGE = "ARG_IMAGE"
        
                fun newInstance(photoFile: String?): PictureDialogFragment {
                    val args = Bundle().apply { putString(ARG_IMAGE, photoFile) }
                    return PictureDialogFragment().apply { arguments = args }
                }
            }
        
            @RequiresApi(Build.VERSION_CODES.TIRAMISU)
            override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
                    //Initialize the binding here so you can use it's properties
                    _binding = DialogPhotoBinding.bind(view)
                    super.onViewCreated(view, savedInstanceState)
                    
                    val photoFileName = arguments?.getString(ARG_IMAGE)
                    Log.d("debug", "$photoFileName retrieved photofilename")
        
                    val photoFile = photoFileName?.let {
                        File(requireContext().applicationContext.filesDir, it)
                    }
                    Log.d("debug", "$photoFile retrieved photoFile")
        
                    
                    Log.d("debug", "${binding.dialogPhotoViewer} retrieved imageview")
                    //Pass the imageView from the binding object
                    updatePhotoForDialog(photoFile, binding.dialogPhotoViewer)   
               
            }

            private fun updatePhotoForDialog(photoFile: File?, imageView: ImageView) {
                ...
            }

            override fun onDestroyView() {
                _binding = null
                super.onDestroyView()
            }

    }