【问题标题】:Android (Xamarin) ListView position issueAndroid(Xamarin)ListView位置问题
【发布时间】:2017-07-14 15:02:51
【问题描述】:

我正在开发一个 ListView,它在屏幕之外填充数据(需要滚动)并面临数据重复的问题。通过一些研究,我实现了一个 ViewHolder,解决了重复数据的问题。但是,ListView 中的某些元素不适用于数据 - 而是定位 - 结果,每次我滚动视图并自行回收时,这些元素仍然会变得混乱。下面是 ListView 适配器和 ViewHolder 代码:

public class ListAdapter : BaseAdapter
   {
       private readonly Activity activity;

       public ListAdapter(Activity a)
       {
           activity = a;
       }

       public override int Count
       {
           get { return nameTexts.Length; }
       }

       public override Java.Lang.Object GetItem(int position)
       {
           return null;
       }

       public override long GetItemId(int position)
       {
           return 0;
       }

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

           if (view == null)
           {
               holder = new ViewHolder();

               view = activity.LayoutInflater.Inflate(Resource.Layout.Hospital_List_Item, parent, false);
               holder.hospitalName = view.FindViewById<TextView>(Resource.Id.selectHospital_HospitalText);

               view.Click += delegate
               {
                   Intent intent = new Intent(activity, typeof(Request_Appointment));
                   intent.PutExtra("hospitalName", nameTexts[position]);
                   activity.StartActivity(intent);
               };

               //  Set alternatiing background of row
               if (position % 2 == 1)
               {
                   view.SetBackgroundResource(Resource.Color._8_white);
               }

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

           //  Set height of row
           view.LayoutParameters.Height = Select_Hospital.LIST_HEIGHT / 6;

           //  Row contents
           holder.hospitalName.Text = nameTexts[position];
           System.Diagnostics.Debug.WriteLine(position);



           return view;
       }

       private readonly string[] nameTexts =
       {
           "Hospital 1",
           "Hospital 2",
           "Hospital 3",
           "Hospital 4",
           "Hospital 5",
           "Hospital 6",
           "Hospital 7",
           "Hospital 8",
           "Hospital 9",
           "Hospital 10",
           "Hospital 11",
           "Hospital 12",
           "Hospital 13",
           "Hospital 14",
           "Hospital 15",
           "Hospital 16",
           "Hospital 17",
           "Hospital 18",
           "Hospital 19",
           "Hospital 20",
           "Hospital 21",
           "Hospital 22",
           "Hospital 23",
           "Hospital 24",
           "Hospital 25"
       };
   }

   //  ViewHolder
   class ViewHolder : Java.Lang.Object
   {
       public TextView hospitalName { get; set; }
       public ToggleButton hospitalFavourites { get; set; }
   }

Hospital_List_Item axml 代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView
        android:id="@+id/selectHospital_HospitalText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dp"
        android:layout_centerVertical="true"
        android:textColor="@color/dark_blue" />
    <ToggleButton
        android:id="@+id/selectHospital_FavouritesToggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="25dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true" />
</RelativeLayout>

初始屏幕:

滚动后:

注意:点击任意行项会显示点击行的位置,滚动后返回错误值。

不按预期工作的元素是交替背景以及点击事件。我尝试将它们移到 if (view == null) 块之外,但这使它变得更糟。非常感谢任何帮助!

 

-编辑-

更新代码:

public class ListAdapter : BaseAdapter
{
    private readonly Activity activity;

    public ListAdapter(Activity a)
    {
        activity = a;
    }

    public override int Count
    {
        get { return nameTexts.Length; }
    }

    public override Java.Lang.Object GetItem(int position)
    {
        return null;
    }

    public override long GetItemId(int position)
    {
        return 0;
    }

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        ViewHolder holder;
        View view = convertView;
        int type;

        if (view == null)
        {
            holder = new ViewHolder();

            view = activity.LayoutInflater.Inflate(Resource.Layout.Hospital_List_Item, parent, false);
            holder.hospitalName = view.FindViewById<TextView>(Resource.Id.selectHospital_HospitalText);
            holder.hospitalFavourites = view.FindViewById<ToggleButton>(Resource.Id.selectHospital_FavouritesToggle);
            holder.position = position;
            System.Diagnostics.Debug.WriteLine(holder.position);

            view.Click += delegate
            {
                Intent intent = new Intent(activity, typeof(Request_Appointment));
                intent.PutExtra("hospitalName", holder.hospitalName.Text);
                activity.StartActivity(intent);
            };

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

        //  Set height of row
        view.LayoutParameters.Height = Select_Hospital.LIST_HEIGHT / 6;

        //  Set alternating background of row
        type = GetItemViewType(holder.position);

        if (type == 0)
        {
            view.SetBackgroundResource(Resource.Color._8_white);
        }

        //  Row contents
        holder.hospitalName.Text = nameTexts[position];

        holder.hospitalFavourites.Text = position.ToString();
        holder.hospitalFavourites.SetOnCheckedChangeListener(null);
        holder.hospitalFavourites.Click += delegate
            {
                holder.hospitalFavourites.Text = position.ToString();
            };

        return view;
    }

