Monodroid - 处理重用 ListView 行的子视图上的事件
Posted
技术标签:
【中文标题】Monodroid - 处理重用 ListView 行的子视图上的事件【英文标题】:Monodroid - Handling events on child Views of reused ListView rows 【发布时间】:2015-04-16 12:58:39 【问题描述】:android 的 ListView 会重复使用已滚动到视图之外的行。 但是,在 C# 中处理行的子视图上的事件时,这似乎是一个问题。
在 Java 中添加事件处理程序的一种公认方法是显式设置一个处理程序,如下所示:
ImageView img = (ImageView) row.findViewById(R.id.pic);
img.setOnClickListener(new View.OnClickListener()
public void onClick(View v)
System.out.println(position);
);
Xamarin 网站上的文档鼓励开发人员使用 C# 的 add 事件侦听器模式,该模式不适合重用的行:
ImageView img = row.FindViewById<ImageView> (Resource.Id.pic);
img.Click += (sender, e) =>
Console.WriteLine(position);
;
设置事件处理程序的 Java 模式非常适合行重用,而其下方添加事件处理程序的 C# 模式会导致处理程序堆积在子节点上重用行的视图。
下面的代码显示了我编写的自定义 BaseAdapter 中的 GetView
方法。
public override Android.Views.View GetView (int position,
View convertView, ViewGroup parent)
View row = convertView;
//TODO: solve event listener bug. (reused rows retain events).
if (row == null)
row = LayoutInflater.From (userListContext)
.Inflate (Resource.Layout.UserListUser, null, false);
ImageView profilePic = row.FindViewById<ImageView> (Resource.Id.profilePic);
//if(profilePic.Clickable) /** kill click handlers? **/
profilePic.Click += async (object sender, EventArgs e) =>
Bundle extras = new Bundle();
extras.PutString("id", UserList[position].id);
Intent intent = new Intent(userListContext, typeof(ProfileActivity));
intent.PutExtras(extras);
postListContext.StartActivity(intent);
;
return row;
问题是,当重复使用一行时,profilePic
视图仍然附加了原始的“点击”处理程序。
有没有办法 (a) 清除 profilePic.Click
或 (b) 使用带有匿名函数的 Android 的 profilePic.SetOnClickListener
Java 模式?
或者,在“点击”处理程序仍然可以访问 position
的正确值的情况下,是否有更好的模式可以使用?
【问题讨论】:
【参考方案1】:或者,在“点击”处理程序可以使用的地方是否有更好的模式? 仍然可以访问正确的位置值吗?
使用setTag/getTag
方法在ImageView
点击监听器的Click方法中获取点击行的正确位置:
profilePic.SetTag(Resource.Id.profilePic, position);
profilePic.Click += async (object sender, EventArgs e) =>
int clickedPos = (int)(((Button)sender).GetTag (Resource.Id.profilePic));
Bundle extras = new Bundle();
extras.PutString("id", UserList[clickedPos].id);
......
;
【讨论】:
谢谢!我会试一试。是否有一种解决方案可以保留对我的 GetView 函数范围内的其他内容的访问权限? @MicronXD:好的,试试吧。你想在我的 GetView 范围内访问什么? @MicronXD:我的建议也是使用ViewHolder Pattern
以获得良好的 ListView 性能,并避免再次重新创建已经创建的视图,请参阅下面的 Xamarin Android - deleting item from ListView deletes TWO items 可能更有帮助
这是正确的方法,但请记住,这会添加一个新的匿名事件处理程序,每次重用行时都无法取消注册。将委托逻辑转移到一个单独的方法中并取消注册,然后在每次调用 GetView
时重新注册。【参考方案2】:
ViewHolder 模式提及/建议的第一个 +1。你在正确的轨道上@MicronXD 但是我想鼓励你使用 ViewHolder 模式,它可以很好地利用你的行的视图重用。
接下来,您需要在您的 Activity 中创建一个方法,该方法实际上完成了启动另一个 Activity 的工作,并且这个新方法应该接受一个 int,它是具有您感兴趣的图像的对象的 ID,或者您可以传入整个对象。因此,例如,如果您从 MainActivity 实例化您的自定义适配器,那么您可以创建一个名为 public void OnThumbnailClicked(int id)
然后每次点击图片时,获取被点击对象的id,然后调用方法如下:(我用了一个客户的例子)
public override View GetView(int position, View convertView, ViewGroup parent)
CustomerHolder holder = null;
var view = convertView;
if (view == null)
view = Context.LayoutInflater.Inflate(Resource.Layout.CustomRow, null);
holder = new CustomerHolder();
holder.Name = view.FindViewById<TextView>(Resource.Id.textViewName);
holder.Email = view.FindViewById<TextView>(Resource.Id.textViewEmail);
holder.Phone = view.FindViewById<TextView>(Resource.Id.textViewPhone);
holder.Image = view.FindViewById<ImageButton>(Resource.Id.imageViewThumbail);
view.Tag = holder;
else
holder = view.Tag as CustomerHolder;
//At this point the holder holds reference to your view objects, whether they are
//recycled or created new.
//Next then you need to populate the views with the Customer info
var Customer = Customers[position];
holder.Name.Text = Customer.Name;
holder.Email.Text = CustomerHolder.Email;
holder.Phone.Text = Customer.Phone;
holder.Image.SetImageResource = (Resource.Drawable.defaulthumbnail);
holder.Image.Clickable = true;
holder.Image.Click += (o, e) =>
var myActivity = (MainActivity)Context;
myActivity.OnThumbnailclicked((Customer[position).id);
;
return view;
private class CustomerHolder : Java.Lang.Object
public TextView Name get; set;
public TextView Email get; set;
public TextView Phone get; set;
public ImageView Thumbnail get; set;
【讨论】:
以上是关于Monodroid - 处理重用 ListView 行的子视图上的事件的主要内容,如果未能解决你的问题,请参考以下文章
我的 monodroid UI 元素(按钮、TextView...)中的枚举需要一个 .Net 包