Updating TextView in RecyclerView Adapter on Timer without NotifyDatasetChange()

Viewed 209

My use case is very simple, I have a RecyclerView and each item in RecyclerView has a timer which is supposed to run a countdown to 10 seconds (for example) and update the TextView in that ViewHolder every second to show the time left in seconds.

I created a timer class as following

public class BurnTimerTask2
    {
        private System.Timers.Timer t = new System.Timers.Timer(1000);
        private int Secs;
        private TextView tv;
        public BurnTimerTask2(int Secs, TextView tv)
        {
            this.Secs = Secs;
            this.tv = tv;
            t.Elapsed += T_Elapsed;
            t.Start();
        }

        private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Android.Util.Log.Debug("PMBurnTimers", "Timer: " + Secs);
            Secs--;
            tv.Text = Secs + " Seconds left";
            if (Secs == 0)
                t.Stop();
        }
    }

In my OnBindViewHolder, I am calling it like this

if (!MYLIST[position].TimerTriggered)
{                                       
  MYLIST[position].TimerTriggered= true;
  new BurnTimerTask2(10, viewHolder.Textview);
}
else
{                   
  viewHolder.Textview.Text = "TIMER ALREADY TRIGGERED";                                      
}

The new BurnTimerTask2(10, viewHolder.Textview); is where I am calling the timer class and passing the TextView (In the above code I am checking if timer has been triggered or not, If it has been triggered, I don't call the timer class so I don't have multiple timers for the same item)

ISSUES:

1- TextView which shows the seconds does not update unless I scroll the RecyclerView slightly, once I slightly scroll the RecyclerView it starts ticking and updates text for TextView as expected.

2- As soon as Timer class is called, the UI in my Activity gets non-responsive, I cannot interact with anything outside the RecyclerView for few seconds and when I can it is so slow, if I click a button, it takes forever for the action on that button to trigger. (Note: This only happens when I update the TextView, If I do not update the TextView and run the timer by itself, everything works fine so definalty not the timer which is causing it but updating the TextView from the timer causing it)

Obviously I not updating the TextView the right way. I tried the following

1- Run the timer within the adapter (same issues)

2- Create a timer for each item within ViewHolder pointed out here Countdown timer in recyclerview not working properly (same issues)

According to the following articles, what I am doing should just simply work but it doesn't How to handle multiple countdown timers in RecyclerView? and https://github.com/Manikkumar1988/TimerInRecyclerView

I am not sure how to tackle this issue. Any idea

1 Answers

Hi I have an idea using the Chronometer(https://developer.android.com/reference/android/widget/Chronometer). It is a child of TextView but handles the count and updating View on its own.

The idea is have the viewHolder of your list holds this Chronometer, then in your onBindViewHolder() just make this Chronometer count the correct time (if each item counting a different time).

Try this adapter, I have a separate timer for each item in the list, and onBindViewHolder I will bind that timer into the Chronometer. Try to see if you can replace myTimers[] using your timer class.


public class SOAdapter extends RecyclerView.Adapter<SOAdapter.ViewHolder> {

    Context context;
    int timerNumber;
    Chronometer[] myTimers;
    SOAdapter(Context context, int timerNumber){
        this.context = context;
        this.timerNumber = timerNumber;
        this.myTimers = new Chronometer[timerNumber];
        for (int i = 0; i < timerNumber; i++){
            Chronometer c = new Chronometer(context);
            c.setTextSize(30);
            c.setBase(SystemClock.elapsedRealtime());
            c.start();
            myTimers[i] = c;
        }
    }

    @NonNull
    @Override
    public SOAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        Chronometer timer = new Chronometer(context);
        timer.setLayoutParams(new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        timer.setTextSize(30);
        timer.setBase(SystemClock.elapsedRealtime());
        timer.start();
        return new ViewHolder(timer);
    }

    @Override
    public void onBindViewHolder(@NonNull SOAdapter.ViewHolder holder, int position) {
        Chronometer cur = myTimers[position];
        holder.bindTimer(cur);
    }

    @Override
    public int getItemCount() {
        return timerNumber;
    }


    public class ViewHolder extends RecyclerView.ViewHolder{
        Chronometer displayTimer;
        ViewHolder(Chronometer timer) {
            super(timer);
            displayTimer = timer;
        }

        public void bindTimer(Chronometer timer){
            displayTimer.setBase(timer.getBase());
        }
    }
}


Related