Android:一个线程玩转商品列表所有item的倒计时器,并对Adapter进行单控件刷新优化

405 阅读6分钟

所以这次我还是会跟大家讲一下关于商城的一个需求------商品的倒计时器。 或者有人会说,商品的一个倒计时很简单,但是我这次的需求是一个商品列表,每个商品都需要有一个自己的计时器。首先我们看看需求效果:

ScreenRecord_2016-12-04-16-00-26.mp4_1480855963.gif

看到gif图大家都应该大概知道我们要做的是一个商品列表的所有商品的倒计时,而且每个商品都拥有自己的倒计时时间。其实,刚刚看到这样的需求时,我就会第一时间去想用网上什么第三方库去帮我解决这个问题。但看过我之前文章的朋友都应该知道,我本人并不喜欢用第三方的库,因为我觉得要真正提高能力需要的是自己动手。所以最后我还是决定应用自己掌握的最简单的方式去解决这个问题。下面讲讲我自己的做法: 不想听我啰嗦的同学们可以直接下载源码自己研究一下,源码下载链接 倒计时需求.png

JavaBean.png

看到上面的一条RecyclerView的倒计时器,当时我第一时间是想起我要不要在RecyclerView的adapter中开线程去控制每个item。确实,在摸索阶段我试过这样去做,但这样去做我发现问题就来了: 1.在adapter开线程,我到底需要多少个线程,什么时候停止这些线程; 2.RecyclerView的adapter里面有holder的复用,那我开的线程怎么去控制呢。 我自己想了一会,觉得这种方法并不能行得通,因为太多问题我无法控制,而且就算做出来这样的倒计时器绝对把界面卡出翔来。正在我很懊恼的时候,坐我旁边的一位同事说,你看能不能不要在adapter那里开线程,看能不能只开一个线程去处理所有的数据。听到这个,我就突然间恍然大悟。下面就讲讲我想到了什么和具体的做法: 因为我一开始就进入一个误区,或者可以说是一个思维定势。我一开始想的是对每个商品也就是RecyclerView的每个item的数据进行单独的处理,并没有把他们想到一块去,把所有数据一次过做处理,所以我想到的是把所有商品的数据一块去处理,用一个线程就可以做到想要的效果。 下面就开始讲讲我的做法: 1.先去JavaBean那里定义两个极为关键的属性:

定义两个极为关键的存储数据属性.png

然后自己写出他们的get、set方法就好。 2.算出系统时间与活动结束时间的时间差,并保存到每个商品中去: 从后台拿到的数据大致是这样的.png

从后台拿到的数据大致是这样的,这里我只是为了演示一下,所以插进一些本地数据。 算出并返回时间差.png 算出时间差并保存在每个商品的counttime属性.png

3.该去写写我们的线程和handler了: class MyThread implements Runnable{ //用来停止线程 boolean endThread; List<RecommendActivitiesBean.ResultListBean> mRecommendActivitiesList; public MyThread(List<RecommendActivitiesBean.ResultListBean> mRecommendActivitiesList){ this.mRecommendActivitiesList = mRecommendActivitiesList; } @Override public void run() { while(!endThread){ try{ //线程每秒钟执行一次 Thread.sleep(1000); //遍历商品列表 for(int i = 0;i < mRecommendActivitiesList.size();i++){ //拿到每件商品的时间差,转化为具体的多少天多少小时多少分多少秒 //并保存在商品time这个属性内 long counttime = mRecommendActivitiesList.get(i).getCountTime(); long days = counttime / (1000 * 60 * 60 * 24); long hours = (counttime-days*(1000 * 60 * 60 * 24))/(1000* 60 * 60); long minutes = (counttime-days*(1000 * 60 * 60 * 24) -hours*(1000* 60 * 60))/(1000* 60); long second = (counttime-days*(1000 * 60 * 60 * 24) -hours*(1000* 60 * 60)-minutes*(1000*60))/1000; //并保存在商品time这个属性内 String finaltime = days + "天" + hours + "时" + minutes + "分" + second + "秒"; mRecommendActivitiesList.get(i).setTime(finaltime); //如果时间差大于1秒钟,将每件商品的时间差减去一秒钟, // 并保存在每件商品的counttime属性内 if(counttime > 1000) { mRecommendActivitiesList.get(i).setCountTime(counttime - 1000); } } Message message = new Message(); message.what = 1; //发送信息给handler handler.sendMessage(message); }catch (Exception e){ } } } } handler.png

就是一个普通的线程和handler,我相信一般初学java或者android的人都会写出来。我在这个线程里:

1.每一秒钟拿商品自己的时间差counttime出来计算还剩下具体的多少天多少小时多少分多少秒; 2,然后把这个String字符串保存在第一步定义的time属性当中, 如果时间差大于1秒钟,那就在每个商品的时间差counttime中减去1秒钟并再次去保存起来; 3.发送消息通知handler去刷新adapter。 4.最后只需要我们去adapter的TextView中setText就完成了: TextView的setText.png

其实到这里,我们要的倒计时功能已经基本实现。但是,我的师兄就来吹毛求疵了。我告诉他我的想法和做法,他基本认同了,但是他觉得handler里面每一秒钟调用notifyDataSetChanged方法去刷新adapter并不太妥。因为每个item中,你只需要一个TextView去刷新时间,其他控件并不需要刷新。所以我听了他的想法,决定把它一并实现了。下面就是我对adapter的一些刷新优化处理: 1.在onBindViewHolder的时候为每个holder绑定当前显示的position, holder.setDataPosition(position)(方法是自己定义的); 2.考虑到holder的复用性,同时将这个holder保存到adapter中的一个List中(注意要判断是否保存过该holder了); 3.在adapter定义一个方法,方法中去遍历上面的List,然后刷新显示倒计时的TextView。 这是师兄给我的一些思路,下面就是具体的实现: 1.在onBindViewHolder的时候为每个holder绑定当前显示的position, holder.setDataPosition(position)(方法是自己定义的):

setDataPosition方法.png

2.考虑到holder的复用性,同时将这个holder保存到adapter中的一个List中(注意要判断是否保存过该holder了); 保存holder的List.png 将holder保存在list里面.png

3.在adapter定义一个方法,方法中去遍历上面的List,然后刷新显示倒计时的TextView: 遍历list,刷新相应holder的TextView.png

4.最后把handler的刷新adapter的notifyDataSetChanged方法换成自己在adapter写的notifyData方法: 自己写的notifyData方法.png 会动的倒计时器效果.png

很简单,我们只用了一个线程和两个保存数据的属性又完成了一个看起来很麻烦的倒计时器问题,并且用一个极为简便的方法去优化了刷新RecyclerView可能会出现的界面卡顿问题,看上去非常nice。有兴趣学习的同学们可以去下载源码继续研究,有什么问题可以向我提出来一起研究、一起进步!