SoFunction
Updated on 2025-04-09

Solve the problem of Android-RecyclerView list countdown disorder

Preface

In a blink of an eye, it was already a year after the last blogging, and a lot of things happened during this period. After resigning and looking for a job, the first version of the new company's project was launched. Now I finally have time to sort out the problems I encountered, and I will share more things if I have time later~~

Scene

The problem I shared today is that when the countdown is displayed in the list, the sliding list will cause abnormal time display. First of all, there are the following issues we need to pay attention to about countdown:

The problem of time jumping caused by the reuse of ViewHolder in RecyclerView.

Issue that countdown will reset when sliding list.

After exiting the page, the timer resource release problem is used. Here I am using the CountDownTimer that comes with the system.

ps: Here we are discussing scenarios where the countdown requirements are not very strict, and it is impossible to predict the operation of manually modifying the system time by users; for business scenarios such as Taobao flash sale, it is recommended to constantly request the background to get the correct time in real time, and try to design the corresponding interface as simple as possible and respond to data faster.

Next, please learn more about it through the code:

Code

// Adapterpublic class MyAdapter extends <> {
  //The server returns data  private List<TimeBean> mDatas;
  //Closing all timers when exiting activity to avoid resource leakage.  private SparseArray<CountDownTimer> countDownMap;

  //Record the time each time it refreshes  private long tempTime;

  public MyAdapter(Context context, List<TimeBean> datas) {
    mDatas = datas;
    countDownMap = new SparseArray<>();
  }

  @Override
  public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = (()).inflate(.list_item_common, parent, false);
    return new ViewHolder(view);
  }

  @Override
  public void onBindViewHolder(final ViewHolder holder, int position) {
    final TimeBean data = (position);
    //Record time point    long timeStamp = () - tempTime;

    long time = () - timeStamp;

    //Clear the previous cache    if ( != null) {
      ();
    }
    if (time > 0) { //Judge whether the countdown is over       = new CountDownTimer(time, 1000) {
        public void onTick(long millisUntilFinished) {
          (getMinuteSecond(millisUntilFinished));
        }
        public void onFinish() {
          //Countdown ends          ("00:00");
        }
      }.start();

      ((), );
    } else {
      ("00:00");
    }
  }

  @Override
  public int getItemCount() {
    if (mDatas != null && !()) {
      return ();
    }
    return 0;
  }

  public class ViewHolder extends  {
    public TextView timeTv;
    public CountDownTimer countDownTimer;

    public ViewHolder(View itemView) {
      super(itemView);
      timeTv = (TextView) (.tv_time);
    }
  }

  public void setGetTime(long tempTime) {
     = tempTime;
  }

  /**
    * Convert milliseconds to 00:00
    */
  public static String getMinuteSecond(long time) {
    int ss = 1000;
    int mi = ss * 60;
    int hh = mi * 60;
    int dd = hh * 24;

    long day = time / dd;
    long hour = (time - day * dd) / hh;
    long minute = (time - day * dd - hour * hh) / mi;
    long second = (time - day * dd - hour * hh - minute * mi) / ss;

    String strMinute = minute < 10 ? "0" + minute : "" + minute;
    String strSecond = second < 10 ? "0" + second : "" + second;
    return strMinute + ":" + strSecond;
  }

  /**
    * Clear resources
    */
  public void cancelAllTimers() {
    if (countDownMap == null) {
      return;
    }
    for (int i = 0,length = (); i < length; i++) {
      CountDownTimer cdt = ((i));
      if (cdt != null) {
        ();
      }
    }
  }
}

The above is the core code of the entire problem; among them, SparseArray<CountDownTimer> is used to save the timer in the list and is used to recycle the timer when exiting the page. SparseArray is a data structure unique to Android, and it is recommended to use it more frequently; () is the time required to count down the server, in milliseconds.

Question 1: Data malfunction caused by ViewHolder reuse

if ( != null) {
    ();
  }

Reset the countdown before setting the countdown.

Question 2: The issue of the countdown reset when sliding the list

