之前我們學習了什麼是擴充卡,并且三種常用的安卓原生擴充卡也講完了,接下來我們就要自定義擴充卡了,自定義的擴充卡能适應更多的情況,功能更加強大,當然也需要我們更加深入的學習才能應用自如。
終于到自己寫一個擴充卡的時候了!
我準備了兩個例子,一個簡單一些,一個複雜一些,這次先看個簡單的:
我還是繼續在前三次的demo項目上繼續添加例子,最後一篇的時候把源碼分享給大家~
讓我們繼續一步步寫下去。
這個例子是在listview上面添加按鈕,具體來說是顯示一個按鈕和一個圖檔,兩行字。這個小問題涉及到的知識挺多的。也許你會想:添加按鈕首先要寫一個有按鈕的xml檔案,然後用教程(三)的方法定義一個擴充卡,然後将資料映射到布局檔案上。但是事實并非這樣,因為按鈕是無法映射的,即使你成功的用布局檔案顯示出了按鈕也無法添加按鈕的響應,這時就要研究一下listview是如何現實的了,而且必須要重寫一個類繼承baseadapter。
首先我們先了解一下自己寫擴充卡的原理:
第一步要首先重寫一個類繼承baseadapter
先讓我們看一下各個方法:
(1)首先是getcount()方法,這個方法要傳回你要添加進listview裡的東西的總數,
也就是要告訴listview,我添加進清單裡的東西有多少,需要多長的清單。
這裡的marray就是一個簡單的list<string>,
可能有人會問,為什麼不直接傳回mlistview的長度呢?
原因就是我們的listview可能會添加”頭“和”尾“,來進行一些更新之類的互動,
就像微網誌之類的下拉重新整理或者到底後加載,是以幹脆直接用我們添加的内容的長度
@override
public int getcount() {
return marray == null ? 0 : marray.size();
}
(2)接下來就是getitem(int position)方法了
listview要加載内容,要獲得内容才可以加載!
這個方法就是要讓listview可以通過一個position來獲得我們要添加在相應位置的内容的
内容是什麼?當然是剛才marray裡相應位置的東西啦!
@override
public object getitem(int position) {
return marray.get(position);
}
(3)然後是getitemid(int position)方法,這個方法應該是為了友善listview進行管理的,
簡單說,我們就按原來的position來讓他管理,原本是幾就是幾,省事,直接傳回position!
@override
public long getitemid(int position) {
return position;
最後,重頭戲!getview()方法!
這裡要實作的東西就比較多了
這個也很好了解,個人的了解就是listview要友善的得到自己裡面的每個view
不然人家怎麼知道你的marray裡的資料,要怎麼填入listview裡的每個view
@override
public view getview(int position, view convertview, viewgroup parent) {
return null;
}
這裡面一般我們會怎麼做呢?
一般,我們添加到listview裡的每個view都是xml定義好的。
一開始需要構造過來一個context!
通過layoutinflater.from(context).inflate(r.layout.你定義的xml,null);
獲得你要添加進去的view來賦給convertview
如果我們定義的xml裡有一個textview
那我們就textview ttextview = (textview)convertview.findviewbyid(r.id.你的textview);
這樣就可以通過position,在marray裡找到我們相應位置的内容,讓textview顯示出來
當然,最後要return convertview
把這個我們包裝好的view給回listview,讓它在清單裡顯示。
下面是具體實作的過程:
項目開始:
也還是先在activity_main.xml裡添加一個button,一會跳轉的時候使用。
然後建立一個類myadapterdemo繼承自activity作為我們第四個個例子的activity,@override 我們的oncreate方法。
建立一個xml檔案myadapterdemo.xml作為我們的布局檔案,其中也是包含一個文本域和一個listview:
myadapterdemo.xml:
代碼如下:
然後需要定義好一個用來顯示每一個列内容的xml
listitem2.xml 包含橫向的圖檔與文字還有一個button,
listitem2.xml:
自定義擴充卡:
建立一個類myadapter繼承自baseadapter,這時候eclipse會提示你override預設的方法,點選之後就會出現上面我所說的那些方法了。
之後建立一個viewholder,具體原因我會在下一次教程中仔細說明。
viewholder:
把viewholder作為内部類放在最後等着,一會就有用了。
構造器
然後我們再研究下我們需要外部的什麼東西,也就是構造器需要被傳進什麼參數。
我們類比一下之前原生的擴充卡的構造方法,自然而然的想到他們都有一個context,同時都需要傳參數!
是以我們的myadapter自然也不例外了~
在之前的說明中說過,一開始需要構造過來一個context!
隻有這樣才能通過layoutinflater.from(context).inflate(r.layout.你定義的xml,null);
獲得你要添加進去的view來賦給convertview。
是以構造器以及必要的對象就要這樣寫:
注意這有個layoutinflater我給大家解釋一下
在實際開發中layoutinflater這個類還是非常有用的,它的作用類似于findviewbyid()。不同點是layoutinflater是用來找res/layout/下的xml布局檔案,并且執行個體化;而findviewbyid()是找xml布局檔案下的具體widget控件(如
button、textview等)。
具體作用:
1、對于一個沒有被載入或者想要動态載入的界面,都需要使用layoutinflater.inflate()來載入;
2、對于一個已經載入的界面,就可以使用activiyt.findviewbyid()方法來獲得其中的界面元素。
這樣我們從構造器中得到了資料data還有context(通過layoutinflater得到布局檔案)
之後我們隻要把@override的方法填寫完整就好了,希望這時候你還沒有忘記開始時我講過的基本知識。
不過沒關系,我會一點點講清楚的:
第一個方法getcount():
是得到長度,是以外部資料有所少就要有多長,是以傳回的是data==null?0:data.size();
也就是如果外面傳過來的資料為空,那麼長度為0,不是空,長度就是資料的數量,
注意:這裡非常有必要處理一下data==null這種情況!很多代碼這裡都沒做處理,這是很不好的習慣。
是以這個方法填好了:
@override
public int
getcount() {
return data == null? 0:data.size();
}
第二個方法getitem(int position):
之前也說過了,這個方法就是要讓listview可以通過一個position來獲得我們要添加在相應位置的内容的
内容是什麼?當然是data裡相應位置的東西啦!
是以這個方法也填好了:
public object getitem(int position) {
return data.get(position);
第三個方法getitemid(int position)
:
這個方法應該是為了友善listview進行管理的,
沒有什麼特殊需求的話,我們就按原來的position來讓他管理,position原本是幾就是幾,直接傳回position
public long getitemid(int position) {
return position;
這樣的話就隻剩下一個大頭了:
千呼萬喚始出來的
public view getview(int position, view convertview, viewgroup parent)
寫到這我發現還是不得不先解釋一下viewholder了,我先粗略的盡量讓大家了解,因為下一講的主題就是它!
咱們一點點的分析,注意看傳回值的類型,是一個view,不難想象這就是傳回了list裡面一個item的view,android中有個叫做recycler(反複循環器)的構件,listview的加載原理是這樣的:
有一個item出了螢幕,在空出來的部分是由出螢幕的那個item通過擴充卡的getview方法改變成了新的item加以填充的。是以
1.如果你有10億個項目(item),其中隻有可見的項目存在記憶體中,其他的在recycler中
2.初始化的時候,listview先請求一個type1視圖(getview),然後請求其他可見的項目。這時convertview在getview中是null的
3.當item1滾出螢幕,并且一個新的項目從螢幕地段上來時,listview再請求一個type1視圖。convertview此時不是空值了,它的值是item1.你隻需要設定新的資料傳回convertview,不必重新建立一個視圖。這樣直接使用convertview進而減少了很不不必要view的建立
而更快的方式是定義一個viewholder,将convertview的tag設定為viewholder,不為空是重新使用
(tip:view中的settag(onbect)表示給view添加一個格外的資料,以後可以用gettag()将這個資料取出來。)
這個問題先說到這,下一講我們在仔細讨論,這樣對getview有所了解了吧。
我直接貼代碼,然後大家看注釋就好了:
這樣大家就比較了解了吧,至于viewholder和tag,下次會好好解釋的~
下面是myadapter的完整代碼
之後我們再回到myadapterdemo向之前那樣增加擴充卡,添加資料就好了,在按鈕的處理上也比較簡單,我偷下懶,先貼代碼後解釋~
myadapterdemo的完整代碼如下:
讓我們再總結一下工作原理listview在開始繪制的時候,系統首先調用getcount()函數,根據他的傳回值得到listview的長度,然後根據這個長度,調用getview()逐一繪制每一行。如果你的getcount()傳回值是0的話,清單将不顯示同樣return1,就隻顯示一行。
系統顯示清單時,首先執行個體化一個擴充卡(這裡将執行個體化自定義的擴充卡)。當手動完成适配時,必須手動映射資料,這需要重寫getview()方法。系統在繪制清單的每一行的時候将調用此方法。getview()有三個參數,position表示将顯示的是第幾行,covertview是從布局檔案中inflate來的布局。我們用layoutinflater的方法将定義好的listitem.xml檔案提取成view執行個體用來顯示。然後将xml檔案中的各個元件執行個體化(簡單的findviewbyid()方法)。這樣便可以将資料對應到各個元件上了。但是按鈕為了響應點選事件,需要為它添加點選監聽器,這樣就能捕獲點選事件。至此一個自定義的listview就完成了。
現在讓我們回過頭從新審視這個過程。系統要繪制listview了,他首先獲得要繪制的這個清單的長度,然後開始繪制第一行,怎麼繪制呢?調用getview()函數。在這個函數裡面首先獲得一個view(實際上是一個viewgroup),然後再執行個體并設定各個元件,顯示之。好了,繪制完這一行了。那 再繪制下一行,直到繪完為止。
如果需要listview也加入監聽,在實際的運作過程中會發現listview的每一行沒有焦點了,這是因為button搶奪了listview的焦點,隻要布局檔案中将button設定為沒有焦點應該就ok了。
最後我們看一下實作效果圖:

到此為止,我們的學習之旅又進了一大步,自定義擴充卡這一部分需要好好的進行了解,隻有真正了解的比較透徹,寫起來才會比較順手,學會和精通是不一樣的,僅僅是學會就隻能實作一些簡單的功能,而學精才能推陳出新,創造出更有影響力的項目。
下一講我會對這一講留下的幾個問題進行詳細的分析,比如holder,tag的詳解,争取讓大家了解的更加透徹一些,請繼續關注~
源代碼我會在最後一講的最後附上連結,因為我也是邊寫部落格邊碼代碼,我覺得這樣思路比較清楚一些。
我也還是個學生,水準有限,還請大家多多指教~