    //  Number of different rows
    public override int ViewTypeCount
    {
        get
        {
            return 2;
        }
    }

    //  To alternate rows
    public override int GetItemViewType(int position)
    {
        return position % 2 == 1 ? 0 : 1;
    }        

    private readonly string[] nameTexts =
    {
        "Hospital 1",
        "Hospital 2",
        "Hospital 3",
        "Hospital 4",
        "Hospital 5",
        "Hospital 6",
        "Hospital 7",
        "Hospital 8",
        "Hospital 9",
        "Hospital 10",
        "Hospital 11",
        "Hospital 12",
        "Hospital 13",
        "Hospital 14",
        "Hospital 15",
        "Hospital 16",
        "Hospital 17",
        "Hospital 18",
        "Hospital 19",
        "Hospital 20",
        "Hospital 21",
        "Hospital 22",
        "Hospital 23",
        "Hospital 24",
        "Hospital 25"
    };
}

class ViewHolder : Java.Lang.Object
{
    public TextView hospitalName { get; set; }
    public ToggleButton hospitalFavourites { get; set; }
    public int position { get; set; }
}

 

-EDIT 2- (OnClick lambda)

public class RecyclerAdapter : RecyclerView.Adapter
{
    private readonly Activity activity;
    public event EventHandler<int> ItemClick;

    public RecyclerAdapter(Activity a)
    {
        activity = a;
    }

    public override int ItemCount
    {
        get
        {
            return nameTexts.Length;
        }
    }

    private void OnClick (int position)
    {
        ItemClick?.Invoke(this, position);
    }

    private void OnItemClick(object sender, int position)
    {
        int buttonNum = position + 1;
        Toast.MakeText(activity, "This is button number " + buttonNum, ToastLength.Short).Show();
    }

    public override long GetItemId(int position)
    {
        return position;
    }

    public override int GetItemViewType(int position)
    {
        return position % 2 == 1 ? 0 : 1;
    }

    public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        ViewHolder vh = holder as ViewHolder;

        //  Set height of row
        vh.ItemView.LayoutParameters.Height = Select_Hospital.LIST_HEIGHT / 6;

        //  Set alternating background of row
        int type = GetItemViewType(position);

        if (type == 0)
        {
            vh.ItemView.SetBackgroundResource(Resource.Color._8_white);
        }

        vh.hospitalName.Text = nameTexts[position];
    }

    public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {
        View view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.sublayout_Hospital_List_Item, parent, false);
        ViewHolder holder = new ViewHolder(view, OnClick);

        holder.ItemView.Click += delegate
        {
            Intent intent = new Intent(activity, typeof(Request_Appointment));
            intent.PutExtra("hospitalName", holder.hospitalName.Text);
            activity.StartActivity(intent);
        };

        ItemClick += OnItemClick;

        return holder;
    }

    private readonly string[] nameTexts =
    {
        "Hospital 1",
        "Hospital 2",
        "Hospital 3",
        "Hospital 4",
        "Hospital 5",
        "Hospital 6",
        "Hospital 7",
        "Hospital 8",
        "Hospital 9",
        "Hospital 10",
        "Hospital 11",
        "Hospital 12",
        "Hospital 13",
        "Hospital 14",
        "Hospital 15",
        "Hospital 16",
        "Hospital 17",
        "Hospital 18",
        "Hospital 19",
        "Hospital 20",
        "Hospital 21",
        "Hospital 22",
        "Hospital 23",
        "Hospital 24",
        "Hospital 25"
    };
}

class ViewHolder : RecyclerView.ViewHolder
{
    public TextView hospitalName { get; set; }
    public ToggleButton hospitalFavourites { get; set; }

    public ViewHolder (View view, Action<int> listener) : base(view)
    {
        hospitalName = view.FindViewById<TextView>(Resource.Id.selectHospital_HospitalText);
        hospitalFavourites = view.FindViewById<ToggleButton>(Resource.Id.selectHospital_FavouritesToggle);

        hospitalFavourites.Click += (sender, e) => listener(AdapterPosition);
    }
}

