Android Monodroid-处理ListAdapter行内的单击事件

Android Monodroid-处理ListAdapter行内的单击事件,android,android-listview,mono,xamarin.android,xamarin,Android,Android Listview,Mono,Xamarin.android,Xamarin,我有一个设置了ArrayAdapter的ListView,适配器中的每一行都包含四个按钮,需要接收和处理单击事件。通常在Android中,当我创建单元格时,我会在每个按钮上调用SetOnClickListener,但Mono提供了为Click事件设置事件处理程序的功能。在Mono中,这看起来有些奇怪,因为我遇到了两个问题之一,这取决于我在哪里设置事件处理程序 ArrayAdapter GetView示例1: View tweetCell = convertView; if (tweetCell

我有一个设置了ArrayAdapter的ListView,适配器中的每一行都包含四个按钮,需要接收和处理单击事件。通常在Android中,当我创建单元格时,我会在每个按钮上调用
SetOnClickListener
,但Mono提供了为
Click
事件设置事件处理程序的功能。在Mono中,这看起来有些奇怪,因为我遇到了两个问题之一,这取决于我在哪里设置事件处理程序

ArrayAdapter GetView示例1:

View tweetCell = convertView;
if (tweetCell == null) {
    tweetCell = ((LayoutInflater)Context.GetSystemService (Context.LayoutInflaterService)).Inflate (Resource.Layout.TweetCell, null);

    tweetCell.FindViewById (Resource.Id.btn_moveTweet).Click += (object sender, EventArgs e) => MoveTweet (GetItem(position));

    tweetCell.FindViewById (Resource.Id.btn_unfavoriteTweet).Click += (object sender, EventArgs e) => UnfavoriteTweet (GetItem(position));

    tweetCell.FindViewById (Resource.Id.btn_hideTweet).Click += (object sender, EventArgs e) => HideTweet (GetItem(position));

    tweetCell.FindViewById (Resource.Id.btn_shareTweet).Click += (object sender, EventArgs e) => ShareTweet (GetItem(position));
}
在这里,我的事件处理程序每个按钮只设置一次(很好!),但是
位置
值在大多数情况下都是错误的。我想知道从Mono到Android代码的转换是否会导致
GetItem(position)
每次对
position
使用相同的值(第一次创建单元格时设置了
position
的值)。这段代码在普通的Android中完全可以正常工作

ArrayAdapter GetView示例2:

View tweetCell = convertView;
if (tweetCell == null) {
    tweetCell = ((LayoutInflater)Context.GetSystemService (Context.LayoutInflaterService)).Inflate (Resource.Layout.TweetCell, null);
}
tweetCell.FindViewById (Resource.Id.btn_moveTweet).Click += (object sender, EventArgs e) => MoveTweet (GetItem(position));

tweetCell.FindViewById (Resource.Id.btn_unfavoriteTweet).Click += (object sender, EventArgs e) => UnfavoriteTweet (GetItem(position));

tweetCell.FindViewById (Resource.Id.btn_hideTweet).Click += (object sender, EventArgs e) => HideTweet (GetItem(position));

tweetCell.FindViewById (Resource.Id.btn_shareTweet).Click += (object sender, EventArgs e) => ShareTweet (GetItem(position));
此方法确实会导致为正确的
位置触发click事件,但它会在每次循环行时设置一个新的事件处理程序。这会导致多行同时发生单击事件。此方法的一个解决方法似乎是保留对事件处理程序的引用,并在
GetView
中再次设置它们之前删除它们,但这似乎非常不雅观


有没有更好的方法来处理Monodroid中ListView项目内的单击事件?

我也有类似的问题。显然,我将避免解决方案2。此外,我观察到解决方案1中的问题只发生在Android 2.3上

这就是我解决问题的方法

我在适配器中保留了对
列表视图的引用,比如
\u ListView
。然后,在您的
GetItem()
方法中(不要错过
position
变量。它的值是一个谜),调用
\u listView.GetPositionForView((View)sender)
以获得正确的位置。

尝试使用以下代码:

    void OnListItemClick(object sender, AdapterView.ItemClickEventArgs e)
    {
        var listView = sender as ListView;
        var item = items[e.Position];

        //init your data...

        tweetCell.FindViewById (Resource.Id.btn_moveTweet).Click += (object sender, EventArgs e) => MoveTweet (item);
    }
我知道这是一个古老的线索,但它有很多选票,仍然被标记为未答复

您描述的场景发生是合乎逻辑的!这和mono没有任何关系。这与convertview有关。以下是文档在convertview上的说明:

convertView—如果可能,要重用的旧视图。注意:你应该 检查此视图是否为非null,以及是否为适当的类型 使用。如果无法将此视图转换为显示 如果数据正确,此方法可以创建新视图

示例1: 在第一个示例中,convertView第一次将为null,并且将设置事件。然后下次调用GetView方法时,convertview可能为空,也可能不为空。如果它不为null,它将使用旧视图,并且事件仍然附加!因此,这意味着带有position参数的事件仍然来自上一个视图

示例2: 这个示例将按预期工作,但不像您提到的那样效率很高。每次调用FindViewById方法时,它都必须找到控件

解决方案: 此性能问题的解决方案是实现viewholder模式

首先,创建一个将保存视图的类:

private class MyViewHolder : Java.Lang.Object 
{
  public Button MoveTweet { get; set; }
  public Button ShareTweet { get; set; }
  public Button UnfavoriteTweet { get; set; }
  public Button HideTweet { get; set; }
}
现在,您可以在代码中使用此viewholder

public override View GetView (int position, View convertView, ViewGroup parent)
{
  MyViewHolder holder;
  var view = convertView;

  if(view != null) 
    holder = view.Tag as MyViewHolder;


  if (holder == null) {
    holder = new MyViewHolder ();
    view = activity.LayoutInflater.Inflate (Resource.Layout.OptimizedItem, null);
    holder.MoveTweet = view.FindViewById<Button> (Resource.Id. btn_moveTweet);
    holder.ShareTweet = view.FindViewById<Button> (Resource.Id. btn_shareTweet);
    holder.UnfavoriteTweet = view.FindViewById<Button> (Resource.Id. btn_unfavoriteTweetTweet);
    holder.HideTweet = view.FindViewById<Button> (Resource.Id. btn_hideTweet);
    view.Tag = holder;
  } 


  holder.MoveTweet.Click += (object sender, EventArgs e) => MoveTweet (GetItem(position));
  holder.UnfavoriteTweet.Click += (object sender, EventArgs e) => UnfavoriteTweet (GetItem(position))
  holder.HideTweet.Click += (object sender, EventArgs e) => HideTweet (GetItem(position));
  holder.FavoriteTweet.Click += (object sender, EventArgs e) => ShareTweet (GetItem(position));

  return view;
}
public override View GetView(int位置、视图转换视图、视图组父视图)
{
我的视窗持有人;
var-view=convertView;
如果(视图!=null)
holder=视图。标记为MyViewHolder;
if(holder==null){
holder=新的MyViewHolder();
视图=activity.LayoutInflater.Inflate(Resource.Layout.OptimizedItem,null);
holder.MoveTweet=view.findviewbyd(Resource.Id.btn_MoveTweet);
holder.ShareTweet=view.FindViewById(Resource.Id.btn\u ShareTweet);
holder.UnfavoriteTweet=view.findviewbyd(Resource.Id.btn_UnfavoriteTweet);
holder.HideTweet=view.findviewbyd(Resource.Id.btn\u HideTweet);
视图.标签=支架;
} 
holder.MoveTweet.Click+=(对象发送者,EventArgs e)=>MoveTweet(GetItem(position));
holder.UnfavoriteTweet.Click+=(对象发送者,事件参数e)=>UnfavoriteTweet(GetItem(position))
holder.hideweet.Click+=(对象发送者,事件参数)=>hideweet(GetItem(position));
holder.FavoriteTeet.Click+=(对象发送者,事件参数)=>ShareTweet(GetItem(position));
返回视图;
}

有关此问题的更多信息,请查看:

我遇到了相同的问题。我解决这个问题的方法是使用ViewHolder模式。您可以在convertView的标记中插入该位置,然后在click事件处理程序中将其拉出。选项1场景在所有版本中都会发生。我还没有尝试过你的解决方案,但那似乎太落后了。。。我一点也不反对你的解决方案,我只是希望它能在Mono中正常工作,这样ListView和适配器就不需要如此紧密地耦合。我知道,但这就是问题的解决方案。我真的希望这在Android的Mono中也能正常工作。另外,我不认为在适配器中保留
ListView
的引用是紧密耦合的。