2015-02-16 11 views
5

La funzione ListView di Android riutilizza le righe che sono state sfogliate. Ma sembra essere un problema quando si gestiscono eventi nelle viste secondarie di una riga in C#.Monodroid: gestione degli eventi su child Visualizzazioni delle righe di ListView riutilizzate

Un accettato modo di aggiungere gestori di eventi in Java è esplicitamente impostare un gestore in questo modo:

ImageView img = (ImageView) row.findViewById(R.id.pic); 
img.setOnClickListener(new View.OnClickListener() { 
    public void onClick(View v) { 
     System.out.println(position); 
    } 
}); 

documenti sul sito di Xamarin incoraggiare gli sviluppatori a utilizzare C# 's aggiungere evento modello ascoltatore che non lo fa gioco piacevole con le righe di riuso:

ImageView img = row.FindViewById<ImageView> (Resource.Id.pic); 
img.Click += (sender, e) => { 
    Console.WriteLine(position); 
}; 

Il modello Java sopra il quale imposta un gestore di eventi è adatto per il riutilizzo della riga mentre il modello C# sotto di esso che aggiunge un gestore di eventi fa sì che i gestori si accumulino sulle visualizzazioni figlio delle righe riutilizzate.

Il codice seguente mostra il mio metodo GetView da un BaseAdapter personalizzato che ho scritto.

public override Android.Views.View GetView (int position, 
              View convertView, ViewGroup parent) 
{ 
    View row = convertView; 

    //TODO: solve event listener bug. (reused rows retain events). 
    if (row == null) { 
     row = LayoutInflater.From (userListContext) 
       .Inflate (Resource.Layout.UserListUser, null, false); 
    } 

    ImageView profilePic = row.FindViewById<ImageView> (Resource.Id.profilePic); 

    //if(profilePic.Clickable) { /** kill click handlers? **/ } 
    profilePic.Click += async (object sender, EventArgs e) => { 
     Bundle extras = new Bundle(); 
     extras.PutString("id", UserList[position].id); 

     Intent intent = new Intent(userListContext, typeof(ProfileActivity)); 
     intent.PutExtras(extras); 
     postListContext.StartActivity(intent); 
    }; 

    return row; 
} 

Il problema è che, quando una riga viene riutilizzato, la vista profilePic ha ancora l'originale gestore "click" allegata.

C'è un modo per (a) eliminare profilePic.Click o (b) utilizzare lo schema Java di Android con funzioni anonime?

Oppure, esiste un modello migliore da utilizzare dove il gestore "clic" può ancora accedere al valore corretto di position?

risposta

3

Oppure, esiste un modello migliore da utilizzare dove il gestore "clic" può accedere ancora al valore corretto della posizione?

metodo Usa setTag/getTag per ottenere la posizione destra della riga cliccato all'interno Click metodo ImageView click ascoltatore:

profilePic.SetTag(Resource.Id.profilePic, position); 
profilePic.Click += async (object sender, EventArgs e) => { 
     int clickedPos = (int)(((Button)sender).GetTag (Resource.Id.profilePic)); 
     Bundle extras = new Bundle(); 
     extras.PutString("id", UserList[clickedPos].id); 
     ...... 
}; 
+0

Grazie! Lo proverò. C'è una soluzione che potrebbe mantenere l'accesso ad altre cose nell'ambito della mia funzione GetView? – MicronXD

+0

@MicronXD: ok provalo. Cosa vuoi accedere nell'ambito del mio GetView? –

+0

@MicronXD: anche il mio suggerimento è usare 'ViewHolder Pattern' per buone prestazioni di ListView ed evitare di ricreare di nuovo le viste che sono già state create, come segue [Xamarin Android: l'eliminazione di elementi da ListView elimina DUE elementi] (http://quabr.com/28474874/xamarin-android-delete-item-from-listview-deletes-two-items) probabilmente più utile –

1

Prima +1 per il modello ViewHolder menzione/suggerimento. Sei sulla strada giusta @MicronXD comunque voglio incoraggiarti ad usare il pattern ViewHolder, esso fa un buon utilizzo dei riutilizzo delle viste delle tue righe.

Successivamente è necessario creare un metodo nell'Attività che svolga effettivamente il compito di avviare un'altra attività, e questo nuovo metodo dovrebbe accettare un int che è l'ID dell'oggetto a cui è interessata l'immagine o può passare in tutto l'oggetto. Ad esempio, se stai istanziando il tuo adattatore personalizzato da MainActivity, puoi creare un metodo chiamato public void OnThumbnailClicked (int id)

Quindi ogni volta che un'immagine viene cliccata, ottieni l'id dell'oggetto che è stato cliccato e chiama il metodo in questo modo: (Ho utilizzato un esempio cliente)

public override View GetView(int position, View convertView, ViewGroup parent) 
     { 
      CustomerHolder holder = null; 
      var view = convertView; 

      if (view == null) 
      { 
       view = Context.LayoutInflater.Inflate(Resource.Layout.CustomRow, null); 

       holder = new CustomerHolder(); 
       holder.Name = view.FindViewById<TextView>(Resource.Id.textViewName); 
       holder.Email = view.FindViewById<TextView>(Resource.Id.textViewEmail); 
       holder.Phone = view.FindViewById<TextView>(Resource.Id.textViewPhone); 
       holder.Image = view.FindViewById<ImageButton>(Resource.Id.imageViewThumbail); 

       view.Tag = holder; 
      } 
      else 
      { 
        holder = view.Tag as CustomerHolder; 
      } 

      //At this point the holder holds reference to your view objects, whether they are 
      //recycled or created new. 
      //Next then you need to populate the views with the Customer info 

      var Customer = Customers[position]; 
      holder.Name.Text = Customer.Name; 
      holder.Email.Text = CustomerHolder.Email; 
      holder.Phone.Text = Customer.Phone; 
      holder.Image.SetImageResource = (Resource.Drawable.defaulthumbnail); 
      holder.Image.Clickable = true; 
      holder.Image.Click += (o, e) => 
      { 
        var myActivity = (MainActivity)Context; 
        myActivity.OnThumbnailclicked((Customer[position).id); 

      }; 
      return view; 
     } 

     private class CustomerHolder : Java.Lang.Object 
     { 
      public TextView Name { get; set; } 
      public TextView Email { get; set; } 
      public TextView Phone { get; set; } 
      public ImageView Thumbnail { get; set; }     
     }   

}