解決Android-RecyclerView列表倒計時錯亂問題
前言
轉(zhuǎn)眼間距離上次寫博客已是過了一個年輪,期間發(fā)生了不少事;經(jīng)歷了離職、找工作,新公司的第一版項目上線。現(xiàn)在總算是有時間可以將遇到的問題梳理下了,后期有時間也會分享更多的東西~~
場景
今天分享的問題是當(dāng)在列表里面顯示倒計時,這時候滑動列表會出現(xiàn)時間顯示不正常的問題。首先關(guān)于倒計時我們需要注意的問題有以下幾方面:
在RecyclerView中ViewHolder的復(fù)用導(dǎo)致的時間亂跳的問題。
滑動列表時倒計時會重置的問題。
在退出頁面后定時器的資源釋放問題,這里我使用的是用系統(tǒng)自帶的CountDownTimer
ps:這里我們討論的是對倒計時要求不是很嚴(yán)格的場景,對于用戶手動修改系統(tǒng)時間這種操作沒法預(yù)計;對于淘寶秒殺這種業(yè)務(wù)場景建議是實(shí)時不斷請求后臺拿取正確時間,對應(yīng)的接口盡量設(shè)計簡單,響應(yīng)數(shù)據(jù)更快。
接下來通過代碼具體了解:
代碼
// 適配器
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
//服務(wù)器返回數(shù)據(jù)
private List<TimeBean> mDatas;
//退出activity時關(guān)閉所有定時器,避免造成資源泄漏。
private SparseArray<CountDownTimer> countDownMap;
//記錄每次刷新時的時間
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 = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_common, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final TimeBean data = mDatas.get(position);
//記錄時間點(diǎn)
long timeStamp = System.currentTimeMillis() - tempTime;
long time = data.getLeftTime() - timeStamp;
//將前一個緩存清除
if (holder.countDownTimer != null) {
holder.countDownTimer.cancel();
}
if (time > 0) { //判斷倒計時是否結(jié)束
holder.countDownTimer = new CountDownTimer(time, 1000) {
public void onTick(long millisUntilFinished) {
holder.timeTv.setText(getMinuteSecond(millisUntilFinished));
}
public void onFinish() {
//倒計時結(jié)束
holder.timeTv.setText("00:00");
}
}.start();
countDownMap.put(holder.timeTv.hashCode(), holder.countDownTimer);
} else {
holder.timeTv.setText("00:00");
}
}
@Override
public int getItemCount() {
if (mDatas != null && !mDatas.isEmpty()) {
return mDatas.size();
}
return 0;
}
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView timeTv;
public CountDownTimer countDownTimer;
public ViewHolder(View itemView) {
super(itemView);
timeTv = (TextView) itemView.findViewById(R.id.tv_time);
}
}
public void setGetTime(long tempTime) {
this.tempTime = tempTime;
}
/**
* 將毫秒數(shù)換算成 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;
}
/**
* 清空資源
*/
public void cancelAllTimers() {
if (countDownMap == null) {
return;
}
for (int i = 0,length = countDownMap.size(); i < length; i++) {
CountDownTimer cdt = countDownMap.get(countDownMap.keyAt(i));
if (cdt != null) {
cdt.cancel();
}
}
}
}
以上算是整個問題的核心代碼了;其中SparseArray<CountDownTimer> 用來保存列表里面的定時器,用于退出頁面時回收定時器。SparseArray是安卓特有的數(shù)據(jù)結(jié)構(gòu),建議多使用;data.getLeftTime() 是服務(wù)器返回的需要倒計時的時間,毫秒為單位。
問題一:ViewHolder的復(fù)用導(dǎo)致的數(shù)據(jù)錯亂
if (holder.countDownTimer != null) {
holder.countDownTimer.cancel();
}
每次設(shè)置倒計時之前重置下倒計時即可解決。
問題二:滑動列表時倒計時會重置的問題
這個問題是由于解決問題一而導(dǎo)致的,因為列表滑動時離開屏幕的會被復(fù)用,這個時候我們會重新設(shè)置定時器,之前我是在倒計時里面記錄倒計時剩余的時間然后重新設(shè)值,但是還是會有問題;這里借用了系統(tǒng)時間來解決,也就是tempTime 這個值。
首先在服務(wù)器請求成功后回調(diào)里面設(shè)置這個值,如:
private MyAdapter adapter;
@Override
public void onHttpRequestSuccess(String url, HttpContext httpContext) {
if (服務(wù)器返回數(shù)據(jù)) {
adapter.setGetTime(System.currentTimeMillis());
}
相當(dāng)于每次做刷新操作時獲取的都是系統(tǒng)當(dāng)時的時間戳。
然后在adapter里面計算
long timeStamp = System.currentTimeMillis() - tempTime;
long time = data.getLeftTime() - timeStamp;
其中tempTime就是我們保存的系統(tǒng)當(dāng)前時間戳,然后每次滑動列表時都會調(diào)用onBindViewHolder,所以timeStamp就是記錄的距離上次刷新經(jīng)過了多少秒,然后用服務(wù)器返回的需要倒計時的時間減去經(jīng)過的秒數(shù)就是還剩下的倒計時秒數(shù)。最后給定時器設(shè)置上就好了。
問題三:資源的釋放
在當(dāng)前的activity中調(diào)用以下方法。
@Override
protected void onDestroy() {
super.onDestroy();
if (adapter != null) {
adapter.cancelAllTimers();
}
}
好了,今天的分享就到這了,因為代碼比較簡單,布局都是一個Textview,所以沒有貼出來,需要代碼的可以留言~~
補(bǔ)充知識:Android 自定義倒計時,支持listview多item一起倒計時
項目中用到的兩種倒計時,一種是用CountDownTimer,但是這種方式在listview中就不是那么好用了,當(dāng)listview 里面多個item都需要倒計時,就不可以用這種了,我這里想到用Thread 加handler來一起實(shí)現(xiàn)。如果大家還有好的倒計時方法,可以留言一起討論哦,由于代碼都是在項目中的,我就截取幾段代碼。
第一種 CountDownTimer:
主要自定義一個類繼承CountDownTimer,在啟動的時候調(diào)用start(),倒計時完畢調(diào)用canel()方法。
time = new TimeCount(remainingTime, 1000);//構(gòu)造CountDownTimer對象
time.start();//開始計時
class TimeCount extends CountDownTimer {
public TimeCount(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {//計時完畢時觸發(fā)
if (isDead) {
remainingTime = 90000;
ColorStateList colorStateList = getResources().getColorStateList(R.color.button_send_code_text2_selector);
getCode.setTextColor(colorStateList);
getCode.setText(R.string.register_tip7);
getCode.setEnabled(true);
}
}
@Override
public void onTick(long millisUntilFinished) {//計時過程顯示
if (isDead) {
getCode.setEnabled(false);
getCode.setTextColor(getResources().getColor(R.color.grey5));
remainingTime = millisUntilFinished;
getCode.setText(millisUntilFinished / 1000 + "秒后重發(fā)");
}
}
}
第二種 Thread 加handler
創(chuàng)建一個新的線程,每秒中減一次時間,然后在handler中每秒中刷新一次界面就可以看到倒計時的效果。
private Thread thread;
//條目倒計時
public void start() {
thread = new Thread() {
public void run() {
while (true) {
try {
if (list != null) {
for (InvestProjectVo item : list) {
if(item.remainOpenTime == 0){
item.status = 0;
}
if(item.remainOpenTime > 0){
item.remainOpenTime = item.remainOpenTime - 1;
}
}
}
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
thread.start();
}
在adapter的getview()方法中,判斷倒計時時間是否大于0,如果大于零可以繼續(xù)顯示倒計時時間
if (vo.remainOpenTime != 0 && vo.remainOpenTime > 0) {
viewCache.showProjectFullIcon.setVisibility(View.GONE);
viewCache.projectProgress.setVisibility(View.GONE);
viewCache.showTimer.setVisibility(View.VISIBLE);
long tempTime = vo.remainOpenTime;
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 > 0) {
viewCache.timer.setText(minutes + "分" + seconds + "秒");
} else {
viewCache.timer.setText(seconds + "秒");
}
}else{
viewCache.showProjectFullIcon.setVisibility(View.GONE);
viewCache.projectProgress.setVisibility(View.VISIBLE);
viewCache.showTimer.setVisibility(View.GONE);
}
在handler中每秒鐘刷新一次界面
mHandler.sendEmptyMessageDelayed(2586221,1000);
adapter.notifyDataSetChanged(); //每隔1毫秒更新一次界面,如果只需要精確到秒的倒計時此處改成1000即可 mHandler.sendEmptyMessageDelayed(2586221,1000);
以上這篇解決Android-RecyclerView列表倒計時錯亂問題就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
- Android中RecyclerView 滑動時圖片加載的優(yōu)化
- Android XRecyclerView實(shí)現(xiàn)多條目加載
- Android利用RecyclerView實(shí)現(xiàn)列表倒計時效果
- Android列表RecyclerView排列布局
- Android MVVM架構(gòu)實(shí)現(xiàn)RecyclerView列表詳解流程
- Android?Recyclerview實(shí)現(xiàn)左滑刪除功能
- Android?RecyclerView實(shí)現(xiàn)九宮格效果
- Android?手寫RecyclerView實(shí)現(xiàn)列表加載
相關(guān)文章
Android SQLite操作之大數(shù)據(jù)處理與同時讀寫方法
這篇文章主要介紹了Android SQLite操作之大數(shù)據(jù)處理與同時讀寫方法,實(shí)例分析了Android操作SQLite時基于事務(wù)的數(shù)據(jù)緩存與批量插入技巧,以及同時讀寫的相關(guān)實(shí)現(xiàn)方法與注意事項,需要的朋友可以參考下2016-07-07
Android自定義密碼樣式 黑點(diǎn)轉(zhuǎn)換成特殊字符
這篇文章主要為大家詳細(xì)介紹了Android自定義密碼樣式的制作方法,黑點(diǎn)換成¥、%等特殊字符,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07
Android網(wǎng)絡(luò)工具類NetworkUtils詳解
這篇文章主要為大家詳細(xì)介紹了Android網(wǎng)絡(luò)工具類NetworkUtils,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04
Android 系統(tǒng)語言切換監(jiān)聽和設(shè)置實(shí)例代碼
本篇文章主要介紹了Android 系統(tǒng)語言切換監(jiān)聽和設(shè)置實(shí)例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
Android?Settings?跳轉(zhuǎn)流程方法詳解
這篇文章主要為大家介紹了Android?Settings跳轉(zhuǎn)流程方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
Android源碼系列之深入理解ImageView的ScaleType屬性
Android源碼系列第一篇,這篇文章主要從源碼的角度深入理解ImageView的ScaleType屬性,感興趣的小伙伴們可以參考一下2016-06-06

