Andre Andre - 3 months ago 54
Android Question

Android ListView clickable with AlertDialog

I'm attempting to make an store page clickable. I'm using a custom listview that shows a AlertDialog about the item clicked.

My goal is, when I click on the button, open an alert dialog that allows the purchase.

When I scroll down all the list, many alertDialog are shown, instead of only one!

My custom adapter extends from BaseAdapter, and my getView() method looks like this:

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

if(row == null)
{
var inflater = context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;
row = inflater.Inflate(Resource.Layout.SingleRowStorePage, parent, false);

holder = new ViewHolder(row);
row.Tag = holder;

} else
{
holder = (ViewHolder)row.Tag;
}

//SET DATA
holder.Qtd.Text = "Qtd:" + quantities[position];
holder.Stock.Text = "Stock: " + shops[position].Stock.ToString();
holder.Name.Text = shops[position].Name;
holder.Creditos.Text = shops[position].Price.ToString() + " créditos ";

//CLICK
holder.Image.Click += delegate
{
Android.Support.V4.App.FragmentTransaction trans = manager.BeginTransaction();
DialogShop dialog = new DialogShop(shops[position]);
dialog.Show(trans, "image");
};

//confirm shop?
holder.Comprar.Click += delegate
{
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.SetTitle("Confirmar compra? item " + shops[position].Name + " com valor: " + quantities[position]);
alert.SetMessage("Lorem ipsum dolor sit amet, consectetuer adipiscing elit.");
alert.SetPositiveButton("Ok", (senderAlert, args) =>
{
//Toast.MakeText(context, "Ok!", ToastLength.Short).Show();
});

alert.SetNegativeButton("Cancelar", (senderAlert, args) =>
{
//Toast.MakeText(context, "Cancelar!", ToastLength.Short).Show();
});

Dialog dialog = alert.Create();
dialog.Show();
dialog.Dismiss();
};

return row;
}


My ViewHolder:

private class ViewHolder : Java.Lang.Object
{
public ImageView Image { get; set; }
public Button Qtd { get; set; }
public TextView Stock { get; set; }
public TextView Name { get; set; }
public TextView Creditos { get; set; }
public Button Comprar { get; set; }

public ViewHolder(View view)
{
Image = view.FindViewById<ImageView>(Resource.Id.singleRowStore);
Qtd = view.FindViewById<Button>(Resource.Id.buttonComprar);
Stock = view.FindViewById<TextView>(Resource.Id.stockStore);
Name = view.FindViewById<TextView>(Resource.Id.nomeStore);
Creditos = view.FindViewById<TextView>(Resource.Id.creditosStore);
Comprar = view.FindViewById<Button>(Resource.Id.comprarStore);
}
}


enter image description here

Answer

The problem is that you are using a delegate to hook up your events and since cells can be recycled, you are appending additional Click Events every time a cell is reused.

In your adapter instead of this:

holder.Image.Click += delegate
{
    Android.Support.V4.App.FragmentTransaction trans = manager.BeginTransaction();
    DialogShop dialog = new DialogShop(shops[position]);
    dialog.Show(trans, "image");
};

Do this instead for all your handlers:

holder.Image.Click -= ImageClicked;
holder.Image.Click += ImageClicked;

private void ImageClicked (object sender, EventArgs e)
{
    Android.Support.V4.App.FragmentTransaction trans = manager.BeginTransaction();
    DialogShop dialog = new DialogShop(shops[position]);
    dialog.Show(trans, "image");
}

You should avoid delegates or anonymous lambdas when possible since you can't get back a reference to the original method and unhook it. (Without keeping a reference to the lamdba with more code).

private class ViewHolder : Java.Lang.Object
        {
            //public EventHandler<>
            public ImageView Image { get; set; }
            public Button Qtd { get; set; }
            public TextView Stock { get; set; }
            public TextView Name { get; set; }
            public TextView Creditos { get; set; }
            public Button Comprar { get; set; }

            //New Event Handler
            public EventHandler<ImageClickedEventArgs> ImageClicked;

            public ViewHolder(View view)
            {
                Image = view.FindViewById<ImageView>(Resource.Id.singleRowStore);
                Qtd = view.FindViewById<Button>(Resource.Id.buttonComprar);
                Stock = view.FindViewById<TextView>(Resource.Id.stockStore);
                Name = view.FindViewById<TextView>(Resource.Id.nomeStore);
                Creditos = view.FindViewById<TextView>(Resource.Id.creditosStore);
                Comprar = view.FindViewById<Button>(Resource.Id.comprarStore);

                Image.Click += Image_Click;
            }

            void Image_Click(object sender, EventArgs e)
            {
                ImageClicked?.Invoke(sender, new ImageClickedEventArgs(this));
            }

            public class ImageClickedEventArgs : EventArgs
            {
                public ViewHolder ViewHolder { get; private set; }

                public ImageClickedEventArgs(ViewHolder viewHolder) : base()
                {
                    ViewHolder = viewHolder;
                }
            }
        }
Comments