Listview加载更多后返回头部问题的源码分析

最近基于SwipeRefreshLayout自定义了一个刷新加载组件,但在加载的时候发现一个问题,每次加载更多后listview总会跳回到头部,这样用户体验很不好。虽然通过notifyDataSetChanged解决了。但我们不能一知半解,还是带着问题在源码中分析。

  • 首先,讲点题外话.这里我们要首先要看一下adapter中viewholder的优化机制
 public void setAdapter(ListAdapter adapter) {
        ···
        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

这里我们可以看到,Listview源码中将adapter赋值给全局变量mAdapter。但在Listview的源码中我们找不到mAdapter的声明,但我们可以在其父类AbsListView中可以找到。


既然是了解viewholder的优化机制,我们就必须要找到adapter.setview这个方法,在obtainView中我们可以找到,下面我们看一下其中的几处代码。

这里写图片描述

这里我们看到,mRecycler(这个一会再说)里获取transientView,如果transientView不为空,就和mAdapter获取的view进行匹配,如果不相等的时候,就添加到mRecycler里去。

这里写图片描述

如果transientView为空,从mRecycler取得scrapView,如果scrapView和mAdapter获取的view不相等,就添加到mRecycler里去。最后返回Child。


这里我们就可以看到mRecycler的重要性,那么mRecycler是什么呢

这里写图片描述

我们可以看到,mRecycler是RecycleBin的一个实例。而RecycleBin则提供了一种缓存机制,他把listview的item分为两部分,当前在屏幕显示的为ActiveViews,不在屏幕显示的全部放到ScrapViews里去。

这里就可以看到上面两段代码的作用。首先,从RecycleBin中取出view,作为mAdapter.getView的第二个参数,并和mAdapter.getView的返回值进行匹配,如果相等,就返回RecycleBin中缓存的view。如果不相等,就将mAdapter.getView的返回值添加到缓存里去。

那么就很清楚了,mAdapter.getView的第二个参数,经过RecycleBin的处理,在对view的缓存中起到关键性的作用。而我们使用viewholder以setTag的方式就是为了应用这种缓存机制。


现在回到我们的主题,如果我们在加载更多后使用setAdaper来刷新数据。那么会是一种什么状态呢,首先在listview里重新执行setAdaper,而在setAdaper里会清空之前所保存的缓存。等于说使用了两个不同的adapter实例对listview进行适配,是不可能保存之前的加载位置的。所以我们应该使用notifyDataSetChanged来执行数据刷新,那这里又引申出一个问题。

  • notifyDataSetChanged和notifyDataSetInvalidated的区别

我测试了一下,notifyDataSetChanged在加载更多的时候可以保存当前位置,而notifyDataSetInvalidated则不行,现在我们来了解一下这两个方法
这里写图片描述

这里写图片描述

我们可以看到,notifyDataSetChanged是在隐藏的数据发生变化时,让对应的item刷新数据,而notifyDataSetInvalidated则是在隐藏数据失效或不可用时调用,这样两者的区别就非常明显了,但我们应该找到具体的执行方法,我们继续看下去。

我们看到notifyDataSetInvalidated和notifyDataSetChanged里面是调用了DataSetObservable类中的方法,而当我们继续追踪到DataSetObserver中时,我们发现了只是两个空方法。

这里写图片描述

这好像只是一个单纯的观察者模式实现,我们并没有找到相应的代码逻辑。然而我们可以断定,Listview中肯定存在相应的处理逻辑。

回到Listview的父类AbsListview中,在OnAttachToWindow方法中,我们发现了观察者的注册代码

这里写图片描述

AdapterDataSetObserver继承自AdapterView.AdapterDataSetObserver,在AdapterDataSetObserver中发现如下代码,

这里写图片描述

这里写图片描述

在onchange方法中,通过rememberSyncState,同步当前的屏幕状态。而onInvalidataed则对状态进行了重置。OVER~~

PS:这是第一次来走源代码,在思路上还是懵懵懂懂。有什么不对的,求大神指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值