2013-04-25 14 views
9

Mam ListView z ustawionym ArrayAdapter, a każdy wiersz w adapterze zawiera cztery przyciski, które muszą odbierać i obsługiwać zdarzenia kliknięcia. Zwykle w Androidzie nazywałbym się SetOnClickListener na każdym przycisku podczas tworzenia komórki, ale Mono daje możliwość ustawienia programów obsługi zdarzeń dla zdarzenia Click. Wygląda na to, że w Mono jest trochę dziwności, ponieważ napotykam na jedną z dwóch kwestii w zależności od tego, gdzie ustawiam moduł obsługi zdarzeń.Monodroid - Obsługa zdarzeń kliknięcia w wierszach ListAdapter

ArrayAdapter GetView Przykład 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)); 
} 

Tutaj mój obsługi zdarzeń tylko zostanie ustawiony raz na przycisk (dobre!), Ale wartość position jest źle przez większość czasu. Zastanawiam się, czy konwersja z Mono na kod Androida powoduje, że GetItem(position) używa tej samej wartości dla position za każdym razem (wartość, która position jest ustawiona na kiedy komórka jest najpierw tworzona). Ten kod działa zupełnie normalnie w normalnym systemie Android.

ArrayAdapter GetView Przykład 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)); 

Metoda ta powoduje zdarzenia kliknięcie do wypalania dla prawidłowego position, ale tworzy nowy obsługi zdarzeń przy każdym rzędzie jest przywracana. Powoduje to zdarzenia kliknięcia dla wielu wierszy w tym samym czasie. Wydaje się, że obejście tej metody zachowuje odniesienia do procedur obsługi zdarzeń i usuwa je przed ponownym ustawieniem w numerze GetView, ale wydaje się to wyjątkowo nieeleganckie.

Czy istnieje lepsza metoda obsługi zdarzeń kliknięcia w elementach ListView w Monodroid?

+0

Wpadłem na ten sam problem. Sposób, w jaki to rozwiązałem, polegał na użyciu wzorca ViewHolder. Zawsze możesz przesunąć pozycję w tagu dla convertView i wyciągnąć ją w module obsługi zdarzeń kliknięcia. – snowCrabs

Odpowiedz

0

Miałem podobne problemy. Najwyraźniej uniknę rozwiązania 2. Ponadto zauważam, że problem w rozwiązaniu 1 zdarzył się tylko na Androidzie 2.3.

W ten sposób naprawiłem problem.

Utrzymuję odniesienie do ListView w adapterze, na przykład _listView. Następnie w metodzie GetItem() (Nie przegap zmiennej position, której wartość jest tajemnicą), zadzwoń pod numer _listView.GetPositionForView((View)sender), aby uzyskać poprawną pozycję.

+0

Scenariusz Opcja 1 dzieje się dla mnie we wszystkich wersjach. Nie próbowałem jeszcze twojego rozwiązania, ale to po prostu wydaje się tak do tyłu ... nie stukając w twoje rozwiązanie, po prostu chciałbym, żeby działało poprawnie w Mono, aby ListView i adapter nie musiały być tak ściśle powiązane. – BigFwoosh

+0

Wiem, ale to jest rozwiązanie tego problemu. Naprawdę mam nadzieję, że to działa poprawnie również w Mono dla Androida. Ponadto, nie sądzę, utrzymywanie odniesienia "ListView" w adapterze jest ściśle sprzężone. –

0

Spróbuj z tym kodem:

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); 
    } 
1

Wiem, że stary wątek, ale ma dużo głosów i nadal jest oznaczony jako nieodebranych

Jest logiczne, że scenariusze, które opisałeś, co się dzieje! Nie ma to nic wspólnego z mono. Ma to związek z konwersją. Oto, co mówi dokumentacja w sprawie konwersji:

convertView - stary widok do ponownego wykorzystania, jeśli to możliwe. Uwaga: Należy sprawdzić, czy ten widok ma wartość inną niż null i odpowiedniego typu przed użyciem przez . Jeśli nie można przekonwertować tego widoku, aby wyświetlić prawidłowe dane, ta metoda może utworzyć nowy widok.

Przykład 1: W pierwszym przykładzie convertView będzie zerowy po raz pierwszy i będzie ustawiony zdarzenia.Następnym razem, gdy metoda o nazwie "convertview" będzie nazywana konwersją, może być lub nie być pusta. Jeśli nie ma wartości null, użyje starego widoku, a zdarzenia nadal będą dołączone! Oznacza to, że zdarzenie z parametrem pozycji jest nadal z poprzedniego widoku!

Przykład 2: Ten przykład będzie działać zgodnie z oczekiwaniami, ale nie jest bardzo wydajny jak wspomniałeś. Musi zlokalizować kontrolki za każdym razem, gdy wywoływana jest metoda FindViewById.

Rozwiązanie: Rozwiązaniem tego problemu z wydajnością jest implementacja wzorca widoku.

Najpierw trzeba utworzyć klasę, która będzie trzymać swoje poglądy:

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

Teraz można użyć tej viewholder w kodzie

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

Aby uzyskać więcej informacji na ten temat sprawdź: https://blog.xamarin.com/creating-highly-performant-smooth-scrolling-android-listviews/

Powiązane problemy