最近公司项目有需求在列表里添加一个30分钟倒计时订购的功能,跟普通发送验证码之类的倒计时不同。有以下几个需要注意的地方
1.在列表里做倒计时要避免闪烁以及性能的问题
2.跟验证码60秒等本地的倒计时不同,时间比较长需要避免误差。以及可能会多次刷新
3.锁屏时不刷新或者Android省电机制导致的误差
Android倒计时的实现方式可以看看博客 Android 倒计时的五种实现方式
在列表里做倒计时我的做法是在适配器里创建一个List集合用于保存要刷新的控件,然后创建一个方法刷新控件,核心代码如下
private List<TextView> tvPays = new ArrayList<>(); @Override protected void convert(BaseViewHolder helper, HomePendingListBean item) { ``` TextView tv_pay = helper.getView(R.id.tv_pay); if (!tvPays.contains(tv_pay)) { tvPays.add(tv_pay); } ``` } public void resetNotifyTime() { notifyTime = 0; tvPays.clear(); } public void notifyPay() { notifyTime++; for (int i = 0; i < tvPays.size(); i++) { int expire_time = mData.get(i).getExpire_time() - notifyTime; // int expire_time = mData.get(i).getExpire_time() - mData.get(i).getExpire_time(); String time = getRemainTime(expire_time); if (expire_time<=0){ tvPays.get(i).setClickable(false); tvPays.get(i).setText("("+ResourceUtils.getString(R.string.coupon_list_unvalid)+")"); }else{ tvPays.get(i).setText(time); tvPays.get(i).setClickable(true); } } } 复制代码
因为倒计时是统一一秒一秒的算的,所以我创建了个变量 notifyTime
通过每次调用方法时++。然后根据后台返回的秒数减去 notifyTime
就能实现计数,并且只新列表时只需要调用 resetNotifyTime
把 notifyTime
重置为 0
即可重新计数。
而保存控件的List的作用是为了不适用notify去刷新全部布局,虽然一开始觉得这种做法不太好,不过比起每次去notify布局其实浪费点空间内存反而这种做法cpu损耗的可能更小一些。需要注意的是重置时要把List也清空一下避免后台返回为空时数组越界。
做到这里只需要在定时器的触发方法里调用 adapter.notityPay()
方法即可。这里使用的adapter是 BRVAH 。非常好用,不知道的朋友可以看一下。
我一开始使用的是Handle发送message的方式实现的定时器,这种方式是有误差的。具体可以看看把倒计时做到极致这篇文章。并且由于Android的省电机制在刷屏时Handle发送Message的方式会慢的一逼误差异常大。所以我采用Rxjava的方式实现
private Disposable mTimeCountDisposable; /** * 倒计时 * * @param count */ public void TimeCount(int count) { if (mTimeCountDisposable != null) { //取消上一次的订阅 mTimeCountDisposable.dispose(); } //0-count依次输出,延时0s执行,每1s发射一次。 mTimeCountDisposable = Flowable.intervalRange(0, count, 0, 1, TimeUnit.SECONDS) .observeOn(AndroidSchedulers.mainThread()) .doOnNext(aLong -> { mHomePendingAdapter.notifyPay(); }) .doOnComplete(() -> { if (mTimeCountDisposable != null) mTimeCountDisposable.dispose(); }) .subscribe(); } 复制代码
这种做法经过测试几乎不存在什么误差并且不会因为锁屏问题导致计时变慢,代码也相对简单,使用的是Rxjava2。这里跟第一篇推荐的文章 Android 倒计时的五种实现方式 里面的有些不同,这里使用Rxjava返回了一个Disposable。可以随时取消订阅。因为列表会多次刷新,如果不取消订阅计时代码会存在多份,会影响列表的刷新。当然也不要忘了在 onDestroy
里取消订阅。
@Override public void onDestroy() { super.onDestroy(); if (mTimeCountDisposable != null) { mTimeCountDisposable.dispose(); } } 复制代码
我觉得使用Rxjava的方式实现代码是最少的,并且就性能来说只是一个线程在后台应该不会损耗太多的性能,比起调用系统服务之类的反而我觉得这个轻量一些。 关于锁屏其他解决方案可以自己去试一下。
1.使用系统的定时器然后做一个服务放到系统后台,然后使用广播通知界面。
2.使用系统定时器,注册一个刷屏广播计算刷屏到开屏的时间加上去。
3.让屏幕一直亮着。