This problem is caused by solving the problem, because those who leave the screen when the list slides will be reused. At this time, we will reset the timer. Before, I recorded the remaining time in the countdown and then reset the value, but there will still be problems; here I borrowed system time to solve it, which is the tempTime value.

First, set this value in the callback after the server request is successful, such as:

  private MyAdapter adapter;

  @Override
  public void onHttpRequestSuccess(String url, HttpContext httpContext)   {
    if (Server returns data) {
      (());
  }

It is equivalent to getting the system's time stamp every time you do a refresh operation.

Then calculate in the adapter

long timeStamp = () - tempTime;

long time = () - timeStamp;

tempTime is the current time stamp of the system we saved, and then onBindViewHolder is called every time we slide the list, so timeStamp is how many seconds it has passed since the last refresh, and then the elapsed seconds is used to subtract the elapsed seconds from the server. Finally, just give the timer settings.

Question 3: Release of resources

Call the following method in the current activity.

@Override
protected void onDestroy() {
  ();
  if (adapter != null) {
    ();
  }
}

Okay, that's all for today's sharing. Because the code is relatively simple and the layout is a Textview, it has not been posted. If you need the code, you can leave a message~~

Supplementary knowledge:Android custom countdown, support listview multiple items together countdown

There are two countdown times used in the project, one is to use CountDownTimer, but this method is not so easy to use in listview. When multiple items in listview need to be counted down, this is not allowed. I think of using Thread plus handler to implement it together. If you have a good countdown method, you can leave a message to discuss it together. Since the code is all in the project, I will intercept a few pieces of code.

The first CountDownTimer:

It mainly customizes a class that inherits CountDownTimer, calls start() at startup, and calls canel() method after countdown.

time = new TimeCount(remainingTime, 1000);//Construct CountDownTimer object();//Start timing
class TimeCount extends CountDownTimer {
    public TimeCount(long millisInFuture, long countDownInterval) {
      super(millisInFuture, countDownInterval);
    }

    @Override
    public void onFinish() {// Triggered when the timing is finished      if (isDead) {
        remainingTime = 90000;
        ColorStateList colorStateList = getResources().getColorStateList(.button_send_code_text2_selector);
        (colorStateList);
        (.register_tip7);
        (true);
      }
    }

    @Override
    public void onTick(long millisUntilFinished) {//Download timer      if (isDead) {
        (false);
        (getResources().getColor(.grey5));
        remainingTime = millisUntilFinished;
        (millisUntilFinished / 1000 + "Resend in seconds");
      }
    }
  }

The second type of Thread plus handler

Create a new thread, subtract the time once per second, and then refresh the interface once per second in the handler to see the countdown effect.

 private Thread thread;

  //Entity countdown  public void start() {
    thread = new Thread() {
      public void run() {
        while (true) {
          try {
            if (list != null) {
              for (InvestProjectVo item : list) {

                if( == 0){
                   = 0;
                }

                if( &gt; 0){
                   =  - 1;
                }
              }
            }
            sleep(1000);
          } catch (InterruptedException e) {
            ();
          }
        }
      }
    };
    ();
  }

In the getview() method of adapter, determine whether the countdown time is greater than 0. If it is greater than zero, the countdown time can continue to be displayed.

          if ( != 0 &amp;&amp;  &gt; 0) {

            ();
            ();
            ();

            long tempTime = ;
            long day = tempTime / 60 / 60 / 24;
            long hours = (tempTime - day * 24 * 60 * 60) / 60 / 60;
            long minutes = (tempTime - day * 24 * 60 * 60 - hours * 60 * 60) / 60;
            long seconds = (tempTime - day * 24 * 60 * 60 - hours * 60 * 60 - minutes * 60);
            if (minutes &gt; 0) {
              (minutes + "point" + seconds + "Second");
            } else {
              (seconds + "Second");
            }
          }else{
            ();
            ();
            ();
          }

Refresh the interface every second in the handler

(2586221,1000);

();
//Update the interface every 1 millisecond. If you only need to accurately count down to seconds, change it to 1000.(2586221,1000);

The above article solves the problem of countdown in the Android-RecyclerView list is all the content I have shared with you. I hope you can give you a reference and I hope you can support me more.