【问题讨论】:

  • 你应该为不同的listView项目有不同的布局
  • @AyushKhare 抱歉,您能详细说明一下吗?
  • 你有不同颜色的行,你应该实现getItemViewType(),计数为2。看看这个tutorial
  • @AyushKhare 谢谢!这解决了背景的问题。我仍然有点击方法的问题。

标签: android listview xamarin adapter


【解决方案1】:

不应该是你的getItem() 看起来像这样:

    public override Java.Lang.Object GetItem(int position){
        return nameTexts[position];
    }

【讨论】:

    【解决方案2】:
    1. HospitalFavourites ToggleButton 未在 GetView 方法中分配

    2. view.Click 委托应该从视图中检索标签,而不是访问 nameTexts[position]。您可以将位置作为属性添加到 ViewHolder 中,以便在单击列表项时访问它。

    3. 用于交替背景颜色的代码应位于 if (view == null) 块之外。

    == 已编辑 ==

    使用下面的代码来修改你想要做的事情。

    using System;
    using Android.App;
    using Android.Views;
    using Android.Widget;
    
    namespace ListAdapterSample.Droid
    {
    public class ListAdapter : BaseAdapter
    {
        private readonly Activity activity;
    
        public ListAdapter(Activity a)
        {
            activity = a;
        }
    
        public override int Count
        {
            get { return nameTexts.Length; }
        }
    
        public override Java.Lang.Object GetItem(int position)
        {
            return null;
        }
    
        public override long GetItemId(int position)
        {
            return 0;
        }
    
        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            ViewHolder holder;
            View view = convertView;
            int type;
    
            if (view == null)
            {
                holder = new ViewHolder();
    
                view = activity.LayoutInflater.Inflate(Resource.Layout.hospital_list_item, parent, false);
                holder.hospitalName = view.FindViewById<TextView>(Resource.Id.selectHospital_HospitalText);
                holder.hospitalFavourites = view.FindViewById<ToggleButton>(Resource.Id.selectHospital_FavouritesToggle);
                System.Diagnostics.Debug.WriteLine(holder.position);
    
                view.Click += delegate(object sender, EventArgs e)
                {
                    var senderView = sender as View;
                    if (senderView != null)
                    {
                        var senderHolder = senderView.Tag as ViewHolder;
                        if (senderHolder != null)
                        {
                            Toast.MakeText(activity, senderHolder.position.ToString(), ToastLength.Short).Show();
                        }
                    }
                };
    
                holder.hospitalFavourites.Click += delegate (object sender, EventArgs e)
                {
                    var senderView = sender as View;
                    if (senderView != null)
                    {
                        var rowHolder = ((View)senderView.Parent).Tag as ViewHolder;
                        rowHolder.hospitalFavourites.Text = rowHolder.position.ToString();
                    };
                };
    
                view.Tag = holder;
            }
            else
            {
                holder = view.Tag as ViewHolder;
            }
    
            //  Set height of row
            view.LayoutParameters.Height = 200;
    
            //  Set alternating background of row
            type = GetItemViewType(position);
    
            if (type == 0)
            {
                //view.SetBackgroundResource(Resource.Color._8_white);
                view.SetBackgroundColor(Android.Graphics.Color.AliceBlue);
            }
    
            //  Row contents
            holder.position = position;
            holder.hospitalName.Text = nameTexts[position];
    
            holder.hospitalFavourites.Text = position.ToString();
            holder.hospitalFavourites.SetOnCheckedChangeListener(null);
    
            return view;
        }
    
        //  Number of different rows
        public override int ViewTypeCount
        {
            get
            {
                return 2;
            }
        }
    
        //  To alternate rows
        public override int GetItemViewType(int position)
        {
            return position % 2 == 1 ? 0 : 1;
        }
    
        private readonly string[] nameTexts =
        {
        "Hospital 1",
        "Hospital 2",
        "Hospital 3",
        "Hospital 4",
        "Hospital 5",
        "Hospital 6",
        "Hospital 7",
        "Hospital 8",
        "Hospital 9",
        "Hospital 10",
        "Hospital 11",
        "Hospital 12",
        "Hospital 13",
        "Hospital 14",
        "Hospital 15",
        "Hospital 16",
        "Hospital 17",
        "Hospital 18",
        "Hospital 19",
        "Hospital 20",
        "Hospital 21",
        "Hospital 22",
        "Hospital 23",
        "Hospital 24",
        "Hospital 25"
    };
    }
    
    class ViewHolder : Java.Lang.Object
    {
        public TextView hospitalName { get; set; }
        public ToggleButton hospitalFavourites { get; set; }
        public int position { get; set; }
    }
    

    }

    【讨论】:

    • AyushKhare 已经解决了后台的问题。我尝试将位置添加为 ViewHolder 中的属性,但这仍然返回了错误的位置。至于 ToggleButton,我目前没有在做这个,所以我还没有实现它。它不影响代码。
    • @lowteetak 我现在已经实现了切换按钮,但它似乎也存在重复问题。例如,当按钮 1 被切换时,按钮 7 和按钮 15 也将被切换。我在原始帖子中添加了对更新代码的编辑。你能帮我解决这个问题吗?
    • 您正在调用 holder.hospitalFavourites.Click += delegate 多次。想象一下,当您向下滚动并回收视图时,您将在最喜欢的按钮单击中添加另一个委托。如果(view == null),事件绑定应该在块内。此外,您需要重新检查收藏按钮点击的逻辑。
    • 到目前为止,单击按钮只会将其文本设置到该位置。将点击事件移入if块后,不仅原来的问题依然存在,每次点击滚动后也会返回不同的位置。
    • 更新了示例代码。您将需要修改点击委托中发生的事情以执行您想要的操作。
    【解决方案3】:

    它可能是适配器中的委托,有时可能是许多问题的根源。尝试使用方法委托而不是匿名委托。并在正确设置之前将 onclick 事件设置为 null。

    这是因为对象正在被回收,并且 click 方法可以调用两次,因此您不需要 if(view == null)。

    我希望你能理解我,我无法提供经过验证的示例代码,但它应该看起来像:

    .
    .
    .
    view.Click -= customOnClickMethod;
    view.Click += customOnClickMethod;
    .
    .
    .
    void customOnClickMethod(object sender, EventArgs e){
        //implement onclick function
    
    }
    

    希望这会有所帮助。

    【讨论】:

    • 感谢您的回复。我已经尝试过了,但问题仍然存在。选中一个切换按钮仍会自动切换另一个低于 te recyclerview 的按钮。
    【解决方案4】:

    设法用以下代码解决它:

    class RecyclerViewAdapter_SelectHospital : RecyclerView.Adapter
    {
        private readonly Context context;
        private List<Hospital> list;
        public event EventHandler<int> ItemClick;
    
        public RecyclerViewAdapter_SelectHospital(Context c, List<Hospital> l)
        {
            context = c;
            list = l;
        }
    
        public override int ItemCount
        {
            get
            {
                return list.Count;
            }
        }
    
        private void OnClick(int position)
        {
            ItemClick?.Invoke(this, position);
        }
    
        private void OnItemClick(object sender, int position)
        {
            if (list[position].favourited == false)
            {
                list[position].favourited = true;
            }
            else
            {
                list[position].favourited = false;
            }
        }
    
        public override long GetItemId(int position)
        {
            return position;
        }
    
        public override int GetItemViewType(int position)
        {
            return position % 2 == 1 ? 0 : 1;
        }
    
        public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
        {
            SelectHospital_ViewHolder vh = holder as SelectHospital_ViewHolder;
    
            //  Set height of row
            vh.ItemView.LayoutParameters.Height = Select_Hospital.LIST_HEIGHT / 6;
    
            //  Set alternating background of row
            int type = GetItemViewType(position);
    
            if (type == 0)
            {
                vh.ItemView.SetBackgroundResource(Resource.Color._8_white);
            }
    
            vh.hospitalName.Text = list[position].name;
            vh.hospitalFavourites.Checked = list[position].favourited;
        }
    
        public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
        {
            View view = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.sublayout_Hospital_List_Item, parent, false);
            SelectHospital_ViewHolder holder = new SelectHospital_ViewHolder(view, OnClick);
    
            holder.ItemView.Click += delegate
            {
                Intent intent = new Intent(context, typeof(Request_Appointment));
                intent.PutExtra("hospitalName", holder.hospitalName.Text);
                context.StartActivity(intent);
            };
    
            ItemClick -= OnItemClick;
            ItemClick += OnItemClick;
    
            return holder;
        }
    }
    
    class SelectHospital_ViewHolder : RecyclerView.ViewHolder
    {
        public TextView hospitalName { get; set; }
        public ToggleButton hospitalFavourites { get; set; }
    
        public SelectHospital_ViewHolder(View view, Action<int> listener) : base(view)
        {
            hospitalName = view.FindViewById<TextView>(Resource.Id.selectHospital_HospitalText);
            hospitalFavourites = view.FindViewById<ToggleButton>(Resource.Id.selectHospital_FavouritesToggle);
    
            hospitalFavourites.Click += (sender, e) => listener(AdapterPosition);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-02-15
      • 1970-01-01
      • 2017-08-24
      • 1970-01-01
      • 1970-01-01
      • 2019-08-10
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      相关资源
      最近更新 更多