如今不管任何应用都能够看到列表的存在,而本章我们将学习如何使用Xamarin去实现它,以及如何使用适配器和自定义适配器(本文中的适配器的主要内容就是将原始的数据转换成了能够供列表控件显示的项)。
二、简介适配器
在开始之前我们需要先了解下适配器,首先是提供给我们使用的适配器之间的关系:
下面我们将上面的适配器进行简单的介绍:
BaseAdapter:顾名思义,就是所以适配器的基类,但是我们不能将其实例化,因为它是一个虚类,一般我们都是继承该类并实现其中的方法,形成形成自定义的列表(大多数情况下我们都会使用到它)。
ArrayAdapter和ArrayAdapter<T>:就是专门用于将列表数据的适配器,该适配器内部已经实现了BaseAdapter的方法,所以我们只需要指定对应的数据项以及列表项资源即可。
CursorAdapter:上面的适配器只是用于列表数据,而该适配器则可以用在将数据库返回的结果集显示到列表中去,当然在难度上也要比上面的适配器高。
三、正文
1.简单列表
下面我们将利用ListActivity和ArrayAdapater<T>去实现一个简单的列表界面,下面为该示例的代码(MainActivity.cs):
1 public class MainActivity : ListActivity 2 { 3 protected string[] items; 4 5 protected override void OnCreate(Bundle bundle) 6 { 7 base.OnCreate(bundle); 8 items = new string[]{ 9 "First","Second","Third" 10 }; 11 ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, items); 12 } 13 14 protected override void OnListItemClick(ListView l, View v, int position, long id) 15 { 16 var t = items[position]; 17 Toast.MakeText(this, t, ToastLength.Short).Show(); 18 } 19 }
这里有一个重要的关键词就是ListActivity,如果存在一个界面整个界面都是列表,那么我们就可以继承这个特殊的活动,并且可以通过ListView属性和ListAdapter去控制,同时还可以响应关于列表的事件。其中我们利用了ArrayAdapter给列表指定了一个适配器,而这个适配器的第一个参数是当前的上下文,第二个是列表中的项的界面,最后一个就是对应的数据了。
最后将显示如下的界面:
当我们点击不同的项后,还能看到底部显示了当前我们选择的项。这个功能就是在我们重写了OnListItemClick实现了,正如代码中所示,我们根据position获取指定的数据,然后通过Toast将其显示出来。
2.自定义一个适配器
简单介绍过如何使用适配器后,我们将开始学习如何利用BaseAdapter自定义一个适配器,能够促使我们理解适配器内部的工作原理,首先我们来看下笔者的写的代码:
1 public class MyCustomeAdapter : BaseAdapter<string> 2 { 3 string[] items; 4 Activity activity; 5 6 public MyCustomeAdapter(Activity context, string[] values) 7 : base() 8 { 9 activity = context; 10 items = values; 11 } 12 13 public override string this[int position] 14 { 15 get { return items[position]; } 16 } 17 18 public override int Count 19 { 20 get { return items.Length; } 21 } 22 23 public override long GetItemId(int position) 24 { 25 return position; 26 } 27 28 public override View GetView(int position, View convertView, ViewGroup parent) 29 { 30 View v = convertView; 31 if (v == null) 32 v = activity.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null); 33 v.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position]; 34 return v; 35 } 36 }
其中最主要的是GetView方法,ListView和ListActivity主要是通过调用该方法获取对应项的视图,然后将其添加为子控件,从而显示,如果我们需要显示例如淘宝等app那种复杂的列表就需要通过重写该方法达到,细心的读者可以发现笔者是先判断convertView是否为null,如果为null才通过Inflate方法重新实例化一个视图,关于这部分我们可以参考该文章。
关于其他的方法相信大家靠名称就能够明白了,这里就不多做解释,具体的使用方法如下所示:
1 protected override void OnCreate(Bundle bundle) 2 { 3 base.OnCreate(bundle); 4 string[] items = new string[]{ 5 "First","Second","Third" 6 }; 7 ListAdapter = new MyCustomeAdapter(this, items); 8 }
3.关键字索引
在手机的使用中,大家一定经常使用着电话簿,当我们点击右边的滚动条进行滑动的时候还会显示a-z的字母并且列表的内容也会根据这些字母的变化发生变化,将快速定位到联系人姓名为指定字母开头的位置,本节我们将会学习如何实现该功能。
提供这个功能我们需要实现一个接口,这个接口就是ISectionIndexer,下面是关于该接口的代码:
1 public interface ISectionIndexer : IJavaObject, IDisposable 2 { 3 int GetPositionForSection(int section); 4 int GetSectionForPosition(int position); 5 Object[] GetSections(); 6 }
关于这些接口简单的介绍下:
GetPositionForSection:根据关键字的索引获取该关键字的起始数据索引。
GetSectionForPosition:根据数据索引获取关键字索引。
GetSections:返回关键字数组。
笔者为了能够节约时间,所以利用上节的示例代码,在MyCustomeAdapter中实现了ISectionIndexer接口,下面是笔者的代码:
1 public class MyCustomeAdapter : BaseAdapter<string> , ISectionIndexer 2 { 3 string[] items; 4 Activity activity; 5 6 Dictionary<string, int> alphaindex; 7 Java.Lang.Object[] sectionsObjects; 8 string[] sections; 9 10 public MyCustomeAdapter(Activity context, string[] values) 11 : base() 12 { 13 activity = context; 14 items = values; 15 16 alphaindex = new Dictionary<string, int>(); 17 //获取每种关键字的起始数据索引 18 for (int i = 0; i < items.Length; i++) 19 { 20 string key = items[i][0].ToString(); 21 if (!alphaindex.ContainsKey(key)) 22 alphaindex.Add(key, i); 23 } 24 25 //将关键字转换成数据 26 sections = new string[alphaindex.Keys.Count]; 27 alphaindex.Keys.CopyTo(sections, 0); 28 29 //将关键字转换成Java.Lang.String类型 30 sectionsObjects = new Java.Lang.Object[alphaindex.Keys.Count]; 31 for (int i = 0; i < sections.Length; i++) 32 { 33 sectionsObjects[i] = new Java.Lang.String(sections[i]); 34 } 35 } 36 37 public override string this[int position] 38 { 39 get { return items[position]; } 40 } 41 42 public override int Count 43 { 44 get { return items.Length; } 45 } 46 47 public override long GetItemId(int position) 48 { 49 return position; 50 } 51 52 public override View GetView(int position, View convertView, ViewGroup parent) 53 { 54 View v = convertView; 55 if (v == null) 56 v = activity.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, null); 57 v.FindViewById<TextView>(Android.Resource.Id.Text1).Text = items[position]; 58 return v; 59 } 60 61 public int GetPositionForSection(int section) 62 { 63 //根据关键字索引获取关键字,然后在根据关键字从alphaindex获取对应的value,即该关键字的起始数据索引 64 return alphaindex[sections[section]]; 65 } 66 67 public int GetSectionForPosition(int position) 68 { 69 int preposition = 0; 70 //循环关键字 71 for (int i = 0; i < sections.Length; i++) 72 { 73 //判断当前的索引是否在i所在关键字的范围内 74 if (GetPositionForSection(i) > position) 75 break; 76 preposition = i; 77 } 78 return preposition; 79 } 80 81 public Java.Lang.Object[] GetSections() 82 { 83 return sectionsObjects; 84 } 85 }