65.9K
CodeProject 正在变化。 阅读更多。
Home

Android Recycler View 带有 Spinner 项目更改选择和更新 UI

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2015年9月25日

CPOL

4分钟阅读

viewsIcon

79890

本文将帮助您了解如何监听 RecyclerView 内部的嵌套控件,并动态更新视图。

引言

由于我一直在学习 Xamarin 跨平台开发,我开发了一个示例应用程序,其中包含 RecyclerView,该视图又包含 Spinner 和 Checkbox 等嵌套控件。我发现当我更改 Spinner 下拉列表的值时,很难动态更新视图行。因此,我想到一个办法,即为 Spinner 下拉列表添加事件监听器,并将事件监听器传递给主 Activity。在主 Activity 中,我只需要更新底层数据集,然后有一个名为 notifyitemchanged 的选项,它会根据提供的索引更新视图行 RecyclerView 列表。

使用代码

首先,本文将介绍如何在 Xamarin Android 中创建 RecyclerView,然后在每一行添加 Spinner 控件,并根据所选的 Spinner 下拉列表值动态更新列表行。Xamarin 官方指南页面非常详细地解释了 RecyclerView 的基本概念(https://developer.xamarin.com/guides/android/user_interface/recyclerview/)。我这里的解释仅限于展示如何在 RecyclerView 行中添加嵌套控件,监听这些嵌套控件的事件监听器,并更新列表行。

让我们开始编写代码;我创建了一个 ViewHolder 类,它将包含视图对象的引用。ViewHolder 类有一个 Spinner 对象,然后在 ViewHolder 类构造函数中将 Spinner 对象分配给“ItemSelected”事件。在这里,我们使用 View 对象注册事件处理程序(在本例中为 itemlistner 和 spinnerItemSelected)。请注意,ItemSelected 事件是用户从 Spinner 下拉列表中选择值后调用的事件,我还添加了另一个事件处理程序(即 itemlistner),它用于指示 RecyclerView 中触摸了哪一行。以下是 ViewHolder 类的完整示例。

 
		public class VegeViewHolder: RecyclerView.ViewHolder
		{
			public ImageView Image { get; set; }
			public TextView Name { get; set; }
			public Spinner Quantity { get; set; }
			public TextView Price { get; set; }
			public TextView TotalAmount { get; set; }

			// Get references to the views defined in the CardView layout.
			public VegeViewHolder(View itemView, Action <int > itemlistner, Action <object,AdapterView.ItemSelectedEventArgs > spinnerItemSelected )
				:base(itemView)
			{
				Image = itemView.FindViewById <ImageView > (Resource.Id.list_image);
				Name = itemView.FindViewById <TextView > (Resource.Id.Name);
				Price = itemView.FindViewById <TextView > (Resource.Id.Price);
				Quantity = itemView.FindViewById <Spinner > (Resource.Id.spinner1);
				TotalAmount = itemView.FindViewById <TextView > (Resource.Id.total);

				itemView.Click += (sender, e) = > itemlistner (base.Position);
				Quantity.ItemSelected+=	new EventHandler <AdapterView.ItemSelectedEventArgs > (spinnerItemSelected);
			}

		}

让我们创建一个自定义的 RecyclerView 适配器类。我们的大部分 RecyclerView 集成代码的“繁重工作”都在适配器中完成。RecyclerView 要求我们提供一个派生自 RecyclerView.Adapter 的适配器,以访问我们的数据源并使用数据源的内容填充每个项目。在此适配器类中,我创建了一个事件处理程序,该处理程序被分配给 Spinner 控件。

 
			public event EventHandler <int > ItemClick;
			public event EventHandler <AdapterView.ItemSelectedEventArgs > SpinnerItemSelectionChanged;	
	

在实现 RecyclerView 适配器时,我们必须覆盖以下方法:

  • OnCreateViewHolder – 实例化项目布局文件和 ViewHolder。
  • OnBindViewHolder – 将指定位置的数据加载到给定 ViewHolder 中存储了引用的视图中。
  • ItemCount – 返回数据源中的项目数量。

布局管理器在 positioning items within the RecyclerView. More detail information can be found at Xamarin Recycler view guide. Let's create the OnCreateViewHolder method and Instantiates the view holder class and pass the event listener to the constructor.

 

 
		public override RecyclerView.ViewHolder	OnCreateViewHolder(ViewGroup parent, int viewType)
		{
			// Inflate the CardView for the photo:
			View itemView = LayoutInflater.From(parent.Context).
				Inflate(Resource.Layout.list_items, parent, false);

			// Create a ViewHolder to find and hold these view references, and 
			// register OnClick with the view holder:
			VegeViewHolder vh = new VegeViewHolder(itemView, OnClick,spinner_ItemSelected);
			return vh;
		}
		
		void OnClick(int position)
		{
			if (ItemClick != null)
				ItemClick (this, position);
		}

		void spinner_ItemSelected (object sender, AdapterView.ItemSelectedEventArgs e)
		{
			if (SpinnerItemSelectionChanged != null)
				SpinnerItemSelectionChanged (sender, e);
		}

现在,让我们创建 OnBindViewHolder。当布局管理器准备好在 RecyclerView 的可见屏幕区域中显示特定视图时,它会调用适配器的 OnBindViewHolder 方法,以使用数据源的内容填充指定行位置的项目。

		public override void OnBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)
		{
			var item = Items[position];

			var vh = viewHolder as VegeViewHolder;
			var spinnerPos = 0;
			var adapter =new ArrayAdapter<String>(Context, Android.Resource.Layout.SimpleSpinnerItem, _quantity);
			adapter.SetDropDownViewResource (Android.Resource.Layout.SimpleSpinnerDropDownItem);

			vh.Name.Text = item.Name;
			vh.Price.Text = string.Format("Price: ${0}",item.Price);
			vh.ItemView.Tag = position;
			if (item.Quantity > 0) {
				spinnerPos = adapter.GetPosition (item.Quantity.ToString ());
				vh.TotalAmount.Text = string.Format ("${0}", item.Price * item.Quantity);
			} else {
				vh.TotalAmount.Text = "";
			}
			vh.Quantity.Tag = position; //keep reference to list view row position
			vh.Quantity.Adapter = adapter;
			vh.Quantity.SetSelection (spinnerPos);
			vh.Image.SetImageResource (item.ImageId);
		}

