我正在为列表视图使用自定义列表适配器。我在列表视图中定义了一个按钮并且点击事件有效,但问题是一旦滚动列表,它将多个视图绑定到同一个按钮。因此,在单击按钮时,会触发与每个关联视图关联的事件。
我该如何处理?
我正在为列表视图使用自定义列表适配器。我在列表视图中定义了一个按钮并且点击事件有效,但问题是一旦滚动列表,它将多个视图绑定到同一个按钮。因此,在单击按钮时,会触发与每个关联视图关联的事件。
我该如何处理?
Stuart 是完全正确的 - 问题是 ListView 中的视图被重用(以避免创建不同的对象),并且由于列表的不同部分是可见的,对于一个新位置,您可以获得任何不再使用的视图。所以你的代码应该正确处理这个问题。我想补充一点,monodroid 中 Java 对象的垃圾收集效果不好。根据我的经验,创建大量从 Java.Lang.Object 派生的对象会使应用程序崩溃。所以:
为每个新行创建新视图将很快使应用程序崩溃,因此您必须尽可能重用 convertView。
Tag 具有 Java.Lang.Object 类型,因此 WrappedPosition 应该派生自 Java 对象。这意味着您应该重用相同的实例,而不是每次都创建新实例。
如果您将点击处理程序移动到单独的方法,您可以在订阅之前取消订阅,因此您不需要任何逻辑“如果视图为空”。
如果您觉得它有用,我可能会在此处发布解释其工作原理的代码示例。最初不要发布它,因为它很大:)
我猜你误解了列表的工作原理——尤其是 convertView 的使用方式。
Android 中的 ListViews 虚拟化 UI - 就像 WP 中的 ListBoxes 和 iOS 中的 UITableViews 一样
这意味着,如果底层列表有 1000 个项目,但屏幕上只有 10 个项目的空间,那么列表将只创建 10 个“容器”来显示列表项目,并使用这些容器仅显示内容在当时看来。
它这样做的方式是通过适配器 - 特别是通过 GetView 回调 - 它将 convertView 作为其参数之一。
如果您选择在 GetView 实现中创建新视图,则可以在回调中订阅新事件...
如果您选择在 GetView 实现中使用 convertView,那么您不应该在回调中订阅新事件 - 除非先取消订阅旧事件。
例如,我猜你的代码目前正在执行类似这样的伪操作:
public View GetView(int pos, View convertView)
{
TextView toShow = convertView as TextView;
if (toShow == null)
{
toShow = new TextView();
}
toShow.Text = "Item at position " + i;
toShow.Click += (s,e) => {
// do something
};
return toShow;
}
代码的问题是您会经常订阅 Click ...您需要通过以下方式解决它:
public View GetView(int pos, View convertView)
{
TextView toShow = convertView as TextView;
if (toShow == null)
{
toShow = new TextView();
toShow.Click += (s,e) => {
// do something with the position embedded in toShow.Tag
};
}
toShow.Text = "Item at position " + i;
toShow.Tag = new WrappedPosition(i);
return toShow;
}
反正这是我的猜测:)