Android利用RecyclerView實現列表倒計時效果


最近面試時,面試官問了一個列表倒計時效果如何實現,然后腦袋突然懵的了O(∩_∩)O,現在記錄一下。

運行效果圖

Alt

實現思路

實現方法主要有兩個:
1.為每個開始倒計時的item啟動一個定時器,再做更新item處理;
2.只啟動一個定時器,然后遍歷數據,再做再做更新item處理。
經過思考,包括性能、實現等方面,決定使用第2種方式實現。

實現過程

  • 數據實體
/** 
  * 總共的倒計時的時間(結束時間-開始時間),單位:毫秒 
  * 例: 2019-02-23 11:00:30 與 2019-02-23 11:00:00 之間的相差的毫秒數 
  */  
 private long totalTime;  
 /** 
  * 倒計時是否在暫停狀態 
  */  
 private boolean isPause = true;  
  • 倒計時
    Timer
mTimer.schedule(mTask, 0, 1000);

TimerTask

 class MyTask extends TimerTask {
        @Override
        public void run() {
            if (mList.isEmpty()) {
                return;
            }
            int size = mList.size();
            CountDownTimerBean bean;
            long totalTime;
            for (int i = 0; i < size; i++) {
                bean = mList.get(i);
                if (!bean.isPause()) {//不處於暫停狀態
                    totalTime = bean.getTotalTime() - 1000;
                    if (totalTime <= 0) {
                        bean.setPause(true);
                        bean.setTotalTime(0);
                    }
                    bean.setTotalTime(totalTime);
                    Message message = mHandler.obtainMessage(1);
                    message.arg1 = i;
                    mHandler.sendMessage(message);
                }
            }
        }
    }
  • 線程交互更新item
 mHandler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case 1:
                        notifyItemChanged(msg.arg1, "update-time");
                        break;
                }
            }
        };
  • 性能優化方面
    1.調用notifyItemChanged()方法后,不要更新整個item(比如說item包含圖片,不需要變的),所以要重寫onBindViewHolder( Holder , int , List )方法:
    @Override
        public void onBindViewHolder(@NonNull Holder holder, int position, @NonNull List<Object> payloads) {
            if (payloads.isEmpty()) {
                onBindViewHolder(holder, position);
                return;
            }
            //更新某個控件,比如說只需要更新時間信息,其他不用動
            CountDownTimerBean bean = mList.get(position);
            long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
            long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
            long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
            long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
            holder.tvTime.setText("剩余時間: " + day + "天" + hour + "小時" + min + "分" + s + "秒");
            holder.btnAction.setText(bean.isPause() ? "開始" : "暫停");
            holder.btnAction.setEnabled(bean.getTotalTime() != 0);
        }
    

    2.銷毀資源操作:

       /**
         * 銷毀資源
         */
        public void destroy() {
            mHandler.removeMessages(1);
            if (mTimer != null) {
                mTimer.cancel();
                mTimer.purge();
                mTimer = null;
            }
        }
    
    • RecyclerView.Adapter部分源碼
    public class CountDownTimerAdapter extends RecyclerView.Adapter<CountDownTimerAdapter.Holder> {
        private static final String TAG = "CountDownTimerAdapter->";
        private List<CountDownTimerBean> mList;//數據
        private Handler mHandler;//線程調度,用來更新列表
    
        private Timer mTimer;
        private MyTask mTask;
    
        public CountDownTimerAdapter() {
            mList = new ArrayList<>();
            mHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case 1:
                            notifyItemChanged(msg.arg1, "update-time");
                            break;
                    }
                }
            };
            mTask = new MyTask();
        }
    
        public void bindAdapterToRecyclerView(@NonNull RecyclerView view) {
            view.setAdapter(this);
        }
    
        /**
         * 設置新的數據源
         *
         * @param list 數據
         */
        public void setNewData(@NonNull List<CountDownTimerBean> list) {
            destroy();
            mList.clear();
            mList.addAll(list);
            notifyDataSetChanged();
            if (mTimer == null) {
                mTimer = new Timer();
            }
            mTimer.schedule(mTask, 0, 1000);
        }
    
        /**
         * 銷毀資源
         */
        public void destroy() {
            mHandler.removeMessages(1);
            if (mTimer != null) {
                mTimer.cancel();
                mTimer.purge();
                mTimer = null;
            }
        }
    
        @NonNull
        @Override
        public Holder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
            View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_count_down_timer, viewGroup, false);
            return new Holder(view);
        }
    
        @Override
        public void onBindViewHolder(@NonNull Holder holder, int position, @NonNull List<Object> payloads) {
            if (payloads.isEmpty()) {
                onBindViewHolder(holder, position);
                return;
            }
            //更新某個控件,比如說只需要更新時間信息,其他不用動
            CountDownTimerBean bean = mList.get(position);
            long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
            long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
            long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
            long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
            holder.tvTime.setText("剩余時間: " + day + "天" + hour + "小時" + min + "分" + s + "秒");
            holder.btnAction.setText(bean.isPause() ? "開始" : "暫停");
            holder.btnAction.setEnabled(bean.getTotalTime() != 0);
        }
    
        @Override
        public void onBindViewHolder(@NonNull final Holder holder, int position) {
            holder.ivIcon.setImageResource(R.mipmap.ic_launcher_round);
            final CountDownTimerBean bean = mList.get(position);
            long day = bean.getTotalTime() / (1000 * 60 * 60 * 24);
            long hour = (bean.getTotalTime() / (1000 * 60 * 60) - day * 24);
            long min = ((bean.getTotalTime() / (60 * 1000)) - day * 24 * 60 - hour * 60);
            long s = (bean.getTotalTime() / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60);
            holder.tvTime.setText("剩余時間: " + day + "天" + hour + "小時" + min + "分" + s + "秒");
            holder.btnAction.setText(bean.isPause() ? "開始" : "暫停");
            holder.btnAction.setEnabled(bean.getTotalTime() != 0);
            holder.btnAction.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (bean.isPause()) {
                        bean.setPause(false);
                        holder.btnAction.setText("暫停");
                    } else {
                        bean.setPause(true);
                        holder.btnAction.setText("開始");
                    }
                }
            });
        }
    
        @Override
        public int getItemCount() {
            return mList.size();
        }
    
        class Holder extends RecyclerView.ViewHolder {
            private ImageView ivIcon;
            private TextView tvTime;
            private Button btnAction;
    
            Holder(@NonNull View itemView) {
                super(itemView);
                ivIcon = itemView.findViewById(R.id.iv_icon);
                tvTime = itemView.findViewById(R.id.tv_time);
                btnAction = itemView.findViewById(R.id.btn_action);
            }
        }
    
        class MyTask extends TimerTask {
            @Override
            public void run() {
                if (mList.isEmpty()) {
                    return;
                }
                int size = mList.size();
                CountDownTimerBean bean;
                long totalTime;
                for (int i = 0; i < size; i++) {
                    bean = mList.get(i);
                    if (!bean.isPause()) {//不處於暫停狀態
                        totalTime = bean.getTotalTime() - 1000;
                        if (totalTime <= 0) {
                            bean.setPause(true);
                            bean.setTotalTime(0);
                        }
                        bean.setTotalTime(totalTime);
                        Message message = mHandler.obtainMessage(1);
                        message.arg1 = i;
                        mHandler.sendMessage(message);
                    }
                }
            }
        }
    }
    

    項目地址

    源碼


    如有問題,歡迎及時溝通。
    posted @ 2019-02-24 15:30  冬季穿短褲  閱讀( 2367)  評論( 0編輯  收藏


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM