Tongireth
Tongireth

Reputation: 91

Android Kotlin: Make view class pass after it's finished to work

I'd like to make my app displays texts one by one, with typewriter effect. I made a custom view for the effect. When the first custom view finishes displaying the texts, my app would wait for a click on the screen, or the layout. On the click, the next custom view starts to display texts, and when it finishes it waits for a click, and so on.

This is the custom view I made:

class TypeWriterView: AppCompatTextView {
private var mText: CharSequence? = null
private var mIndex = 0
private var mDelay: Long = 150 // in ms

  constructor(context: Context) : super(context)
  constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

  private val mHandler: Handler = Handler()
  private val characterAdder: Runnable = object : Runnable {
      override fun run() {
          text = mText!!.subSequence(0, mIndex++)
          if (mIndex <= mText!!.length) {
              mHandler.postDelayed(this, mDelay)
          }
      }
  }

  fun commence(txt: CharSequence?, delay: Long) {
    mText = txt
    mIndex = 0
    text = ""
    mDelay = delay
    mHandler.removeCallbacks(characterAdder)
    mHandler.postDelayed(characterAdder, mDelay)
  }
}

To make it work like I mentioned above, I thought it would need to pass some values to activity(in my case it's actually a fragment) on finish, so I searched some and put some code in run() method.

override fun run() {
   val job = GlobalScope.launch(Dispatchers.Default) {
       text = mText!!.subSequence(0, mIndex++)
       if (mIndex <= mText!!.length) {
           mHandler.postDelayed(**this**, mDelay)
       }
   }
   runBlocking {
       job.join()
       // pass value after finishing to display texts
   }
}

Then "this" gets red underline with the error message saying:

Type mismatch.
Required: Runnable
Found: CoroutineScope

So what should I put here instead of "this"? Or are there any better way to do this?

Upvotes: 0

Views: 53

Answers (1)

Animesh Sahu
Animesh Sahu

Reputation: 8096

The Handler is of no use when you are using coroutines.

The delay() should be used to delay the execution, and you don't need to push Runnable to the thread again and again.

class TypeWriterView: AppCompatTextView {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)

    suspend fun commence(txt: CharSequence?, delayMs: Long) {
        if (txt == null) return

        var index = 0
        while (index < txt.length) {
            text = txt.subSequence(0, ++index)
            delay(delayMs)
        }
    }

    // Get [Job] reference after launching in [owner]
    fun commenceOnLifecycle(owner: LifecycleOwner, txt: CharSequence?, delayMs: Long): Job =
            owner.lifecycleScope.launch { commence(txt, delayMs) }
}

LifecycleOwner is implemented by Activity, Fragment, etc. whichever has a lifecycle attached (more info: LifecycleOwner), it dispatches job to Dispatchers.Main by default so UI can be changed.

Upvotes: 1

Related Questions