Android uses recyclerview to achieve list countdown effect
During the recent interview, the interviewer asked how to realize the countdown effect of a list. Now record it.
Operation effect diagram
Realization idea
There are two main implementation methods: 1. Start a timer for each item that starts the countdown, and then update the item; 2. Start only one timer, then traverse the data, and then update the item. After thinking about the performance and implementation, we decided to use the second method.
Implementation process
Data entity
/** * 总共的倒计时的时间(结束时间-开始时间),单位:毫秒 * 例: 2019-02-23 11:00:30 与 2019-02-23 11:00:00 之间的相差的毫秒数 */ private long totalTime; /** * 倒计时是否在暂停状态 */ private boolean isPause = true;
count down
Timer
mTimer.schedule(mTask,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); } } } }
Thread interactive update item
mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: notifyItemChanged(msg.arg1,"update-time"); break; } } };
Performance optimization
1. After calling the notifyitemchanged() method, do not update the entire item (for example, the item contains pictures and does not need to be changed), so override the onbindviewholder (holder, int, list) method:
@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. Destroy resources:
/** * 销毁资源 */ public void destroy() { mHandler.removeMessages(1); if (mTimer != null) { mTimer.cancel(); mTimer.purge(); mTimer = null; } }
Recyclerview. Adapter part of the source code
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,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,比如说只需要更新时间信息,其他不用动 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); } } } } }
Project address: Android uses recyclerview to realize the list countdown effect
The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.