I'm basically trying to execute a method in my RecyclerView inside on scroll state change here I'm using a Handler thread to execute a method after some delay but the method is not getting executed at all but however if I put that method outside the handler thread it's getting executed successfully so my question is why am I unable to run Handler thread inside RecyclerView onscroll here is my code please take a look.
rvsongs!!.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if(newState== SCROLL_STATE_IDLE) {
val timerHandler = Handler()
var updater:Runnable?=null
updater = object:Runnable {
override fun run() {
rvsongs!!.setIndexBarVisibility(false)//this method is not getting executed however when put outside handler thread it's executed without any problem
}
}
timerHandler.postDelayed(updater,100)
}
else
{
rvsongs!!.setIndexBarVisibility(true)
}
}
})
The issue is that you are calling setIndexBarVisibilty after the complete onScrollStateChanged is processed. In other words, the recyclerView is rendered before setIndexBatVisibility.
To solve this, just call invalidate after setIndexBarVisibility
...
var updater:Runnable?=null
updater = object:Runnable {
override fun run() {
rvsongs!!.setIndexBarVisibility(false)
rvsongs.invalidate()
}
}
...
Related
I want to call my function indefinitely every 1 second in a specific situation. I'm using AsyncTask to execute my API calls.
I'm using this type of call for some time but this is the first time when it actually blocked my UI Thread and I don't know why.
The handler in the code below is called inside onPostExecute.
protected fun purchaseCheck(transactionId: String){
app.sysLog("Wait for purchase...")
task = asyncTask({
api.checkPaymentStatus(transactionId)
}, taskName = "Purchase Status") { r ->
r.js?.let {
when(r.httpCode){
HTTP_PAYMENT_CHECK_PENDING -> {
App.log("purchaseCheck: response pending purchase - try again")
MainActivity.afterDelay(1000){
purchaseCheck(transactionId)
}
}
else -> {
App.log("purchaseCheck: response purchase success")
onPurchaseSuccessfullyCompleted()
}
}
}?:kotlin.run {
when(r.httpCode){
HTTP_PAYMENT_CARD_EXPIRED -> {
App.log("purchaseCheck: response card expired")
showApiErrorAndRetry(r, App.getString("err_purchase_card_expired"))
}
else -> {
App.log("purchaseCheck: response error (retry)")
MainActivity.afterDelay(1000){
purchaseCheck(transactionId)
}
}
}
}
}
}
Basically
MainActivity.afterDelay(1000){
purchaseCheck(transactionId)
}
is causing my ProgressBar animation to freeze. When I remove that delay it is working as intended.
Here is afterDelay function:
fun afterDelay(delay: Int, body: () -> Unit): Cancellable {
class DelayRun : Runnable, Cancellable {
override fun run() = body()
override fun cancel() {
removePost(this)
}
}
return DelayRun().also {
post(delay, it)
}
}
fun removePost(runnable: Runnable) {
App.handler.removeCallbacks(runnable)
}
fun post(delay: Int, runnable: Runnable){
App.handler.postDelayed(runnable, delay.toLong())
}
Handler in Application class:
class App : Application(), Application.ActivityLifecycleCallbacks{
companion object {
val handler = Handler()
}
...
}
Edit:
After suggestion from post below I implemented Handler like this:
class App : Application(), Application.ActivityLifecycleCallbacks{
companion object {
val handler: Handler by lazy {
HandlerThread("MyHandlerThread").let {
it.start()
Handler(it.looper)
}
}
}
...
}
but it is still freezing my UI Thread. (ProgressBar is lagging)
By default, Handler posts tasks on Main (UI) thread. Therefore any job/task you send to your handler will be executed on UI thread - that is the reason why UI freezes - it waits for job to finish before redrawing.
You want to make your handler using another thread. The most simple way is to create HandlerThread.
val handlerThread = new HandlerThread("MyHandlerThread")
handlerThread.start()
val looper = handlerThread.getLooper()
val handler = new Handler(looper)
After these four lines of code, handler will execute it jobs on another thread. But let's take a problem further - you are using AsyncTask, which is deprecated. You also do not want your delay to be counted by afterDelay function, handler can do it for you.
In your case you can just do something like this:
handler.postDelayed(1000, { ... your job ... }).
Getting it together:
protected fun purchaseCheck(transactionId: String){
app.sysLog("Wait for purchase...")
val runnable = {
val status = api.checkPaymentStatus(transactionId)
status.js?.let { ... }
}
handler.postDelayed(1000, runnable)
}
I also recommend you to declare handler on Activity level rather than Application. Since you usually don't want it to be global.
I want to animate the progress bar so i am setting its progress using for loop but the loop is too much faster that i can't see the animation . I want the code to add a delay in the loop , i tried using thread delay but not working -
here is the code
private fun showProgress() {
for(i in 0..100){
Thread{
binding.customProgressBar.progress=i
Thread.sleep(100)
}
}
}
Solution : Was not calling start method , but if there any other approach then please let me know
private fun showProgress() {
Thread {
for (i in 0..100) {
binding.customProgressBar.progress = i
Thread.sleep(100)
}
}.start()
}
now i want to terminate the thread when fragment is on pause state .
how to achieve that ?
since you are using kotlin it is better to use coroutine, you can achieve your goal with something like this:
private suspend fun someProgress(scope: CoroutineScope) {
val job = scope.launch {
for (i in 0..100) {
binding.customProgress.progress = i
delay(100)
}
}
// use job.cancel() for cancelling the job or use job.join() for waiting for the job to finish
}
you can learn more about coroutine and how it works in here.
Here I am trying to add a view over main_layout for a specific time duration, for that I write this
main_layout.addView(linearLayout)
Handler(Looper.getMainLooper()).post(Runnable {
val timer = Timer()
timer.schedule(object : TimerTask() {
override fun run() {
main_layout.removeView(imageView)
timer.cancel()
}
}, 8000)
})
but app gets crash with the error :
java.lang.IllegalStateException: The current thread must have a looper!
kindly let me know, what else I have to do here.
I have resolved my problem by using HandleMessage() method in handler.
mainHandler = object :Handler(){
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
if (msg!!.what ==1){
removeView()
}
}
}
private fun removedata() {
main_layout.removeView(imageView)
}
also, to perform the desired operation, this method sends the required sets of instruction
mainHandler.sendEmptyMessageDelayed(1,5000)
so as you can see that to call remove(), it requires 1 to fullfil the condition and second parameter is the desired delay I wanted to set.
I'm having an issue where whenever I call this ViewModel method I got the UI blocked and the line ui_registration_done_progressBar.visibility = View.VISIBLE is not executed even though the debbuger shows me that is called normally
The sendRegistration method deals with a lot of stuff and takes some time to be finished, once it is finished the UI is unlocked and the ui_registration_done_progressBar.visibility = View.VISIBLE is executed
override fun onClick(v: View?) {
when(v?.id){
R.id.ui_registration_done_next ->{
ui_registration_done_progressBar.visibility = View.VISIBLE
sendRegistrationViewModel.sendRegistration()
}
}
}
I tried to call faster methods from ViewModel the in this case the problem does not occurs
I would recommend getting the long running task off of the UI thread. You could add use a runnable or an async task.
e.g.:
Runnable runnable = new Runnable() {
#Override
public void run() {
sendRegistrationViewModel.sendRegistration();
}
};
AsyncTask.execute(runnable);
You can use doAsync to run the task in a background thread
import org.jetbrains.anko.doAsync
...
...
override fun onClick(v: View?) {
when(v?.id){
R.id.ui_registration_done_next ->{
doAsync {
ui_registration_done_progressBar.visibility = View.VISIBLE
sendRegistrationViewModel.sendRegistration()
}
}
}
}
adding the dependency into your build.gradle file:
dependencies {
...
...
//* To use doAsync instead of AsyncTask
implementation "org.jetbrains.anko:anko-commons:0.10.4"
}
I have a method called showProgress which runs a fade animation and switches the current view with a page that shows just a loading spinner (ProgressBar).
fun showProgress(show: Boolean, progressView: ProgressBar, layoutView: View): Completable {
val showView = if (show) progressView else layoutView
val hideView = if (show) layoutView else progressView
return fadeAnimation(showView, hideView)
}
private fun fadeAnimation(showView: View, hideView: View): Completable {
return Completable.create {
// animateSemaphore.acquire()
Log.d("beforeFade", "showView: ${showView.visibility}; hideView: ${hideView.visibility}")
val animTime: Long = showView.context.applicationContext.resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
showView.alpha = 0f
handler.post {
showView.visibility = View.VISIBLE
showView.animate()
.alpha(1f)
.setDuration(animTime)
.setListener(null)
hideView.animate()
.alpha(0f)
.setDuration(animTime)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
hideView.visibility = View.GONE
Log.d("fade", "showView: ${showView.visibility}; hideView: ${hideView.visibility}")
it.onComplete()
}
})
}
}
}
I use it when I'm waiting for a network request to complete with RxJava and I want to show the ProgressBar while the app is waiting for the request to finish. However, sometimes the network request will return before showProgress is finished and will cause the animation to run twice causing progressView and layoutView to both be set to View.GONE.
My question is how do I make sure that when showProgress is called while another showProgress is still running, the showProgress being called will wait till the one running is finished? Just an FYI, semaphores won't exactly work since showProgress runs on the main thread and will cause a running showProgress to freeze.