9

我有一个设置了 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)使用相同的值(首次创建单元格时设置的值)。此代码在普通 Android 中完全可以正常工作。positionposition

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 事件position,但它会在每次回收该行时设置一个新的事件处理程序。这会同时导致很多行的点击事件。此方法的解决方法似乎是保留对事件处理程序的引用并在再次将它们设置在内部之前将其删除GetView,但这似乎非常不雅。

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

4

3 回答 3

1

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

您描述的场景发生是合乎逻辑的!它与单声道无关。它与转换视图有关。这是 convertview 文档中所说的:

convertView - 如果可能的话,要重用的旧视图。注意:在使用之前,您应该检查此视图是否为非空且类型合适。如果无法将此视图转换为显示正确的数据,则此方法可以创建一个新视图。

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

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

有关此检查的更多信息:https ://blog.xamarin.com/creating-highly-performant-smooth-scrolling-android-listviews/

于 2016-04-05T07:12:56.813 回答
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);
    }
于 2014-10-02T09:26:00.340 回答
0

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

这就是我解决问题的方法。

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

于 2013-04-25T19:01:34.953 回答