Android如何利用RecyclerView實現(xiàn)列表倒計時效果實例代碼
前言
最近面試時,面試官問了一個列表倒計時效果如何實現(xiàn),然后腦袋突然懵的了O(∩_∩)O,現(xiàn)在記錄一下。
運行效果圖

實現(xiàn)思路
實現(xiàn)方法主要有兩個:
1.為每個開始倒計時的item啟動一個定時器,再做更新item處理;
2.只啟動一個定時器,然后遍歷數(shù)據(jù),再做再做更新item處理。
經(jīng)過思考,包括性能、實現(xiàn)等方面,決定使用第2種方式實現(xiàn)。
實現(xiàn)過程
數(shù)據(jù)實體
/** * 總共的倒計時的時間(結(jié)束時間-開始時間),單位:毫秒 * 例: 2019-02-23 11:00:30 與 2019-02-23 11:00:00 之間的相差的毫秒數(shù) */ private long totalTime; /** * 倒計時是否在暫停狀態(tài) */ 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()) {//不處于暫停狀態(tài)
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;
}
}
};
性能優(yōu)化方面
1.調(diào)用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;//數(shù)據(jù)
private Handler mHandler;//線程調(diào)度,用來更新列表
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);
}
/**
* 設(shè)置新的數(shù)據(jù)源
*
* @param list 數(shù)據(jù)
*/
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()) {//不處于暫停狀態(tài)
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);
}
}
}
}
}
項目地址
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Android OpenGLES如何給相機(jī)添加濾鏡詳解
這篇文章主要給大家介紹了關(guān)于Android OpenGLES如何給相機(jī)添加濾鏡的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Android RecyclerView實現(xiàn)吸頂動態(tài)效果流程分析
RecyclerView是Android一個更強大的控件,其不僅可以實現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實現(xiàn)數(shù)據(jù)縱向滾動,也可以實現(xiàn)橫向滾動(ListView做不到橫向滾動)。接下來講解RecyclerView的用法2022-12-12
adb wireless進(jìn)行Android手機(jī)調(diào)試詳解
這篇文章給大家講解了在Android手機(jī)上使用adb wireless進(jìn)行調(diào)試的步驟以及問題解決辦法,有需要的跟著學(xué)習(xí)下吧。2017-12-12
Android開發(fā)筆記之如何正確獲取WebView的網(wǎng)頁Title
獲取h5頁面的攜帶的title中是很簡單的,下面這篇文章主要給大家介紹了關(guān)于Android開發(fā)筆記之如何正確獲取WebView的網(wǎng)頁Title的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面來一起看看吧2018-09-09
Android學(xué)習(xí)之Broadcast的簡單使用
這篇文章主要為大家詳細(xì)介紹了Android學(xué)習(xí)之Broadcast的簡單使用方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
android使用OkHttp實現(xiàn)下載的進(jìn)度監(jiān)聽和斷點續(xù)傳
本篇文章主要介紹了android使用OkHttp實現(xiàn)下載的進(jìn)度監(jiān)聽和斷點續(xù)傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02

