Android 列表倒計時,和對列表刷新的優化,RecyclerView刷新單個控件;

1、每個Item一個計時器,條目多的話,性能損耗太大;

2、單個計時器,然後遍歷數據 刷新條目;

計時器兩種實現方式:1、Handler輪詢; 2、子線程睡眠(時間到後 移除列表中的條目會有問題);

源碼地址:https://github.com/CuiChenbo/CountdownList

代碼很簡單,沒有任何難度,列表使用 RecyclerView+BaseRecyclerViewAdapterHelper實現;

    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.1'

上代碼:

public class MainActivity extends AppCompatActivity {

    private RecyclerView rv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        rv = findViewById(R.id.rv);
        initView();
        initData();

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                quickAdapter.addData(0,new TimeBean("附加商品、離活動結束還剩:" , 99));
                rv.scrollToPosition(0);
            }
        });
    }

    private QuickAdapter quickAdapter;

    private void initView() {
        quickAdapter = new QuickAdapter(R.layout.item);
        rv.setLayoutManager(new LinearLayoutManager(this));
        rv.setAdapter(quickAdapter);
        rv.setItemAnimator(null);
        Countdown();
    }

    private void initData() {
        List<TimeBean> datas = new ArrayList<>();
        for (int i = 1; i < 10; i++) {
            datas.add(new TimeBean("商品" + i + "、離活動結束還剩:", (i + 5) * i));
        }
        quickAdapter.setNewData(datas);
    }


    private class QuickAdapter extends BaseQuickAdapter<TimeBean, BaseViewHolder> {

        public QuickAdapter(int layoutResId) {
            super(layoutResId);
        }

        @Override
        protected void convert(BaseViewHolder vh, TimeBean datas) {
            vh.setText(R.id.tv, datas.getStr() + "");
            vh.setText(R.id.tv2, datas.getTime() + "s");
        }
    }

    private Handler mHandler = new Handler();
    private Runnable runnable;

    private void Countdown() {
        runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < quickAdapter.getData().size(); i++) {
                    TimeBean bean = quickAdapter.getData().get(i);
                    if (bean.getTime() > 0) {
                        bean.setTime(bean.getTime() - 1);
                        quickAdapter.setData(i, bean);
                    } else {
                        quickAdapter.remove(i);
                    }
                }
                mHandler.postDelayed(runnable, 1000L);
            }
        };
        mHandler.postDelayed(runnable, 1000L);
    }


    private void Countdown2() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    SystemClock.sleep(1000L);
                    for (int i = 0; i < quickAdapter.getData().size(); i++) {
                        final TimeBean bean = quickAdapter.getData().get(i);
                        final int finalI = i;
                        if (bean.getTime() > 0) {
                            bean.setTime(bean.getTime() - 1);
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    quickAdapter.setData(finalI, bean); 
                                }
                            });
                        } else {
                            // 當時間是0時 移除條目(子線程加睡眠模式移除條目有問題,原因時數據源未更新)
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    quickAdapter.remove(finalI);
                                }
                            });
                        }
                    }
                }
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(runnable);
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }
public class TimeBean {
    public TimeBean(String str, int time) {
        this.str = str;
        this.time = time;
    }

    private String str;
    private int time;

}

 

性能優化

如果看了上面的代碼可以發現,功能是實現了但是沒有動畫效果,因爲rv.setItemAnimator(null) 屏蔽了RecyclerView的動畫;不屏蔽動畫的話,每次刷新條目都會閃一下;閃一下的原因是條目重新繪製了UI;如果條目中的控件特別多或者有圖片的情況下,用戶體驗會更加不好;

還想要動畫、還想刷新時不閃動;那麼它來了!!!根據id刷新單個控件

刷新RecyclerView條目的指定控件:

看一波源碼:payload 參數的解釋是 傳 null 代表刷新整個條目;

        /*
         * @param position Position of the item that has changed
         * @param payload Optional parameter, use null to identify a "full" update
         *
         * @see #notifyItemRangeChanged(int, int)
         */
        public final void notifyItemChanged(int position, @Nullable Object payload) {
            mObservable.notifyItemRangeChanged(position, 1, payload);
        }

        /*
         * @param positionStart Position of the first item that has changed
         * @param itemCount Number of items that have changed
         * @param payload  Optional parameter, use null to identify a "full" update
         *
         * @see #notifyItemChanged(int)
         */
        public final void notifyItemRangeChanged(int positionStart, int itemCount,
                @Nullable Object payload) {
            mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
        }

