Need to click twice to update text in text view

169 views Asked by At

Hello there I'm working on this project for onscreen handwriting recognition using ML Kit.

The problem that I'm facing is I need to click twice to update the text in the text box. On the first click, it recognises the text and then on the second click, it recognises the text and updates. I'm new to Kotlin and I'm not being able to figure out why.

Main activity :

StrokeManager.download()
recognize.setOnClickListener {

    StrokeManager.recognize(this)

    res()
    textview = findViewById(R.id.textView) as TextView
    textview.setText(res())

}

Strokemanger Object file:

fun recognize(context: Context) {
    val recognizer: DigitalInkRecognizer =
        DigitalInkRecognition.getClient(
            DigitalInkRecognizerOptions.builder(model).build()
        )


    val ink = inkBuilder.build()

    recognizer.recognize(ink)
        .addOnSuccessListener { result: RecognitionResult ->
            recogResult = result.candidates[0].text
        }
        .addOnFailureListener { e: Exception ->
            Log.e("StrokeManager", "Error during recognition: $e")
        }
}

fun res(): String? {
    var resultToText = recogResult

    return resultToText
}

bellow is the link to my git project:

project link

2

There are 2 answers

0
Jenea Vranceanu On BEST ANSWER

recognizer works asynchronously. It means it will not return the result as soon as you call recognize method. By the time you call res() the OnSuccessListener hasn't yet been called.

One option would be to modify fun recognize(context: Context) to accept one more argument as a callback:

fun recognize(context: Context, callback: (String)->Unit) {
    val recognizer: DigitalInkRecognizer =
            DigitalInkRecognition.getClient(
                    DigitalInkRecognizerOptions.builder(model).build()
            )


    val ink = inkBuilder.build()

    recognizer.recognize(ink)
            .addOnSuccessListener { result: RecognitionResult ->
                    callback(result.candidates[0].text)
            }
            .addOnFailureListener { e: Exception ->
                    Log.e("StrokeManager", "Error during recognition: $e")
                    callback("Error during recognition")
            }
}

And this is how an updated version of "reading" the resulting text could look like:

recognize.setOnClickListener {
    recognize.isClickable = false
    StrokeManager.recognize(this) { result ->
        recognize.isClickable = true
        textview = findViewById(R.id.textView) as TextView
        textview.setText(result)
    }
}

I've used isClickable property of recognize view to prevent multiple recognition processes being launch in a short period of time, especially if the previous process is not yet finished.

0
SpiritCrusher On

recognizer.recognize(ink) is an Asynchronous call . So you have to wait for its result i.e either one Success or failure. Since we have to show result when success its better to call #res() from Success. You can do it as follows:

recognizer.recognize(ink)
        .addOnSuccessListener { result: RecognitionResult ->
        recogResult = result.candidates[0].text
        setRecResult()
 }
        .addOnFailureListener { e: Exception ->
        Log.e("StrokeManager", "Error during recognition: $e")
}



fun setRecResult(){
    runOnUiThread {
        textview = findViewById(R.id.textView) as TextView
        textview.setText(recogResult)  
    }
}

runOnUiThread will prevent accessing UI from background thread.