我有一个设置了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项目中的点击事件是否有更好的方法?
答案 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);
}