在上面的代码中,从宏观上看,我们将特定行位置的视图对象的值设置为数据集中的值。

现在是时候将这些事件处理程序分配给主 Activity 类了。因此,每当用户触摸或更改 Spinner 值时,这些事件都会在 Activity 类级别上处理。我们将在 MainActivity 中创建一个 item-click 事件处理程序方法和 SpinnerItemSelectionChangedEvent。此处理程序会简要显示一个 Toast,指示触摸了哪个项目行,并在 Spinner 值更改时,Toast 会显示从 Spinner 下拉列表中选择的值。

		void OnItemClick(object sender, int position)
		{
			// Display a toast that briefly shows the enumeration of the item selected
			int itemPosition = position + 1;
			Toast.MakeText(this, "Vegetable Item " + itemPosition, ToastLength.Short).Show();
		}

		void SpinnerItemSelectionChangedEvent(object sender, AdapterView.ItemSelectedEventArgs e)
		{
			Spinner spinner = (Spinner)sender;
			var itemPosition = Convert.ToInt32 (spinner.Tag);
			
			var currentquantity = vegeList[itemPosition].Quantity;
			var selectedQuantity = Convert.ToInt32( spinner.GetItemAtPosition (e.Position).ToString());
			if (currentquantity != selectedQuantity) {
				vegeList [itemPosition].Quantity = selectedQuantity;
				mAdapter.NotifyItemChanged (itemPosition);
			}

		}

从上面的 SpinnerItemSelectionChangedEvent 代码中可以看出,当数量(即 Spinner 对象)值发生变化时,我们正在实现 notifyitemchanged

  • NotifyItemChanged – 信号指示指定位置的项目已更改。

调用 NotifyItemChanged 后,指定位置的 RecyclerView 将会更新/重绘。这里的诀窍是确保仅在旧值与新值不相等时才调用 NotifyItemChanged。否则,适配器将进入无限循环,因为当重绘项目行并为 Spinner 对象分配事件处理程序时,SpinnerItemSelectionChangedEvent 总是会被触发,然后触发 NotifyItemChanged。因此,必须确保仅在数据集值与当前值不同时才调用 NotifyItemChanged 来更新行。

关于编码部分就这些了,请看下面的最终结果。

最终结果

最终图片在此

 

 

 

 

关注点

本文的目的是理解如何在 Android RecyclerView 中处理嵌套控件的事件。正如上面的示例所示,这不仅仅是 Spinner 控件,我们还可以为放置在 RecyclerView 中的 Checkbox、Button、EditText 控件实现事件处理程序。

历史

2015年9月 - 初始文档

© . All rights reserved.