Reputation: 91
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
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