I have count down timer as image below :
now these timers will start count down in each item, i tried many times to change this from my Adapter
Here you can find the source code of Countdown Timer in Recycler Listview here
Adapter.java
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;
public class Adapter extends RecyclerView.Adapter{
private ArrayList al_data;
public class MyViewHolder extends RecyclerView.ViewHolder{
public TextView tv_timer;
CountDownTimer timer;
public MyViewHolder (View view){
super(view);
tv_timer = (TextView)view.findViewById(R.id.tv_timer);
}
}
public Adapter(ArrayList al_data) {
this.al_data = al_data;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_layout,parent,false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(final MyViewHolder holder, int position) {
holder.tv_timer.setText(al_data.get(position));
if (holder.timer != null) {
holder.timer.cancel();
}
long timer = Long.parseLong(al_data.get(position));
timer = timer*1000;
holder.timer = new CountDownTimer(timer, 1000) {
public void onTick(long millisUntilFinished) {
holder.tv_timer.setText("" + millisUntilFinished/1000 + " Sec");
}
public void onFinish() {
holder.tv_timer.setText("00:00:00");
}
}.start();
}
@Override
public int getItemCount() {
return al_data.size();
}
}
Here's a sample that can be used to update the visible items in your RecyclerView. I assume you use LinearLayoutManager for your RecyclerView's layout.
First of all, remove CountDownTimer
from your code. The problem with it is that it uses the main thread's looper to loop through the messages (actually it uses the "current thread" but since you initialize it in your main thread, current thread is main thread now) and it can only be scheduled for a finite interval.
Now the new part. Assume there's your fragment that inits the RecyclerView and all its related parts. Let's say that these are the fields available in your fragment:
private AdapterItems myAdapter;
private LinearLayoutManager layoutManager;
private ScheduledFuture updateFuture;
Then put the following part into your onCreateView(...)
after you're done with all the view, adapter and other initializations:
if (updateFuture == null) {
final Handler mainHandler = new Handler(Looper.getMainLooper());
updateFuture = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
final int firstVisible = layoutManager.findFirstVisibleItemPosition();
final int lastVisible = layoutManager.findLastVisibleItemPosition();
mainHandler.post(new Runnable() {
@Override
public void run() {
myAdapter.notifyItemRangeChanged(firstVisible, lastVisible - firstVisible + 1);
}
});
}
}, 0, 1000, TimeUnit.MILLISECONDS);
}
Here's a little explanation of the code. We create a new single threaded ScheduledExecutorService
which can be used to schedule runnables at fixed rate (scheduleAtFixedRate(...)). Every 1000 milliseconds from the call of this method, there's going to be a new execution.
Now the inner part. Every layout manager (except the base class LayoutManager
) has two very important methods: findFirstVisibleItemPosition() and findLastVisibleItemPosition(). As the names show, these can be used to find the first and last visible items' position in the adapter. Then we use these informations to call the RecyclerView's adapter's (myAdapter
) notifyItemRangeChanged(...) which will update only the given items' view in the range by calling onBindViewHolder(...)
automatically.
Note the mainHandler
which is used to call the notifyItemRangeChanged(...)
method on the UI thread as you cannot update the UI from a non-UI thread (and the one created by the ScheduledExecutorService
is non-UI).
Don't forget to cancel your ScheduledFuture when you don't need the updates anymore by calling its cancel()
method (e.g. in your fragment's onDestroyView()
).
if (updateFuture != null) {
updateFuture.cancel(true);
updateFuture = null;
}
You have your Items
class which, I assume, contains all the data (images, text, etc.) for the list items. You should put the target date to this class as well, this way all your items can have different target dates. If you want to update this date later on, you'll have to change it in the appropriate Items
instance and call notifyItemChanged(position) on your adapter.