Monodroid - 处理ListAdapter行内的Click事件
我有一个ListView,并为其设置了一个ArrayAdapter,并且适配器中的每一行都包含需要接收和处理点击事件的四个按钮。通常在Android中,我会在创建单元格时在每个按钮上调用SetOnClickListener
,但是Mono可以为事件Click
事件设置事件处理程序。看起来在Mono中有一些奇怪的地方,因为我遇到了两个问题之一,这取决于我设置事件处理程序的位置。Monodroid - 处理ListAdapter行内的Click事件
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
值大部分时间是错误的。我想知道从单声道到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
被解雇的单击事件,但它的每行循环时间设置一个新的事件处理程序。这会导致大量行的点击事件同时发生。此方法的解决方法似乎是保持对事件处理程序的引用,并在将它们再次设置到GetView
之前将其删除,但这看起来非常不雅观。
是否有更好的方法来处理Monodroid中的ListView项目内的点击事件?
我也有类似的问题。显然,我会避免解决方案2.此外,我观察到解决方案1中的问题只发生在Android 2.3上。
这是我如何解决这个问题。
我在适配器中保留对ListView
的引用,如_listView
。然后,在你的GetItem()
方法中(不要错过position
变量,它的值是一个谜),请拨打_listView.GetPositionForView((View)sender)
以获得正确的位置。
在所有版本中,选项1的情况正在发生。我还没有尝试过你的解决办法,但只是似乎如此倒退...不敲你的解决方案在所有的,我只是希望它在单工作正常,这样的ListView和适配器没必要如此紧密耦合。 – BigFwoosh 2013-04-29 13:10:46
我知道,但这是解决问题的办法。我确实希望这适用于Android的Mono。另外,我不认为在适配器中保留“ListView”的引用是紧密耦合的。 – 2013-04-29 18:07:14
尝试使用此代码:
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);
}
我知道它的一个古老的线程,但它有很多票,并且仍被标记为解答
这是合乎逻辑的一些情形中你描述的发生!它与单声道无关。它与convertview有关。这是文档在convertview上所说的内容:
convertView - 如果可能,重新使用旧视图。注意:您应该在 使用前检查此视图是否为非空以及适当类型。如果无法将此视图转换为显示 正确的数据,则此方法可以创建新视图。
例1: 在你的第一个例子中,convertView将为空在第一时间和事件将被设置。然后在下一次GetView方法被称为convertview可以或可以不为空。如果它不为空,它将使用旧视图并附加事件!所以这意味着带有位置参数的事件仍然来自之前的视图!
示例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;
}
使用viewholder欲了解更多相关信息,请上网:https://blog.xamarin.com/creating-highly-performant-smooth-scrolling-android-listviews/
我遇到了同样的问题。我解决它的方式是使用ViewHolder模式。您可以随时在convertView的标签中推入位置,并在click事件处理程序中将其拉出。 – snowCrabs 2013-04-26 15:48:30