I have a button such as
<com.google.android.material.button.MaterialButton
android:id="@ id/button"
style="@style/Widget.MaterialComponents.Button.OutlinedButton.Icon"
android:layout_width="150dp"
android:layout_height="50dp"
android:text="@string/lightning"
android:textColor="#FFFFFF"
android:textSize="14sp"
app:icon="@drawable/ic_baseline_flash_on_24"
app:iconTint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.061"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.239"
app:strokeColor="@color/white"
app:strokeWidth="1dp" />
I have created a setOnClickListener method and inside their is a loop.
val btnClickMe1 = findViewById<Button>(R.id.button)
btnClickMe1.setOnClickListener {
//do something here 20 times
}
This method works fine, however, I have created a .apk file. When I click the button the function runs. I want to know how I can exit the loop by clicking the same button. Regardless of whether or not the loop has finished.
CodePudding user response:
Create a variable that allows you to know the state of the operation, executing or not executing, and performs said operation if the variable is executing.
When you press the button, the variable will change to its negative, when it is false, the next iteration in the loop will detect if it is still active, if not it will skip the loop.
private val isButtonLoopActive: Boolean = false
btn.setOnClickListener {
isButtonLoopActive = !isButtonLoopActive
for (i in 1..20) {
if (isButtonLoopActive) {
//TODO
} else {
break
}
}
}
CodePudding user response:
You haven't posted what your actual code is, but if your loop just runs inside the click listener, then execution won't finish (and the thread won't be able to do anything else, including respond to button clicks) until that loop has finished and you exit the listener function.
You need to set that loop running asynchronously somehow (delayed Runnable
events, a coroutine, a worker thread) so it can do its thing without blocking the thread, and the rest of the app can run as normal. And then your click listener needs to check if that task is currently running - if not, start it, if so then stop it.
Here's a way you could do it with coroutines:
// a reference to the currently running task (if any)
private var job: Job? = null
// kick off a new task, and store a reference to it
private fun startTask() {
// you'll have to use the appropriate scope and 'launchWhen' function
// to keep it running/paused the way you want - depends what you're doing
job = viewLifecycleOwner.lifecycleScope.launchWhenCreated {
repeat(20) {
// the actual thing you're doing!
binding.text.isVisible = !binding.text.isVisible
delay(500)
}
}
}
// stop any running task and discard it, so the next click will start a new one
private fun endTask() {
job?.cancel()
job = null
}
Then your click listener can be:
binding.button.setOnClickListener {
// check if there's a running job -
// if so, the click means stop, otherwise it means start
if (job?.isActive == true) endTask() else startTask()
}
That's just one way - another typical one is having a Runnable
that calls postDelayed
on a Handler
or View
, so it does an action and then posts itself to run again in the future (or doesn't, if some completed condition is met). You'd need to have some running
boolean somewhere, and use that to see if the posting should start or if the Runnable
should give up next time it runs. And it would need to be stopped when the app goes to the background or whatever - again, depends what you're doing