Android - 在ListView中延迟点击次数

时间:2012-01-11 06:27:38

标签: android onclick android-listview android-fragments android-viewpager

我的应用程序中有以下结构:

FragmentActivity ViewPager使用带有Android 2.1的compativility pack保存FragmentStatePagerAdapter管理的多个片段

每个片段都包含ListViewListView中的每个元素都有一个LinearLayout,其中包含两个TextViews和一个ButtonLinearLayout和按钮有onClickListeners(单独)。点击LinearLayout即可启动另一个Activity。我注意到点击行为是非常不一致的:有时候动作会立即执行但很多时候它会被延迟,有时它会被忽略,无论我点击多少次。它甚至更奇怪,因为我可以点击,只有当我开始滚动列表时才会执行操作。我尝试了setFocusable(false)setSelectable(true)的各种组合,但似乎没有任何区别。有任何想法吗?我很乐意提供更多细节。

6 个答案:

答案 0 :(得分:9)

我遇到了类似的问题,我花了2天的时间来调试并解决它。 我有一个ListAdapter,它为每个列表项在LinearLayout中创建几个TextView。 每个TextView都有自己的OnClickListener,因为我需要处理每个项目的点击。

当我更改实现以便重用View时,OnClickListener停止正常工作。在4.4.2上,大多数点击都有效,但有时直到我在列表中滚动才会有反应。在2.3上,第一次点击不起作用,然后是爆发处理的所有点击。

在我的特殊情况下,我创建了所有View in 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开发者已经做了一些事情,这可能被认为是一个肮脏的黑客。 LayoutParams中的特殊标志用于确定必须在事件更改中注册视图。

现在我的问题是:通过在重用视图时重置LayoutParams,此标志始终被重置。因此,Android系统不会注册视图,事件也不会通过。

总结一下:当在ListAdapter的getView中重用视图时,不要覆盖LayoutParams,因为它们保留了android系统的内部信息。

答案 1 :(得分:4)

我遇到了同样的问题,但在我的情况下,解决方案不是保持对视图的引用,这导致了ListView的视图缓存问题。在使用converView正确实现getView()方法后,丢失/意外点击调用的所有奇怪行为都消失了。

答案 2 :(得分:3)

如果有人想知道我是如何解决这个问题的。基本上我必须简化我的布局。似乎当你有复杂的嵌套结构时,事件可能需要很长时间才能冒泡,如果你同时开始滚动列表事件可能会触发错误的操作。我通过尽可能多地切换到RelativeLayout来修剪布局,这似乎有很多帮助

答案 3 :(得分:1)

对我有用的是将OnItemClickListener分配给ListViewsetOnItemClickListener,而不是OnClickListener分配给各个列表项。显然按钮仍然需要自己的OnClickListener,但我没有测试过这种情况。

答案 4 :(得分:1)

不确定这是否对任何人都有帮助,但我有一个类似的问题而不是TableLayout。上述解决方案并没有解决我的问题。

对我来说问题是: android:animateLayoutChanges="true"

删除此项允许我在TableLayout行中单击按钮才能正常工作。我必须手动设置我的视图动画,而不是依赖上面的属性。

答案 5 :(得分:-1)

您似乎正在事件线程中执行一些阻塞过程(如调用Web服务或打开文件),因此您的事件线程被阻止。如果是这种情况,请将阻止代码处理到另一个线程而不是Event THread。