Xamarin.Android开发实践(十四)
原文:Xamarin.Android開發(fā)實(shí)踐(十四)
Xamarin.Android之ListView和Adapter
一、前言
如今不管任何應(yīng)用都能夠看到列表的存在,而本章我們將學(xué)習(xí)如何使用Xamarin去實(shí)現(xiàn)它,以及如何使用適配器和自定義適配器(本文中的適配器的主要內(nèi)容就是將原始的數(shù)據(jù)轉(zhuǎn)換成了能夠供列表控件顯示的項(xiàng))。
?
二、簡(jiǎn)介適配器
在開始之前我們需要先了解下適配器,首先是提供給我們使用的適配器之間的關(guān)系:
下面我們將上面的適配器進(jìn)行簡(jiǎn)單的介紹:
BaseAdapter:顧名思義,就是所以適配器的基類,但是我們不能將其實(shí)例化,因?yàn)樗且粋€(gè)虛類,一般我們都是繼承該類并實(shí)現(xiàn)其中的方法,形成形成自定義的列表(大多數(shù)情況下我們都會(huì)使用到它)。
ArrayAdapter和ArrayAdapter<T>:就是專門用于將列表數(shù)據(jù)的適配器,該適配器內(nèi)部已經(jīng)實(shí)現(xiàn)了BaseAdapter的方法,所以我們只需要指定對(duì)應(yīng)的數(shù)據(jù)項(xiàng)以及列表項(xiàng)資源即可。
CursorAdapter:上面的適配器只是用于列表數(shù)據(jù),而該適配器則可以用在將數(shù)據(jù)庫返回的結(jié)果集顯示到列表中去,當(dāng)然在難度上也要比上面的適配器高。
?
三、正文
?
1.簡(jiǎn)單列表
下面我們將利用ListActivity和ArrayAdapater<T>去實(shí)現(xiàn)一個(gè)簡(jiǎn)單的列表界面,下面為該示例的代碼(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 }這里有一個(gè)重要的關(guān)鍵詞就是ListActivity,如果存在一個(gè)界面整個(gè)界面都是列表,那么我們就可以繼承這個(gè)特殊的活動(dòng),并且可以通過ListView屬性和ListAdapter去控制,同時(shí)還可以響應(yīng)關(guān)于列表的事件。其中我們利用了ArrayAdapter給列表指定了一個(gè)適配器,而這個(gè)適配器的第一個(gè)參數(shù)是當(dāng)前的上下文,第二個(gè)是列表中的項(xiàng)的界面,最后一個(gè)就是對(duì)應(yīng)的數(shù)據(jù)了。
?
最后將顯示如下的界面:
?
當(dāng)我們點(diǎn)擊不同的項(xiàng)后,還能看到底部顯示了當(dāng)前我們選擇的項(xiàng)。這個(gè)功能就是在我們重寫了OnListItemClick實(shí)現(xiàn)了,正如代碼中所示,我們根據(jù)position獲取指定的數(shù)據(jù),然后通過Toast將其顯示出來。
?
?
2.自定義一個(gè)適配器
簡(jiǎn)單介紹過如何使用適配器后,我們將開始學(xué)習(xí)如何利用BaseAdapter自定義一個(gè)適配器,能夠促使我們理解適配器內(nèi)部的工作原理,首先我們來看下筆者的寫的代碼:
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主要是通過調(diào)用該方法獲取對(duì)應(yīng)項(xiàng)的視圖,然后將其添加為子控件,從而顯示,如果我們需要顯示例如淘寶等app那種復(fù)雜的列表就需要通過重寫該方法達(dá)到,細(xì)心的讀者可以發(fā)現(xiàn)筆者是先判斷convertView是否為null,如果為null才通過Inflate方法重新實(shí)例化一個(gè)視圖,關(guān)于這部分我們可以參考該文章。
?
關(guān)于其他的方法相信大家靠名稱就能夠明白了,這里就不多做解釋,具體的使用方法如下所示:
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.關(guān)鍵字索引
在手機(jī)的使用中,大家一定經(jīng)常使用著電話簿,當(dāng)我們點(diǎn)擊右邊的滾動(dòng)條進(jìn)行滑動(dòng)的時(shí)候還會(huì)顯示a-z的字母并且列表的內(nèi)容也會(huì)根據(jù)這些字母的變化發(fā)生變化,將快速定位到聯(lián)系人姓名為指定字母開頭的位置,本節(jié)我們將會(huì)學(xué)習(xí)如何實(shí)現(xiàn)該功能。
?
提供這個(gè)功能我們需要實(shí)現(xiàn)一個(gè)接口,這個(gè)接口就是ISectionIndexer,下面是關(guān)于該接口的代碼:
1 public interface ISectionIndexer : IJavaObject, IDisposable 2 { 3 int GetPositionForSection(int section); 4 int GetSectionForPosition(int position); 5 Object[] GetSections(); 6 }關(guān)于這些接口簡(jiǎn)單的介紹下:
GetPositionForSection:根據(jù)關(guān)鍵字的索引獲取該關(guān)鍵字的起始數(shù)據(jù)索引。
GetSectionForPosition:根據(jù)數(shù)據(jù)索引獲取關(guān)鍵字索引。
GetSections:返回關(guān)鍵字?jǐn)?shù)組。
?
筆者為了能夠節(jié)約時(shí)間,所以利用上節(jié)的示例代碼,在MyCustomeAdapter中實(shí)現(xiàn)了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 //獲取每種關(guān)鍵字的起始數(shù)據(jù)索引 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 //將關(guān)鍵字轉(zhuǎn)換成數(shù)據(jù) 26 sections = new string[alphaindex.Keys.Count]; 27 alphaindex.Keys.CopyTo(sections, 0); 28 29 //將關(guān)鍵字轉(zhuǎn)換成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 //根據(jù)關(guān)鍵字索引獲取關(guān)鍵字,然后在根據(jù)關(guān)鍵字從alphaindex獲取對(duì)應(yīng)的value,即該關(guān)鍵字的起始數(shù)據(jù)索引 64 return alphaindex[sections[section]]; 65 } 66 67 public int GetSectionForPosition(int position) 68 { 69 int preposition = 0; 70 //循環(huán)關(guān)鍵字 71 for (int i = 0; i < sections.Length; i++) 72 { 73 //判斷當(dāng)前的索引是否在i所在關(guān)鍵字的范圍內(nè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 }
?
關(guān)于該接口方法中的實(shí)現(xiàn)可以看代碼中的注釋,在使用的時(shí)候也要寫如下代碼:
1 protected override void OnCreate(Bundle bundle) 2 { 3 base.OnCreate(bundle); 4 string[] items = new string[]{ 5 "Apple","As","Banner", 6 "BMW","Beatf","Bad", 7 "Day","Dazy","Eat","Else", 8 "Feel","Fly","Go","Hourse" 9 }; 10 ListAdapter = new MyCustomeAdapter(this, items); 11 ListView.FastScrollEnabled = true; 12 ListView.FastScrollAlwaysVisible = true; 13 }?
按照官方的說法其實(shí)只要將FastScrollEnabled設(shè)置為true即可,但是筆者在虛擬機(jī)中測(cè)試的時(shí)候并不會(huì)顯示,所以筆者還要再將FastScrollAlwaysVisible一起設(shè)置為true才可以看到效果,下面為筆者的虛擬機(jī)上的截圖:
?
?
4.Cursor列表
前面我們都是通過數(shù)組來填充列表的,但是實(shí)際情況下數(shù)據(jù)大多數(shù)都是來自于數(shù)據(jù)庫,而數(shù)據(jù)庫通過查詢后將會(huì)得到ICursor類型的返回值,當(dāng)然我們也可以通過將ICursor轉(zhuǎn)換成數(shù)組或者鍵值對(duì),但這些都是沒有必要的,因?yàn)橛凶詭У倪m配器(CursorAdapter)。
?
首先我們需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫,下面是筆者的代碼,想學(xué)習(xí)數(shù)據(jù)庫的可以參考本人的以下隨筆:
《Xamarin.Android之SQLiteOpenHelper》
《Xamarin.Android之ContentProvider》
《Xamarin.Android之SQLite.NET ORM》
?
代碼如下所示:
1 public class TestDatabase : SQLiteOpenHelper 2 { 3 public TestDatabase(Context context):base(context,"testdatabase.db",null,1) 4 {} 5 6 public override void OnCreate(SQLiteDatabase db) 7 { 8 db.ExecSQL("CREATE TABLE TestTable ( _id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,name TEXT NOT NULL)"); 9 db.ExecSQL("INSERT INTO TestTable (name) values('Vegetables')"); 10 db.ExecSQL("INSERT INTO TestTable (name) values('Fruits')"); 11 db.ExecSQL("INSERT INTO TestTable (name) values('Flower Buds')"); 12 db.ExecSQL("INSERT INTO TestTable (name) values('Legumes')"); 13 db.ExecSQL("INSERT INTO TestTable (name) values('Bulbs')"); 14 db.ExecSQL("INSERT INTO TestTable (name) values('Tubers')"); 15 } 16 17 public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 18 { 19 20 } 21 }?
數(shù)據(jù)庫創(chuàng)建部分比較簡(jiǎn)單,主要的重點(diǎn)是下面的代碼。我們將實(shí)例化SimpleCursorAdapter并賦值給ListAdapter中去,代碼如下所示:
1 [Activity(Label = "CursorAdapter", MainLauncher = true, Icon = "@drawable/icon")] 2 public class MainActivity : ListActivity 3 { 4 TestDatabase vdb; 5 ICursor cursor; 6 7 protected override void OnCreate(Bundle bundle) 8 { 9 base.OnCreate(bundle); 10 vdb = new TestDatabase(this); 11 //通過SQL查詢數(shù)據(jù)庫 12 cursor = vdb.ReadableDatabase.RawQuery("SELECT * FROM TestTable", null); 13 StartManagingCursor(cursor); 14 //創(chuàng)建適配器 15 ListAdapter = new SimpleCursorAdapter(this, Android.Resource.Layout.SimpleListItem1, cursor, 16 new string[] { "name" }, //指定使用結(jié)果集中的哪個(gè)字段數(shù)據(jù) 17 new int[] { Android.Resource.Id.Text1 }); //填充到哪個(gè)控件中 18 } 19 20 protected override void OnDestroy() 21 { 22 base.OnDestroy(); 23 StopManagingCursor(cursor); 24 cursor.Close(); 25 } 26 }通過上面的代碼我們可以看到SimpleCursorAdapter相比之前的適配器,需要的參數(shù)更多。同時(shí)還需要對(duì)Cursor這個(gè)重要資源進(jìn)行管理,對(duì)應(yīng)的可以看到StartManagingCursor和StopManagingCursor,希望讀者在實(shí)際開發(fā)中一定要注意資源的釋放,以上的結(jié)果筆者就不截圖了。
?
5.自定義CursorAdapter
為了某些效果我們不一定非要繼承BaseAdapter從而實(shí)現(xiàn)很多的方法,如果 數(shù)據(jù)是來自于數(shù)據(jù)庫的我們還可以繼承自CursorAdapter,這樣我們能夠節(jié)省實(shí)現(xiàn)很多的方法,從而只需要關(guān)注重要部分,下面我們繼承 CursorAdapter來查看需要實(shí)現(xiàn)哪些方法:
1 public class CustomeCursorAdapter : Android.Widget.CursorAdapter 2 { 3 4 public override void BindView(Android.Views.View view, Android.Content.Context context, Android.Database.ICursor cursor) 5 { 6 throw new NotImplementedException(); 7 } 8 9 public override Android.Views.View NewView(Android.Content.Context context, Android.Database.ICursor cursor, Android.Views.ViewGroup parent) 10 { 11 throw new NotImplementedException(); 12 } 13 }可以看到我們只需要實(shí)現(xiàn)兩個(gè)方法,相比BaseAdapter而言減少了很多,其中BindView就是將ICursor中的數(shù)據(jù)與View進(jìn)行綁定,而NewView則是創(chuàng)建所需的界面。下面我們自定一個(gè)適配器實(shí)現(xiàn)和上一節(jié)一樣的效果:
?
1 public class CustomeCursorAdapter : Android.Widget.CursorAdapter 2 { 3 Activity context; 4 5 public CustomeCursorAdapter(Activity context, ICursor cursor) 6 : base(context, cursor) 7 { 8 this.context = context; 9 } 10 11 public override void BindView(Android.Views.View view, Android.Content.Context context, Android.Database.ICursor cursor) 12 { 13 TextView tv = view.FindViewById<TextView>(Android.Resource.Id.Text1); 14 tv.Text = cursor.GetString(1); 15 } 16 17 public override Android.Views.View NewView(Android.Content.Context context, Android.Database.ICursor cursor, Android.Views.ViewGroup parent) 18 { 19 return this.context.LayoutInflater.Inflate(Android.Resource.Layout.SimpleListItem1, parent, false); 20 } 21 }?
?6.注意點(diǎn)
?需要讀者需要使用系統(tǒng)自帶的SimpleAdapter時(shí),傳遞數(shù)據(jù)時(shí)候不能以Dictionary類型添加,必須以JavaDictionary,否則將會(huì)出現(xiàn)無法將其轉(zhuǎn)換為java.util.map的錯(cuò)誤。
總結(jié)
以上是生活随笔為你收集整理的Xamarin.Android开发实践(十四)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 插入排序(java版)
- 下一篇: Android开源库集锦