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

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