9

ArrayAdapter が設定された ListView があり、アダプターの各行には、クリック イベントを受け取って処理する必要がある 4 つのボタンが含まれています。通常、Android ではSetOnClickListenerセルを作成するときに各ボタンを呼び出しますが、Mono では代わりにイベントのイベント ハンドラーを設定できますClick。ただし、イベント ハンドラーを設定する場所に応じて 2 つの問題のいずれかに遭遇するため、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));
}

ここでは、イベント ハンドラーはボタンごとに 1 回しか設定されませんが (良いことです!)、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));

このメソッドは正しい に対してクリック イベントを発生させますがposition、行がリサイクルされるたびに新しいイベント ハンドラーを設定します。これにより、多数の行に対して同時にクリック イベントが発生します。このメソッドの回避策は、イベント ハンドラーへの参照を保持し、それらを 内GetViewで再度設定する前に削除することですが、これは非常に洗練されていないようです。

Monodroid の ListView アイテム内でクリック イベントを処理するためのより良い方法はありますか?

4

3 に答える 3

1

古いスレッドであることは知っていますが、多くの投票があり、まだ未回答としてマークされています

あなたが説明したシナリオが起こるのは論理的です! モノとは一切関係ありません。これは、convertview に関係しています。これは、convertview に関するドキュメントの内容です。

convertView - 可能であれば、再利用する古いビュー。注: 使用する前に、このビューが null ではなく、適切な型であることを確認する必要があります。このビューを変換して正しいデータを表示できない場合は、このメソッドで新しいビューを作成できます。

例 1: 最初の例では、convertView は最初は null になり、イベントが設定されます。次にGetViewメソッドが呼び出されたときに、convertview が null である場合とそうでない場合があります。null でない場合は、イベントが添付されたままの古いビューが使用されます。したがって、これは、位置パラメーターを持つイベントがまだ前のビューからのものであることを意味します!

例 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/ を参照してください。

于 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 に答える