可以傳遞控件的id來刷新單個控件,例:

adapter.notifyItemChanged(position); //刷新單個條目 

替換爲下面的 ↓

adapter.notifyItemChanged(position,  R.id.***);  //刷新單個條目中的指定id的控件

adapter.notifyItemRangeChanged(position, ItemCount , R.id.***);  //刷新多條目中的指定id的控件

 

好,上代碼:

public class GoodListActivity extends AppCompatActivity {

    private RecyclerView rv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_good_list);
        rv = findViewById(R.id.rv);
        initView();
        initData();

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mAdapter.addData(0,new TimeBean("附加商品、離活動結束還剩:" , 99));
                rv.scrollToPosition(0);
            }
        });
        findViewById(R.id.btnGood).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               finish();
            }
        });
    }

    private ListAdapter mAdapter;

    private void initView() {
        mAdapter = new ListAdapter(this,R.layout.item);
        rv.setLayoutManager(new LinearLayoutManager(this));
        rv.setItemAnimator(new DefaultItemAnimator());
        rv.setAdapter(mAdapter);
        Countdown();
    }

    private void initData() {
        List<TimeBean> datas = new ArrayList<>();
        for (int i = 1; i < 10; i++) {
            datas.add(new TimeBean("商品" + i + "、離活動結束還剩:", (i + 5) * i));
        }
        mAdapter.setNewData(datas);
    }

    private class ListAdapter extends BaseRecyclerAdapter<TimeBean>{

        public ListAdapter(Context context, int layoutRes) {
            super(context, layoutRes);
        }

        @Override
        public void convert(BaseRecyclerHolder holder, List<TimeBean> items, int position) {
            ((TextView)holder.getView(R.id.tv)).setText(items.get(position).getStr());
            ((TextView)holder.getView(R.id.tvTime)).setText(items.get(position).getTime()+"s");
        }

    }

    private Handler mHandler = new Handler();
    private Runnable runnable;

    private void Countdown() {
        runnable = new Runnable() {
            @Override
            public void run() {
                List<TimeBean> deleteDatas = null; //記錄需要刪除的條目
                for (int i = 0; i < mAdapter.getDatas().size(); i++) {
                    TimeBean bean = mAdapter.getDatas().get(i);
                    if (bean.getTime() > 0) {
                        bean.setTime(bean.getTime() - 1);
                        mAdapter.getDatas().set(i, bean); //時間未到的 ,只改變數據,暫不刷新條目
                    } else {
                        if (deleteDatas == null)
                            deleteDatas = new ArrayList<>();
                        deleteDatas.add(bean); //把需要刪除的條目暫存起來,避免邊遍歷邊操作集合;
                    }
                }
                if (deleteDatas != null && deleteDatas.size() != 0) { //刪除條目
                    for (int i = 0; i < deleteDatas.size(); i++) {
                        mAdapter.remove(deleteDatas.get(i));
                    }
                }

                mAdapter.notifyItemRangeChanged(0, mAdapter.getItemCount(), R.id.tvTime); //刷新條目中的指定控件

                mHandler.postDelayed(runnable, 1000L);
            }
        };
        mHandler.postDelayed(runnable, 1000L);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(runnable);
        mHandler.removeCallbacksAndMessages(null);
        mHandler = null;
    }
}

 

重點就是這一段:

 List<TimeBean> deleteDatas = null; //記錄需要刪除的條目
                for (int i = 0; i < mAdapter.getDatas().size(); i++) {
                    TimeBean bean = mAdapter.getDatas().get(i);
                    if (bean.getTime() > 0) {
                        bean.setTime(bean.getTime() - 1);
                        mAdapter.getDatas().set(i, bean); //時間未到的 ,只改變數據,暫不刷新條目
                    } else {
                        if (deleteDatas == null)
                            deleteDatas = new ArrayList<>();
                        deleteDatas.add(bean); //把需要刪除的條目暫存起來,避免邊遍歷邊操作集合;
                    }
                }
                if (deleteDatas != null && deleteDatas.size() != 0) { //刪除條目
                    for (int i = 0; i < deleteDatas.size(); i++) {
                        mAdapter.remove(deleteDatas.get(i));
                    }
                }

                mAdapter.notifyItemRangeChanged(0, mAdapter.getItemCount(), R.id.tvTime); //刷新條目中的指定控件

 

好啦!BaseRecyclerAdapter的地址:https://blog.csdn.net/qq_35605213/article/details/80176558

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章