由NotifyDateSetChanged无法生效产生的思考

2018-07-22  本文已影响12人  Rreply

最近在使用notifyDataSetChanged方法时,发现数据并没有按照想象中的刷新,经过查看文档以及搜索,得知了产生这种现象的原因。

 if (mCountDownAdapter == null) {
            mCountDowns = LitePal.findAll(CountDown.class);
            mCountDownAdapter = new CountDownAdapter(mCountDowns);
            mRecyclerView.setAdapter(mCountDownAdapter);
        } 

首先Litepal.findAll方法最终调用了如下onFindAll方法

public <T> List<T> onFindAll(Class<T> modelClass, boolean isEager, long... ids) {
        List<T> dataList;
        if (isAffectAllLines(ids)) {
            dataList = query(modelClass, null, null, null, null, null, "id", null,
                    getForeignKeyAssociations(modelClass.getName(), isEager));
        } else {
            dataList = query(modelClass, null, getWhereOfIdsWithOr(ids), null, null, null, "id",
                    null, getForeignKeyAssociations(modelClass.getName(), isEager));
        }
        return dataList;
    }

可以看出Litepal.findAll最终返回的List是根据查询的数据添加到List中返回的,也就是说如果数据库发生了变化,那么这个List是不会变化的。这就为我们理解RecycleView为什么不能正确刷新打下了基础。

mCountDownAdapter = new CountDownAdapter(mCountDowns);

在这里,Adapter是和mCountDowns连接起来的,mCountdowns就是返回的List。因此在Adapter没有重建的时候,它绑定的都是mCountdowns列表。
在用户输入数据的时候,我是进行下面处理的:

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                mCountDown.setName(s.toString());
                mCountDown.save();    //将CountDown实例产生的变化存储到数据库中
            }

因此,我处理数据变化是方式是直接在数据库中进行修改,而与Adapter绑定的List却不会发生变化。所以Adapter调用NotifyDateSetChanged的时候就不会刷新数据了,因为其绑定的List数据没有刷新。
因此,有两个方法可以解决这个问题,一种是在修改数据到数据库的同时,对Adapter相关联的List也进行修改。一种是将关联的List重新清空,获取数据。从对性能的损失上来看,第一种方法较好。
有时候修改的只是一个item中的数据,而调用NotifyDateSetChanged的时候确实把全部可见项的数据都刷新了,造成了不必要的性能损失。
使用NotifyItemChanged(int position)方法就能够很好地避免这种损失,可以创建一个全局变量来储存点击的位置。

        @Override
        public void onClick(View v) {
            int countdown_id = mCountDown.getId();
            Intent intent = DetailActivity.newIntent(getActivity(), countdown_id);
            startActivity(intent);
            position = getAdapterPosition();   //获取该位置
        }

然而到这里还不算完成,NotifyItemChanged(int position)方法刷新的是这个item中所有的View,不管其有没有被修改。这就导致一个问题,如果这个item中存在很多耗时的动画图片等,刷新这些view就会耗费大量的时间。
举个例子,我刷微博的时候,看到一个博主发的gif太搞笑了,点了一个赞,如果整个item都刷新的话,这个gif也要重新刷新,这就太浪费时间了。
因此使用notifyItemChanged(int position, Object payloads)方法,首先需要重写如下方法

        @Override
        public void onBindViewHolder(ViewHolder holder, int position, List<Object> payloads) {
              //因为payloads为空时,默认刷新item中全部的view,所以需要这样写
            if(!payloads.isEmpty()) {
                if (payloads.get(0) instanceof Integer) {
                    holder.tvScore.setText(String.valueOf((Integer)payloads.get(0)))
                }
            }else {
                super.onBindViewHolder(holder,position, payloads);
            }
        }

最后在需要刷新数据的时候调用notifyItemChanged(int position, Object payloads)就行了。

上一篇下一篇

猜你喜欢

热点阅读