Android - ListView 中的延迟点击
Posted
技术标签:
【中文标题】Android - ListView 中的延迟点击【英文标题】:Android - delayed clicks in ListView 【发布时间】:2012-02-07 13:27:24 【问题描述】:我的应用中有以下结构:
带有ViewPager
的FragmentActivity 持有多个由FragmentStatePagerAdapter
管理的片段,使用android 2.1 的兼容包
每个片段都包含ListView
。 ListView
中的每个元素都有一个LinearLayout
,其中有两个TextViews
和一个Button
。 LinearLayout
和按钮具有 onClickListeners
(单独)。单击LinearLayout
会启动另一个Activity
。我注意到点击行为非常不一致:有时会立即执行操作,但通常会延迟,有时无论我点击多少次都会忽略它。它变得更加奇怪,因为我可以点击并且只有在我开始滚动列表时才会执行该操作。我尝试了setFocusable(false)
和setSelectable(true)
的各种组合,但似乎没有任何区别。有任何想法吗?我很乐意提供更多详细信息。
【问题讨论】:
【参考方案1】:我遇到了类似的问题,我花了 2 天时间来调试并解决它。 我有一个 ListAdapter,它在 LinearLayout 中为每个列表项创建几个 TextView。 每个 TextView 都有自己的 OnClickListener,因为我需要处理每个项目的点击。
当我更改实现以便重用视图时,OnClickListener 停止正常工作。在 4.4.2 上,大多数点击都有效,但有时在我滚动列表之前没有任何反应。在 2.3 上,第一次点击无法正常工作,然后所有点击都会突然处理。
在我的特殊情况下,我在 Java 代码中创建了所有视图,而不是通过膨胀资源。 关键点是,即使视图被重用,我也确实设置了 LinearLayout 的 LayoutParams(这似乎更安全,然后假设重用的视图具有正确的布局参数)。 当我在重用时不设置 LayoutParams 一切正常! 这是关键代码:
public View getView(int position, View convertView, ViewGroup parent)
LinearLayout tapeLine = null;
if (convertView != null && convertView instanceof LinearLayout && ((LinearLayout)convertView).getChildCount() == 4) tapeLine = (LinearLayout) convertView; // Reuse view
else tapeLine = new LinearLayout(activity);
if (convertView == null) // Don't set LayoutParams when reusing view
ViewGroup.LayoutParams tapeLineLayoutParams = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
tapeLine.setLayoutParams(tapeLineLayoutParams);
ScrollingTape scrollingTape = calculatorHolder.getCalculator().getScrollingTape();
int tapeWidthPx = parent.getWidth();
TapeLineTextSizeInfo tapeLineTextSizeInfo = calculatorHolder.getTapeLineTextSizeHelper().createTapeLineTextSizeInfo(tapeWidthPx);
ScrollingTapeLine line = scrollingTape.getLine(position);
tapeLine.setOrientation(LinearLayout.HORIZONTAL);
int tapeBackgroundColor = getBackgroundColor(line);
tapeLine.setBackgroundColor(tapeBackgroundColor);
addColumnViews(tapeLine, line, tapeLineTextSizeInfo);
tapeLine.setTag(R.id.scrollingtapeadapter_viewtag_position, position);
tapeLine.setOnLongClickListener(longClickListener);
tapeLine.setOnClickListener(remainClickListener);
return tapeLine;
列表视图这种奇怪行为的背景是什么? 我在 Android 源代码中做了一些调试和研究。 当 android 更新一个视图时,有两个重要的步骤 onMeasure 和 onLayout。 ListAdapter 的 getView 方法不仅被调用来绘制视图,而且在 onMeasure 期间也更早地被调用。在后一种情况下,视图已创建,但尚未在事件链中注册以处理点击事件。
当一个为onMeasure创建的视图在以后被重用以精确地绘制到屏幕上时,它必须从Android系统中注册来处理点击事件。 对于这种特殊情况,Android 开发人员做了一些事情,这可以被认为是一个肮脏的 hack。 LayoutParams 中的一个特殊标志用于决定必须在事件更改中注册视图。
现在我的问题是:通过在重用视图时也重置 LayoutParams,这个标志总是被重置。因此Android系统不会注册视图,事件也不会通过。
总结一下:在 ListAdapter 的 getView 中重用视图时,不要覆盖 LayoutParams,因为它们保留了 android 系统的内部信息。
【讨论】:
我遇到了同样的问题。在我的情况下,布局必须根据列表的数据源调整大小,所以当视图第一次膨胀时,我做了我的 layout.params 更改,并且对行项目的点击再次正常工作。 OM*G,不可能,这就是给我的!那太倒退了,晦涩难懂,彻头彻尾的脑死亡。 API 是一种合同,不遵守此合同。我已经为此浪费了几个小时,不要让那个 Google 开发人员靠近我。谢谢你没有让我为此发疯。【参考方案2】:我遇到了同样的问题,但在我的情况下,解决方案不是保留对视图的引用,这会导致 ListView 的视图缓存出现问题。使用 converView 正确实现 getView()
方法后,所有丢失/意外点击调用的奇怪行为都消失了。
【讨论】:
在处理列表时,我总是在代码中使用处理程序模式 +1 对我有用。我保留了对我的一个视图的引用(然后用于单个ListView
行),原因是我不会进入这里 - 由于某种原因,这会延迟对该项目的所有点击,直到活动 重新启动,它们将全部流过并导致大量异常。在适配器内创建视图解决了这个问题 - 很奇怪【参考方案3】:
如果有人想知道我是如何解决这个问题的。基本上我不得不简化我的布局。似乎当您有复杂的嵌套结构时,事件可能需要很长时间才能冒泡,如果您同时开始滚动列表,事件可能会触发错误的操作。我通过尽可能切换到 RelativeLayout 来修剪布局,这似乎有很大帮助
【讨论】:
【参考方案4】:对我有用的是将OnItemClickListener
分配给ListView
到setOnItemClickListener
,而不是OnClickListener
分配给各个列表项。显然按钮仍然需要自己的OnClickListener
,但我还没有测试过那种场景。
【讨论】:
【参考方案5】:不确定这是否对任何人有帮助,但我在使用 TableLayout 时遇到了类似的问题。上述解决方案没有解决我的问题。
对我来说,问题是:
android:animateLayoutChanges="true"
删除它允许我的 TableLayout 行中的按钮单击正常工作。然后我必须手动为我的视图设置动画,而不是依赖上述属性。
【讨论】:
【参考方案6】:您似乎正在事件线程中执行一些阻塞进程(如调用 Web 服务或打开文件),因此您的事件线程被阻塞。如果是这种情况,请将您的阻塞代码处理到 Event THread 之外的另一个线程中。
【讨论】:
我使用 Loaders 特别是 AsyncLoader 来完成我所有的后台进程。但我会重新评估代码,谢谢你的提示! 毕竟不是原因。看我的回答以上是关于Android - ListView 中的延迟点击的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android 的 ListView 中延迟加载图像
android 中的appwidget中加了listview,怎么让点击listview中的item后跳转到app主页面
在android(初级)的Listview上延迟加载图像? [复制]
可点击元素的 ListView 中的 Android LongClick