0

I am working on a simple Photo Editor Android app using Kotlin.

I have decided to add a ProgressBar indicating some calculations going on, when an effect is being applied to an image. Unfortunately, the visibility of it doesn't change before the calculations have been started, making the progress bar not appear even for a moment.

Here is the code:

binding.seekBarEffectVal.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener{
            override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) {

            }

            override fun onStartTrackingTouch(p0: SeekBar?) {

            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {

                binding.progressBarProcessingImage.visibility = View.VISIBLE
                seekBar?.let {
                    // Normalize seekbar value to range 0.0-1.0 with step: 1/1024
                    // With that, we get precise values for rgb ranges (0 to 255, -255 to 255),
                    // some small integer ranges (e.g. blur radius)
                    // and floating point ranges (e.g. contrast coefficient)
                    val value = it.progress.toDouble() / it.max

                    IEffectFactory().createEffect(param1!!, value)?.let {
                        effectConfigApplyListener.onEffectConfigApply(it)
                    }
                }

                binding.progressBarProcessingImage.visibility = View.GONE
            }
        })

This whole thing is inside a class inheriting from Fragment and the effectConfigApplyListener is implemented in the Main Activity, inside which the fragment lays.

What could also be important, every effect has a similar implementation, with runBlocking(Dispatchers.Default) block, which launches computations for each row of an image, as in example below:

override fun modifyPhoto(bitmap: Bitmap): Bitmap {

        val width = bitmap.width
        val height = bitmap.height
        //allocating two integer arrays, one for input image pixels and one for output image pixels
        val pixels = IntArray(width * height)
        val newPixels = IntArray(width * height)
        //filling input image array with pixel values from the bitmap
        bitmap.getPixels(pixels,0, width, 0, 0, width, height)
        val coef = getCoefficient()
        runBlocking(Dispatchers.Default) {
            for (y in 0 until height) {
                launch {
                    for (x in 0 until width) {
                        val colour = pixels[y * width + x]
                        val red = truncate(Color.red(colour) + coef)
                        val green = truncate(Color.green(colour) + coef)
                        val blue = truncate(Color.blue(colour) + coef)
                        newPixels[y * width + x] = Color.rgb(red, green, blue)
                    }
                }
            }
        }
        // creating an output bitmap with calculated pixels
        val newBitmap = Bitmap.createBitmap(width, height, bitmap.config)
        newBitmap.setPixels(newPixels, 0, width, 0, 0, width, height)
        return newBitmap
    }

All I could think of was running the visibility change inside a runBlocking(Dispatchers.Main) block, which I thought would force the UI update before any further code is executed. Unfortunately this approach did not give any expected results.

What could be the reason why the visibility of the progress bar doesn't change when I want it to? My guess is that I somehow block the UI thread but have no idea where.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • "I somehow block the UI thread but have no idea where." that's exactly what `runBlocking` does, blocks the current thread while doing work elsewhere. – weston Aug 27 '23 at 17:19
  • Maybe look at an answer like this: https://stackoverflow.com/a/60834383/360211 – weston Aug 27 '23 at 17:24

0 Answers0