如何修复动态适配器包装器Android中的notifyDataSetChanged/ListView问题

ipa*_*son 5 android android-lazyadapter android-listview android-adapter

简介:尝试通过自定义适配器包装器将标题行动态添加到ListView.ListView无法保持滚动位置同步.提供了可运行的演示项目.

我想根据CursorAdapter中的值动态地将项添加到列表中,在用户当前查看的位置之前的几个位置.为此,我有一个包装CursorAdapter的适配器,并将新内容保存在SparseArray中.当项目被添加到自定义适配器时,ListView需要更新,但我遇到了很多陷阱,试图让它工作,并希望得到一些建议.

演示项目可以在这里下载:DynamicSectionedList.zip

在演示中,通过向前看10个位置来动态添加标题,以找到列表项切换到下一个字母的正确位置.notifyDataSetChanged的每个实现都有如下所述的问题:

演示1 此演示显示了notifyDataSetChanged()的重要性.点击任何内容,应用程序将崩溃.这是由于一些理智ListView中检查...... mItemCount != adapter.getItemCount().道德是,我们需要通知列表数据已经改变.

演示2 自然的下一步是在发生更改时通知ListView更改.不幸的是,在ListView滚动时这样做会破坏所有触摸交互,直到应用程序切换到触摸模式.为了注意到这一点,你需要"推动滚动"到足以产生新的标题.点击屏幕不会导致滚动停止,一旦停止,列表项目都不会被点击.这是由于if (!mDataChanged) { /* do very important stuff */ }AbsListView.onTouchEvent()中的一些代码.

演示3 为了解决这个问题,Demo 3引入了pendingChanges标志,自定义适配器获得了一个notifyDataSetChangedIfNeeded(),一旦它进入"安全"状态进行更改,ListView就可以调用它.必须通知更改的第一点是ListView.layoutChildren(),因此我重写该方法以在需要时首先通知更改,然后通过调用.过去至少一个标题然后单击列表项.

虽然我不完全确定原因,但这并不是很正常.使用键盘/轨迹球点击或选择项目会导致列表刷新而不会正确同步旧位置.它滚动到列表的顶部,这是不可接受的.

演示4 演示3中的滚动问题可以被征服,至少在触摸模式下.通过在触摸时添加对notifyDataSetChangedIfNeeded()的调用,数据更改发生在所有触摸交互按预期工作并且列表位置正确同步的时间.

但是,当设备没有处于触摸模式时,我找不到模拟器,更不用说它看起来确实像黑客一样.该列表几乎总是滚动回到顶部,我无法找出导致它偶尔保持正确位置的原因.

由于Android在每一步都在与我作斗争,我觉得应该有更好的方法.请尝试演示,如果可以应用任何修复,让它工作,这将是伟大的!

非常感谢能够对此进行研究的任何人,希望如果我们能够使代码正常工作,那么对于其他试图对带有标题的列表进行相同优化的人来说,这将是有用的.

ipa*_*son 5

上面介绍的方法的主要问题是数据集是直接从超类执行的代码中更改的.通过在getView()中填充标题,我正在更改数据,这可能是操作的中间或超类中的"不安全"状态.这就是为什么在fling滚动期间调用onNotifyDataSetChanged时所有触摸交互都会中断的原因.数据需要从外部源添加,而不是从适配器方法中添加.

这可以通过使用Handler或AsyncTask将标题添加到适配器并调用其notifyDataSetChanged来完成.这些将在UI线程上运行,不会干扰超类状态.感谢CommonsWare的EndlessAdapter示例,用于介绍AsyncTask的概念,以便将数据添加到列表中.

进行更改后,不再需要onTouchEvent()和layoutChildren()黑客攻击.