How To Handle Multiple Countdown Timers In Recyclerview?
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”>
<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?"