对于倒计时,为了让时间更精细化,以及列表Item的复用性考虑,个人觉得最好的处理办法是接口返回两个时间,一个倒计时结束时间、一个服务器时间,然后我们本地需要算服务器时间与本地时间的差值,第一次算出来后存下来,然后 倒计时时间 = 倒计时结束时间 -(本地时间+时间差值),然后为了复用,我们可以在RecyclerView的onViewAttachedToWindow重新开启倒计时,在onViewDetachedFromWindow的时候关闭当前item的倒计时。
最近项目需要的样式是这样的,没找到现成的库,所以自己就搞了一个简单的,下面是源码:
public class CountDownView extends LinearLayout {
private TextView tvDay, tvHours, tvMinutes, tvSeconds;
private TextView tvDaySuffix, tvHoursSuffix, tvMinutesSuffix;
private CustomCountDownTimer mCustomCountDownTimer;
private long countDownTime;
public CountDownView(Context context) {
this(context, null);
}
public CountDownView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CountDownView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
private void initView() {
View view = LayoutInflater.from(getContext()).inflate(R.layout.count_down_view, this);
tvDay = view.findViewById(R.id.tv_day);
tvDaySuffix = view.findViewById(R.id.tv_day_suffix);
tvHours = view.findViewById(R.id.tv_hour);
tvHoursSuffix = view.findViewById(R.id.tv_hour_suffix);
tvMinutes = view.findViewById(R.id.tv_minutes);
tvMinutesSuffix = view.findViewById(R.id.tv_minutes_suffix);
tvSeconds = view.findViewById(R.id.tv_seconds);
}
public void setItemBackGround(int resId){
tvDay.setBackground(getResources().getDrawable(resId));
tvHours.setBackground(getResources().getDrawable(resId));
tvMinutes.setBackground(getResources().getDrawable(resId));
tvSeconds.setBackground(getResources().getDrawable(resId));
}
public void setSuffixColor(String color){
tvDaySuffix.setTextColor(Color.parseColor(color));
tvHoursSuffix.setTextColor(Color.parseColor(color));
tvMinutesSuffix.setTextColor(Color.parseColor(color));
}
public void setEndTime(long ms) {
countDownTime = ms;
int day;
int hour;
day = (int) (ms / (1000 * 60 * 60 * 24));
hour = (int) ((ms % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
int minute = (int) ((ms % (1000 * 60 * 60)) / (1000 * 60));
int second = (int) ((ms % (1000 * 60)) / 1000);
if (day > 0) {
tvDay.setVisibility(VISIBLE);
tvDaySuffix.setVisibility(VISIBLE);
tvDay.setText(day + "天");
} else {
tvDay.setVisibility(GONE);
tvDaySuffix.setVisibility(GONE);
}
if (hour > 0) {
tvHours.setText(hour < 10 ? "0" + hour : "" + hour);
} else {
tvHours.setText("00");
}
if (minute > 0){
tvMinutes.setText(minute < 10 ? "0" + minute : "" + minute);
}else {
tvMinutes.setText("00");
}
if (second > 0){
tvSeconds.setText(second == 0 ? "00" : (second < 10 ? "0" + second : "" + second));
}else {
tvSeconds.setText("00");
}
}
public void start() {
if (countDownTime <= 0) return;
if (null != mCustomCountDownTimer) {
mCustomCountDownTimer.stop();
mCustomCountDownTimer = null;
}
mCustomCountDownTimer = new CustomCountDownTimer(countDownTime, 1000) {
@Override
public void onTick(long millisUntilFinished) {
setEndTime(millisUntilFinished);
}
@Override
public void onFinish() {
// callback
if (null != listener) {
listener.onEnd();
}
}
};
mCustomCountDownTimer.start();
}
public void stop() {
if (null != mCustomCountDownTimer) mCustomCountDownTimer.stop();
}
public OnCountDownEndListener listener;
public void setOnCountDownEndListener(OnCountDownEndListener listener) {
this.listener = listener;
}
public interface OnCountDownEndListener {
void onEnd();
}
}
public abstract class CustomCountDownTimer {
private static final int MSG = 1;
private final long mMillisInFuture;
private final long mCountdownInterval;
private long mStopTimeInFuture;
private long mPauseTimeInFuture;
private boolean isStop = false;
private boolean isPause = false;
/**
* @param millisInFuture 总倒计时时间
* @param countDownInterval 倒计时间隔时间
*/
public CustomCountDownTimer(long millisInFuture, long countDownInterval) {
// 解决秒数有时会一开始就减去了2秒问题(如10秒总数的,刚开始就8999,然后没有不会显示9秒,直接到8秒)
if (countDownInterval > 1000) millisInFuture += 15;
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
private synchronized CustomCountDownTimer start(long millisInFuture) {
isStop = false;
if (millisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + millisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* 开始倒计时
*/
public synchronized final void start() {
start(mMillisInFuture);
}
/**
* 停止倒计时
*/
public synchronized final void stop() {
isStop = true;
mHandler.removeMessages(MSG);
}
/**
* 暂时倒计时
* 调用{@link #restart()}方法重新开始
*/
public synchronized final void pause() {
if (isStop) return ;
isPause = true;
mPauseTimeInFuture = mStopTimeInFuture - SystemClock.elapsedRealtime();
mHandler.removeMessages(MSG);
}
/**
* 重新开始
*/
public synchronized final void restart() {
if (isStop || !isPause) return ;
isPause = false;
start(mPauseTimeInFuture);
}
/**
* 倒计时间隔回调
* @param millisUntilFinished 剩余毫秒数
*/
public abstract void onTick(long millisUntilFinished);
/**
* 倒计时结束回调
*/
public abstract void onFinish();
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CustomCountDownTimer.this) {
if (isStop || isPause) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
adapter中代码如下:
public class WelfareProductsListAdapter extends BaseQuickAdapter
//服务器与手机本地时间差
public long gapTime;
public WelfareProductsListAdapter(int layoutResId, @Nullable List data) {
super(layoutResId, data);
}
@Override
protected void convert(@NonNull BaseViewHolder helper, WelfareItemModel item) {
CountDownView countdownView = helper.getView(R.id.cv_countdownView_turn);
countdownView.setTag(item.getId());
long localTime = System.currentTimeMillis() / 1000;
//倒计时的时间
long endTime = (item.getStartTime() - (localTime + gapTime)) * 1000;
if (endTime > 0) {
countdownView.setEndTime(endTime);
countdownView.start();
} else {
countdownView.stop();
}
countdownView.setOnCountDownEndListener(() -> {
notifyItemChanged(helper.getAdapterPosition());
});
}
@Override
public void onViewAttachedToWindow(BaseViewHolder holder) {
super.onViewAttachedToWindow(holder);
CountDownView countdownView = holder.getView(R.id.cv_countdownView_turn);
int position = holder.getAdapterPosition();
if (countdownView != null) {
long localTime = System.currentTimeMillis() / 1000;
//倒计时的时间
long endTime = (getItem(position).getStartTime() - (localTime + gapTime)) * 1000;
if (endTime > 0) {
countdownView.setEndTime(endTime);
countdownView.start();
countdownView.setVisibility(View.VISIBLE);
} else {
countdownView.stop();
countdownView.setVisibility(View.INVISIBLE);
}
}
}
@Override
public void onViewDetachedFromWindow(@NonNull BaseViewHolder holder) {
super.onViewDetachedFromWindow(holder);
CountDownView countdownView = holder.getView(R.id.cv_countdownView_turn);
if (countdownView != null) {
countdownView.stop();
}
}
}