• Android利用RecyclerView实现列表倒计时效果


    最近面试时,面试官问了一个列表倒计时效果如何实现,然后脑袋突然懵的了O(∩_∩)O,现在记录一下。

    运行效果图

    Alt

    实现思路

    实现方法主要有两个:
    1.为每个开始倒计时的item启动一个定时器,再做更新item处理;
    2.只启动一个定时器,然后遍历数据,再做再做更新item处理。
    经过思考,包括性能、实现等方面,决定使用第2种方式实现。

    实现过程

    • 数据实体
    /** 
      * 总共的倒计时的时间(结束时间-开始时间),单位:毫秒 
      * 例: 2019-02-23 11:00:30 与 2019-02-23 11:00:00 之间的相差的毫秒数 
      */  
     private long totalTime;  
     /** 
      * 倒计时是否在暂停状态 
      */  
     private boolean isPause = true;  
    
    • 倒计时
      Timer
    mTimer.schedule(mTask, 0, 1000);
    

    TimerTask

     class MyTask extends TimerTask {
            @Override
            public void run() {
                if (mList.isEmpty()) {
                    return;
                }
                int size = mList.size();
                CountDownTimerBean bean;
                long totalTime;
                for (int i = 0; i < size; i++) {
                    bean = mList.get(i);
                    if (!bean.isPause()) {//不处于暂停状态
                        totalTime = bean.getTotalTime() - 1000;
                        if (totalTime <= 0) {
                            bean.setPause(true);
                            bean.setTotalTime(0);
                        }
                        bean.setTotalTime(totalTime);
                        Message message = mHandler.obtainMessage(1);
                        message.arg1 = i;
                        mHandler.sendMessage(message);
                    }
                }
            }
        }
    
    • 线程交互更新item
     mHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 1:
                            notifyItemChanged(msg.arg1, "update-time");
                            break;
                    }
                }
            };
    
    • 性能优化方面
      1.调用notifyItemChanged()方法后,不要更新整个item(比如说item包含图片,不需要变的),所以要重写onBindViewHolder( Holder , int , List )方法:
      @Override
          public void onBindViewHolder(@NonNull Holder holder, int position, @NonNull List<Object> payloads) {
              if (payloads.isEmpty()) {
                  onBindViewHolder(holder, position);
                  return;
              }
              //更新某个控件,比如说只需要更新时间信息,其他不用动
              CountDownTimerBean bean = mList.get(position);
              long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
              long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
              long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
              long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
              holder.tvTime.setText("剩余时间: " + day + "天" + hour + "小时" + min + "分" + s + "秒");
              holder.btnAction.setText(bean.isPause() ? "开始" : "暂停");
              holder.btnAction.setEnabled(bean.getTotalTime() != 0);
          }
      

      2.销毁资源操作:

         /**
           * 销毁资源
           */
          public void destroy() {
              mHandler.removeMessages(1);
              if (mTimer != null) {
                  mTimer.cancel();
                  mTimer.purge();
                  mTimer = null;
              }
          }
      
      • RecyclerView.Adapter部分源码
      public class CountDownTimerAdapter extends RecyclerView.Adapter<CountDownTimerAdapter.Holder> {
          private static final String TAG = "CountDownTimerAdapter->";
          private List<CountDownTimerBean> mList;//数据
          private Handler mHandler;//线程调度,用来更新列表
      
          private Timer mTimer;
          private MyTask mTask;
      
          public CountDownTimerAdapter() {
              mList = new ArrayList<>();
              mHandler = new Handler(Looper.getMainLooper()) {
                  @Override
                  public void handleMessage(Message msg) {
                      switch (msg.what) {
                          case 1:
                              notifyItemChanged(msg.arg1, "update-time");
                              break;
                      }
                  }
              };
              mTask = new MyTask();
          }
      
          public void bindAdapterToRecyclerView(@NonNull RecyclerView view) {
              view.setAdapter(this);
          }
      
          /**
           * 设置新的数据源
           *
           * @param list 数据
           */
          public void setNewData(@NonNull List<CountDownTimerBean> list) {
              destroy();
              mList.clear();
              mList.addAll(list);
              notifyDataSetChanged();
              if (mTimer == null) {
                  mTimer = new Timer();
              }
              mTimer.schedule(mTask, 0, 1000);
          }
      
          /**
           * 销毁资源
           */
          public void destroy() {
              mHandler.removeMessages(1);
              if (mTimer != null) {
                  mTimer.cancel();
                  mTimer.purge();
                  mTimer = null;
              }
          }
      
          @NonNull
          @Override
          public Holder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
              View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_count_down_timer, viewGroup, false);
              return new Holder(view);
          }
      
          @Override
          public void onBindViewHolder(@NonNull Holder holder, int position, @NonNull List<Object> payloads) {
              if (payloads.isEmpty()) {
                  onBindViewHolder(holder, position);
                  return;
              }
              //更新某个控件,比如说只需要更新时间信息,其他不用动
              CountDownTimerBean bean = mList.get(position);
              long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
              long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
              long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
              long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
              holder.tvTime.setText("剩余时间: " + day + "天" + hour + "小时" + min + "分" + s + "秒");
              holder.btnAction.setText(bean.isPause() ? "开始" : "暂停");
              holder.btnAction.setEnabled(bean.getTotalTime() != 0);
          }
      
          @Override
          public void onBindViewHolder(@NonNull final Holder holder, int position) {
              holder.ivIcon.setImageResource(R.mipmap.ic_launcher_round);
              final CountDownTimerBean bean = mList.get(position);
              long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
              long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
              long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
              long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
              holder.tvTime.setText("剩余时间: " + day + "天" + hour + "小时" + min + "分" + s + "秒");
              holder.btnAction.setText(bean.isPause() ? "开始" : "暂停");
              holder.btnAction.setEnabled(bean.getTotalTime() != 0);
              holder.btnAction.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      if (bean.isPause()) {
                          bean.setPause(false);
                          holder.btnAction.setText("暂停");
                      } else {
                          bean.setPause(true);
                          holder.btnAction.setText("开始");
                      }
                  }
              });
          }
      
          @Override
          public int getItemCount() {
              return mList.size();
          }
      
          class Holder extends RecyclerView.ViewHolder {
              private ImageView ivIcon;
              private TextView tvTime;
              private Button btnAction;
      
              Holder(@NonNull View itemView) {
                  super(itemView);
                  ivIcon = itemView.findViewById(R.id.iv_icon);
                  tvTime = itemView.findViewById(R.id.tv_time);
                  btnAction = itemView.findViewById(R.id.btn_action);
              }
          }
      
          class MyTask extends TimerTask {
              @Override
              public void run() {
                  if (mList.isEmpty()) {
                      return;
                  }
                  int size = mList.size();
                  CountDownTimerBean bean;
                  long totalTime;
                  for (int i = 0; i < size; i++) {
                      bean = mList.get(i);
                      if (!bean.isPause()) {//不处于暂停状态
                          totalTime = bean.getTotalTime() - 1000;
                          if (totalTime <= 0) {
                              bean.setPause(true);
                              bean.setTotalTime(0);
                          }
                          bean.setTotalTime(totalTime);
                          Message message = mHandler.obtainMessage(1);
                          message.arg1 = i;
                          mHandler.sendMessage(message);
                      }
                  }
              }
          }
      }
      

      项目地址

      源码


      如有问题,欢迎及时沟通。
    • 相关阅读:
      函数的缺省参数和函数初始化示例以及布尔型参数的使用示例
      指针使用示例程序
      按值传递对象和按址传递对象
      详解js跨域
      CSS之BFC及其应用
      js图片预加载、有序加载
      12个非常有用的JavaScript技巧
      MySQL使用pt-online-change-schema工具在线修改1.6亿级数据表结构
      nodeJS实现一个在线填表应用
      浏览器的缓存机制
    • 原文地址:https://www.cnblogs.com/qq714081644/p/6547192.html
    • Copyright © 2020-2023  润新知