Monodroid - 处理ListAdapter行中的Click事件

时间:2013-04-25 13:11:12

标签: android android-listview mono xamarin.android xamarin

我有一个设置了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));
}

在这里,我的事件处理程序每​​个按钮只设置一次(好!),但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));

此方法确实会导致为正确的position触发click事件,但每次回收行时它都会设置一个新的事件处理程序。这会导致同时有很多行的点击事件。此方法的解决方法似乎是保留对事件处理程序的引用并在GetView内再次设置它们之前删除它们,但这看起来非常不优雅。

在Monodroid中处理ListView项目中的点击事件是否有更好的方法?

3 个答案:

答案 0 :(得分:1)

我知道它是一个旧线程,但它有很多投票,仍然标记为未答复

您描述的场景发生时,这是合乎逻辑的!它与单声道无关。它与convertview有关。这就是文档在convertview上所说的内容:

  

convertView - 如果可能,重用旧视图。注意:你应该   检查此视图是否为非null且之前是否为适当的类型   使用。如果无法转换此视图以显示   正确的数据,这种方法可以创建一个新的视图。

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

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

<强>解决方案: 此性能问题的解决方案是实现视图模式。

首先,您创建一个用于保存视图的类:

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; }
}

现在您可以在代码中使用此视图

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;
}

有关此结帐的详细信息:https://blog.xamarin.com/creating-highly-performant-smooth-scrolling-android-listviews/

答案 1 :(得分:0)

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

这是我解决问题的方法。

我保留对适配器中ListView的引用,比如说_listView。然后,在您的GetItem()方法中(不要错过position变量。它的值很神秘),请致电_listView.GetPositionForView((View)sender)以获得正确的位置。

答案 2 :(得分:0)

尝试使用此代码:

    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);
    }