Skip to content Skip to sidebar Skip to footer

How To Handle Multiple Countdown Timers In Recyclerview?

I have a Recyclerview , and I need to display a countdown on every row. Here is a similar question coutndown timers in listview It has a good solution , but I need that with rec

Solution 1:

The Simple Solution Would Be:

Handler handler=newHandler();
handler.postDelayed(newUpdateTimerThread(holder),0); 

public Class UpdateTimerThread implementsRunnable{
Holder holder;

    publicUpdateTimerThread(Holder holder){
        this.holder=holder;
    }
    @Overridepublicvoidrun() {
        lgetCreatedTime = lgetCreatedTime + 1000;
        long diffSeconds;
        long diffMinutes;
        long diffHours;
        diffSeconds = lgetCreatedTime / 1000 % 60;
        diffMinutes = lgetCreatedTime / (60 * 1000) % 60;
        diffHours = lgetCreatedTime / (60 * 60 * 1000) % 24;
        holder.GameTimer.setText(String.format("%02d:%02d:%02d", diffHours,   diffMinutes, diffSeconds));
        handler.postDelayed(this,1000);
    }
}

Note: 1.holder is the object of ViewHolder class

2.Create a class UpdateTimerThread implements Runnable

3.Convert Date and Time to long and store into lgetCreatedTime

4.Run Handler for every 1 Sec to tick the Time

Solution 2:

Add a CountDownTimer member in the ViewHolder. In onBindViewHolder() set and start the counter, and don't forget to cancel any existing one in the same instance of ViewHolder. In onTick() you need to update the value on the display, not start the counter.

Solution 3:

Here you can check and download the source code of Countdown timers in a RecyclerView

activity_main.xml

<RelativeLayoutandroid:layout_width=”match_parent”android:layout_height=”match_parent”xmlns:android=”http://schemas.android.com/apk/res/android”&gt;

<android.support.v7.widget.RecyclerViewandroid:id=”@+id/recycler_view”android:layout_width=”match_parent”android:layout_height=”wrap_content”android:scrollbars=”vertical” /></RelativeLayout>

adapter_layout.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="15dp"android:padding="10dp"android:id="@+id/tv_timer"/></LinearLayout>

MainActivity.java

package com.androidsolutionworld.multipletimer;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.LinearLayout;
import java.util.ArrayList;

publicclassMainActivityextendsAppCompatActivity {
private RecyclerView recyclerView;
privateArrayListal_data=newArrayList<>();
private Adapter obj_adapter;

@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
    al_data.add("1234");
    al_data.add("1257");
    al_data.add("100");
    al_data.add("1547");
    al_data.add("200");
    al_data.add("500");
    al_data.add("2000");
    al_data.add("1000");

    obj_adapter = newAdapter(al_data);
    LinearLayoutManagerlayoutManager=newLinearLayoutManager(getApplicationContext(),LinearLayoutManager.VERTICAL,false);
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(obj_adapter);
}
}

Custom Adapter:

import android.os.CountDownTimer;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;

publicclassAdapterextendsRecyclerView.Adapter{

private ArrayList al_data;

publicclassMyViewHolderextendsRecyclerView.ViewHolder{
    public TextView tv_timer;
    CountDownTimer timer;

    publicMyViewHolder(View view){
        super(view);
        tv_timer = (TextView)view.findViewById(R.id.tv_timer);

    }


}

publicAdapter(ArrayList al_data) {
    this.al_data = al_data;
}

@Overridepublic MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Viewview= LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_layout,parent,false);


    returnnewMyViewHolder(view);
}

@OverridepublicvoidonBindViewHolder(final MyViewHolder holder, int position) {

    holder.tv_timer.setText(al_data.get(position));

    if (holder.timer != null) {
        holder.timer.cancel();
    }
     longtimer= Long.parseLong(al_data.get(position));

    timer = timer*1000;

    holder.timer = newCountDownTimer(timer, 1000) {
        publicvoidonTick(long millisUntilFinished) {
          holder.tv_timer.setText("" + millisUntilFinished/1000 + " Sec");
        }

        publicvoidonFinish() {
            holder.tv_timer.setText("00:00:00");
        }
    }.start();


}

@OverridepublicintgetItemCount() {
    return al_data.size();
}

}

Solution 4:

here i am extending AppCompatTextView and adding the CountDownTimer inside it. and I am handling onStop and onStart so that CountDownTimer doesnt work in background.

classTimeCounterView : androidx.appcompat.widget.AppCompatTextView {constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

var countDownTimer: CountDownTimer? = nullfunstartCountDown(expireTimeStamp: Long, removeItem: () -> Unit) {
    stopCounter()
    text = calculateTimeExpire(expireTimeStamp)//custom method to calculate time until expireTime
    countDownTimer = object : CountDownTimer(
        expireTimeStamp * 1000 - System.currentTimeMillis(),
        ONE_MINUTE_MILLIS / 16
    ) {
        overridefunonTick(millisUntilFinished: Long) {
            text = calculateTimeExpire(expireTimeStamp)
        }

        overridefunonFinish() {
            text = context.getString(R.string.choice_finished)
            removeItem()
        }
    }
    countDownTimer?.start()
}

funstopCounter() {
    countDownTimer?.cancel()
}

companionobject {
    constval ONE_MINUTE_MILLIS: Long = 60000
}
}

then use it as normal view inside your itemview xml like this

....
    <com.example.custom.TimeCounterView
        android:id="@+id/timeTV"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="12dp"
        android:layout_weight="0"
        android:textColor="@color/colorText"
        tools:text="23:59" />
....

then inside your adapter you will need to implement very important method onViewRecycled which will be called to free the memory from the timer object.

overridefunonViewRecycled(holder: ChannelChoiceViewHolder) {
        super.onViewRecycled(holder)
        holder.stopCounter()
    }

then inside the holder you would do so

funbind(item: ChannelPageChoice, position: Int) {
    //...
    binding.timeTV.startCountDown(item.expire){ removeItem(position) }
    //...
}
funstopCounter() {
    binding.timeTV.stopCounter()
}

then inside the activity/fragment you must set the recyclerview to null in onStop and reattach it again in onStart by doing this onViewRecycled will be called on all your views and timer will be stopped if the recycler is not visible on screen.

overridefunonStop() {
    super.onStop()
    currentItemPosition =
        (binding.recyclerView.layoutManager as? LinearLayoutManager)
            ?.findFirstVisibleItemPosition() ?: 0

    binding.recyclerView.adapter = null
}

overridefunonStart() {
    super.onStart()
    binding.recyclerView.adapter = myCustomAdapter
    binding.recyclerView.scrollToPosition(currentItemPosition)
}

Solution 5:

You can create CountDownTimer reference in ViewHolder class and create its instance in onBindViewHolder method of Adapter.

It works perfectly.

Tested Myself.

Post a Comment for "How To Handle Multiple Countdown Timers In Recyclerview?"