美文网首页Android UIAndroid进阶之旅Android知识
Android 用 RecyclerView 实现倒计时列表功能

Android 用 RecyclerView 实现倒计时列表功能

作者: DoubleThunder | 来源:发表于2017-03-21 23:50 被阅读5293次

    Android 用 RecyclerView 实现倒计时列表功能

    标签(空格分隔): Android RecyclerView CountDownTime

    在很多时候我们会用到一个场景,在 RecyclerView 的 item 上显示不同的倒计时,如下图所示。

    RecyclerView效果RecyclerView效果

    需要注意的地方也没多少:

    1.ViewHolder 的重复使用与倒计时显示冲突。
    2.退出应用时释放所有 CountDownTimer 资源。
    

    关于使用 Android 用 ListView 实现列表倒计时

    Step00.核心代码 MyAdapter.java

    //适配器
    public static class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    
        private List<TimerItem> mDatas;
        //用于退出activity,避免countdown,造成资源浪费。
        private SparseArray<CountDownTimer> countDownMap;
    
        public MyAdapter(Context context, List<TimerItem> 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);
        }
    
        /**
         * 清空资源
         */
        public void cancelAllTimers() {
            if (countDownMap == null) {
                return;
            }
            Log.e("TAG",  "size :  " + countDownMap.size());
            for (int i = 0,length = countDownMap.size(); i < length; i++) {
                CountDownTimer cdt = countDownMap.get(countDownMap.keyAt(i));
                if (cdt != null) {
                    cdt.cancel();
                }
            }
        }
    
        @Override
        public void onBindViewHolder(final ViewHolder holder, int position) {
            final TimerItem data = mDatas.get(position);
            holder.statusTv.setText(data.name);
            long time = data.expirationTime;
            time = time - System.currentTimeMillis();
            //将前一个缓存清除
            if (holder.countDownTimer != null) {
                holder.countDownTimer.cancel();
            }
            if (time > 0) {
                holder.countDownTimer = new CountDownTimer(time, 1000) {
                    public void onTick(long millisUntilFinished) {
                        holder.timeTv.setText(TimeTools.getCountTimeByLong(millisUntilFinished));
                        Log.e("TAG", data.name + " :  " + millisUntilFinished);
                    }
                    public void onFinish() {
                        holder.timeTv.setText("00:00:00");
                        holder.statusTv.setText(data.name + ":结束");
                    }
                }.start();
    
                countDownMap.put(holder.timeTv.hashCode(), holder.countDownTimer);
            } else {
                holder.timeTv.setText("00:00:00");
                holder.statusTv.setText(data.name + ":结束");
            }
    
        }
    
        @Override
        public int getItemCount() {
            if (mDatas != null && !mDatas.isEmpty()) {
                return mDatas.size();
            }
            return 0;
        }
    
        public class ViewHolder extends RecyclerView.ViewHolder {
            public TextView statusTv;
            public TextView timeTv;
            public CountDownTimer countDownTimer;
    
            public ViewHolder(View itemView) {
                super(itemView);
                statusTv = (TextView) itemView.findViewById(R.id.tv_status);
                timeTv = (TextView) itemView.findViewById(R.id.tv_time);
            }
        }
    }
    

    主要通过 SparseArray<CountDownTimer> 类保存每个 Item 上的 CountDownTimer 类。
    当需要复用时,通用 TextView 的 hashCode 取出对应的 CountDownTimer。重新启动并显示。
    当退出此页面时,调用 cancelAllTimers() 方法,遍历 countDownCounters 关闭所有的 CountDownTimer 类。避免造成内存泄漏。

    Step01.数据填充

    mAdapter = new MyAdapter(mContext,TimerItemUtil.getTimerItemList());
    mListView.setAdapter(mAdapter);
    

    Step02.Entity 类

    public class TimerItem {
        //其他属性
        public String name;
        //倒计时长,单位毫秒
        public long expirationTime;
        public TimerItem(String name, long expirationTime) {
            this.name = name;
            this.expirationTime = expirationTime;
        }
    }
    

    Step03.获取倒计时集合(自定义时间)。

    public static List<TimerItem> getTimerItemList() {
        List<TimerItem> lstTimerItems = new ArrayList<>();
        lstTimerItems.add(new TimerItem("A", System.currentTimeMillis() + 11 * 1000));
        lstTimerItems.add(new TimerItem("B", System.currentTimeMillis() + 22 * 1000));
        lstTimerItems.add(new TimerItem("C", System.currentTimeMillis() + 26 * 1000));
        lstTimerItems.add(new TimerItem("D", System.currentTimeMillis() + 33 * 1000));
        lstTimerItems.add(new TimerItem("E", System.currentTimeMillis() + 24 * 1000));
        lstTimerItems.add(new TimerItem("F", System.currentTimeMillis() + 98 * 1000));
        lstTimerItems.add(new TimerItem("G", System.currentTimeMillis() + 14 * 1000));
        lstTimerItems.add(new TimerItem("H", System.currentTimeMillis() + 36 * 1000));
        lstTimerItems.add(new TimerItem("I", System.currentTimeMillis() + 58 * 1000));
        lstTimerItems.add(new TimerItem("J", System.currentTimeMillis() + 47 * 1000));
        lstTimerItems.add(new TimerItem("K", System.currentTimeMillis() + 66 * 1000));
        lstTimerItems.add(new TimerItem("L", System.currentTimeMillis() + 55 * 1000));
        lstTimerItems.add(new TimerItem("M", System.currentTimeMillis() + 62 * 1000));
        lstTimerItems.add(new TimerItem("N", System.currentTimeMillis() + 45 * 1000));
        lstTimerItems.add(new TimerItem("O", System.currentTimeMillis() + 14 * 1000));
    
        return lstTimerItems;
    }
    

    Step04.list_item_common.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:orientation="horizontal"
        android:padding="10dp">
    
        <TextView
            android:id="@+id/tv_status"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            tools:text="status"
            android:layout_gravity="center"
            android:gravity="center"
            />
    
        <TextView
            android:id="@+id/tv_time"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:textSize="15sp"
            android:layout_gravity="center_vertical"
            tools:text="00:01:54"/>
    </LinearLayout>
    

    Step05.当退出 Activity 时释放资源.

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mAdapter != null) {
            mAdapter.cancelAllTimers();
        }
    }
    

    Step06.源代码 Github

    相关文章

      网友评论

      • 愿孤独的人会唱歌:滑动回来之后 又会重一开始的时间开始倒计时 如何处理
      • 一个冬季:每次都new的话,浪费资源
        DreamWinter:其实计时器一个就够了,放在activity里。建一个接口,在Adapter里初始化,然后每次在activity里轮流调用该接口来实现页面更新。
      • 2ffb547a1925:这样写在复用的时候会把前边的数据暂停掉。。。
      • ca956d384340:每次都初始化,你这样计时都没有意义,每次都重新来过
        d499c115489d:@ca956d384340 为什么会重新来过
      • 程序猿男神:TimerItemUtil、TimeTools楼主工具类能否提供一下?谢谢
        DoubleThunder:代码已上传Github https://github.com/sfyc23/CountDownList 。
      • h4de5:嗯。我来说说我们项目中实现的列表中的倒计时效果吧。看了楼主的思路是使用CountDownTimer类,我们项目中采用的方式是继承Chronometer自定义的一个控件,用法也很简单,就是在BindViewHolder 的时候 用CuntDownTimerView.init(初始化时间).start();就可以了。
        DoubleThunder:Chronometer 类是计时器。是不是可以这么理解你们自定义一个 View,在这个 View 里实现了倒计时功能与逻辑处理。我尝试一下。这个项目与你说的实现应该是一样的https://github.com/iwgang/CountdownView
      • 墨源为水:一个列表是不是应该只开启一个定时器,每次定时器改变,会相继改变对应的textview,这样就不用大量的实例化定时器。
        DoubleThunder:@墨源为水 思路我大概明白,但不知道代码怎么实现:joy:
        墨源为水: @sfyc23 可以用sparsearray装textview,定时器每发生一次改变,就去遍历textview通过tag记录改变其值。
        DoubleThunder:那如何才能动态的变化、监听多条 TextView 的变化呢。

      本文标题:Android 用 RecyclerView 实现倒计时列表功能

      本文链接:https://www.haomeiwen.com/subject/tfynnttx.html