Recyclerview with Multiple Countdown Timers Causes Flickering

Recyclerview with multiple countdown timers causes flickering

thanx Hammad Tariq Sahi i have used your logic and solve my problem in this way....i have also refereed this link

in my adapter

ArrayList<ViewHolder> viewHoldersList;
private Handler handler = new Handler();
private Runnable updateRemainingTimeRunnable = new Runnable() {
@Override
public void run() {
synchronized (viewHoldersList) {
for (ViewHolder holder : viewHoldersList) {
holder.updateTimeRemaining();
}
}
}
};

inside constructor of my adapter

viewHoldersList = new ArrayList<>();
startUpdateTimer();

and added this method to calculate time

private void startUpdateTimer() {
Timer tmr = new Timer();
tmr.schedule(new TimerTask() {
@Override
public void run() {
handler.post(updateRemainingTimeRunnable);
}
}, 1000, 1000);
}

added two methods to my viewholder class

public void setData(Product product){
this.product = product;
}
public void updateTimeRemaining() {
if(product.getProductType().equalsIgnoreCase("Auction Product")) {
Log.e("hetal",product.getProductType());
try {
String start = product.getStart();
String stop = product.getStop();

//txt_timeleft.setText("");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

Calendar start_date = Calendar.getInstance();
start_date.setTime(format.parse(start));

Calendar end_date = Calendar.getInstance();
end_date.setTime(format.parse(stop));

final Calendar today = Calendar.getInstance();
CountDownTimer timer;

long timeDiff = end_date.getTimeInMillis() - today.getTimeInMillis();
if (timeDiff > 0) {
long seconds = timeDiff / 1000 % 60;
long minutes = timeDiff / (60 * 1000) % 60;
long hours = timeDiff / (60 * 60 * 1000) % 24;
//long days = (int) diff / (24 * 60 * 60 * 1000);
long days = TimeUnit.MILLISECONDS.toDays(timeDiff);

String left = "";
if (days > 0)
left += days + " " + context.getString(R.string.txt_day) + " ,";
if (hours > 0)
left += hours + " " + context.getString(R.string.txt_hour) + " ,";
if (minutes > 0)
left += minutes + " " + context.getString(R.string.txt_minute) + " ,";

left += seconds + " " + context.getString(R.string.txt_second);

final String finalLeft = left;
txt_timeleft.setText(finalLeft);
} else {
txt_timeleft.setText("Time Out !!");
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

and finally inside onBindViewHolder

synchronized (viewHoldersList) {
viewHolder.setData(product);
if(viewHoldersList.size()< (list.size()-2)) viewHoldersList.add(viewHolder);
}

works perfect....thanx all

Android Count Down Timer in RecyclerView flickers between two values

check this Multiple count down timers in RecyclerView flickering when scrolled

On onBindViewHolder check if timer is already initialized or not. If initialized then cancel it first and then start it.

How to fix timer when scrolling on RecyclerView on Android?

When you want use Timer into your adapter, Shouldn't use this in onBindViewHolder.

Because onBindViewHolder call every when you scrolling on items

You should use Timer into constructor of your Adapter, and every second notifyDataSetChanged your adapter.

Don't worry, this is way not bad structure and not memory leak. you can check this in profile tab!

class AuctionsTodayAdapter(val context: Context, val model: MutableList<Today>) :
RecyclerView.Adapter<AuctionsTodayAdapter.MyHolder>() {

private var newData: Long = 0

init {
for (items in model) {
items.end.let {
newData = items.end.toLong()
}
}
//set the timer which will refresh the data every 1 second.
object : CountDownTimer(newData, 1000) {
override fun onFinish() {
notifyDataSetChanged()
}

override fun onTick(p0: Long) {
var i = 0
val dataLength = model.size
while (i < dataLength) {
val item = model[i]
item.end -= 1000
i++
}
notifyDataSetChanged()
}
}.start()
}

override fun onBindViewHolder(holder: MyHolder, position: Int) {
var modelUse = model[position]
//Img
modelUse.image.let {
Glide.with(context)
.load(Constants.MAIN_BASE_URL + it)
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.RESOURCE))
.into(holder.rowMAIN_img)
}
//Timer
modelUse.end.let {
if (modelUse.calculateEnd > 0) {
holder.rowMAIN_timer.text = getDurationBreakdown(modelUse.end.toLong())
} else {
holder.rowMain_timer.text = "Finished"
}
}
}

private fun millToMins(milliSec: Long): String {
var seconds = (milliSec / 1000).toInt()
val hours = seconds / (60 * 60)
val tempMint = seconds - hours * 60 * 60
val minutes = tempMint / 60
seconds = tempMint - minutes * 60

return String.format("%02d", hours) + ":" + String.format(
"%02d",
minutes
) + ":" + String.format("%02d", seconds)
}
}

I hope i can help you.

How to handle multiple countdown timers in ListView?

Instead of trying to show the remaining time for all, the idea is to update the remaining time for the items which are visible.

Please follow the following sample code and let me know :

MainActivity :

public class MainActivity extends Activity {

private ListView lvItems;
private List<Product> lstProducts;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

lvItems = (ListView) findViewById(R.id.lvItems);
lstProducts = new ArrayList<>();
lstProducts.add(new Product("A", System.currentTimeMillis() + 10000));
lstProducts.add(new Product("B", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("C", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("D", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("E", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("F", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("G", System.currentTimeMillis() + 30000));
lstProducts.add(new Product("H", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("I", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("J", System.currentTimeMillis() + 40000));
lstProducts.add(new Product("K", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("L", System.currentTimeMillis() + 50000));
lstProducts.add(new Product("M", System.currentTimeMillis() + 60000));
lstProducts.add(new Product("N", System.currentTimeMillis() + 20000));
lstProducts.add(new Product("O", System.currentTimeMillis() + 10000));

lvItems.setAdapter(new CountdownAdapter(MainActivity.this, lstProducts));
}

private class Product {
String name;
long expirationTime;

public Product(String name, long expirationTime) {
this.name = name;
this.expirationTime = expirationTime;
}
}

public class CountdownAdapter extends ArrayAdapter<Product> {

private LayoutInflater lf;
private List<ViewHolder> lstHolders;
private Handler mHandler = new Handler();
private Runnable updateRemainingTimeRunnable = new Runnable() {
@Override
public void run() {
synchronized (lstHolders) {
long currentTime = System.currentTimeMillis();
for (ViewHolder holder : lstHolders) {
holder.updateTimeRemaining(currentTime);
}
}
}
};

public CountdownAdapter(Context context, List<Product> objects) {
super(context, 0, objects);
lf = LayoutInflater.from(context);
lstHolders = new ArrayList<>();
startUpdateTimer();
}

private void startUpdateTimer() {
Timer tmr = new Timer();
tmr.schedule(new TimerTask() {
@Override
public void run() {
mHandler.post(updateRemainingTimeRunnable);
}
}, 1000, 1000);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = lf.inflate(R.layout.list_item, parent, false);
holder.tvProduct = (TextView) convertView.findViewById(R.id.tvProduct);
holder.tvTimeRemaining = (TextView) convertView.findViewById(R.id.tvTimeRemaining);
convertView.setTag(holder);
synchronized (lstHolders) {
lstHolders.add(holder);
}
} else {
holder = (ViewHolder) convertView.getTag();
}

holder.setData(getItem(position));

return convertView;
}
}

private class ViewHolder {
TextView tvProduct;
TextView tvTimeRemaining;
Product mProduct;

public void setData(Product item) {
mProduct = item;
tvProduct.setText(item.name);
updateTimeRemaining(System.currentTimeMillis());
}

public void updateTimeRemaining(long currentTime) {
long timeDiff = mProduct.expirationTime - currentTime;
if (timeDiff > 0) {
int seconds = (int) (timeDiff / 1000) % 60;
int minutes = (int) ((timeDiff / (1000 * 60)) % 60);
int hours = (int) ((timeDiff / (1000 * 60 * 60)) % 24);
tvTimeRemaining.setText(hours + " hrs " + minutes + " mins " + seconds + " sec");
} else {
tvTimeRemaining.setText("Expired!!");
}
}
}
}

activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ListView
android:id="@+id/lvItems"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</RelativeLayout>

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">

<TextView
android:id="@+id/tvProduct"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Product Name"
android:textSize="16dp"
android:textStyle="bold" />

<TextView
android:id="@+id/tvTimeRemaining"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Time Remaining : " />

</LinearLayout>

ListView with Coundown timer. Timer flickers when scrolling the listview

UPDATE

I updated my answer here have a look: (it's uses cardview with recyclerview but the same principle can be applied also for listview)
How to change text based on time and date?

You have to take care when you create the countdowntimer instance and when you cancel it. If you don't cancel the timer it will update the wrong views.



Related Topics



Leave a reply



Submit