天天看點

Android 擴充卡教程(四)

之前我們學習了什麼是擴充卡,并且三種常用的安卓原生擴充卡也講完了,接下來我們就要自定義擴充卡了,自定義的擴充卡能适應更多的情況,功能更加強大,當然也需要我們更加深入的學習才能應用自如。

終于到自己寫一個擴充卡的時候了!

我準備了兩個例子,一個簡單一些,一個複雜一些,這次先看個簡單的:

我還是繼續在前三次的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了。

最後我們看一下實作效果圖:

Android 擴充卡教程(四)

到此為止,我們的學習之旅又進了一大步,自定義擴充卡這一部分需要好好的進行了解,隻有真正了解的比較透徹,寫起來才會比較順手,學會和精通是不一樣的,僅僅是學會就隻能實作一些簡單的功能,而學精才能推陳出新,創造出更有影響力的項目。

下一講我會對這一講留下的幾個問題進行詳細的分析,比如holder,tag的詳解,争取讓大家了解的更加透徹一些,請繼續關注~

源代碼我會在最後一講的最後附上連結,因為我也是邊寫部落格邊碼代碼,我覺得這樣思路比較清楚一些。

我也還是個學生,水準有限,還請大家多多指教~

繼續閱讀