天天看點

Android 自定義 BaseAdapter 最佳實踐

雖然現在很多新的項目都在使用recyclerview,但是很多開發者在一些場景中還是傾向使用listview或者gridview,然後就是需要寫許多的adapter。一次項目組在新啟動一個新項目的時候,有個同僚拿來了一個網上說的萬能adapter,在使用的時候發現即使在單個視圖類型一旦邏輯判斷比較複雜情況下非常不友善,更不用說在擴充卡adapter中使用多視圖類型了,這裡僅是個人觀點,也許沒有掌握到精華。

Android 自定義 BaseAdapter 最佳實踐

當然了随着recyclerview的使用,網上也有很多有關對recyclerview多視圖類型adapter封裝的部落格,multitype 3.0是一個大神寫的比較全面的adapter,萬能擴充卡adapter自己使用不是很友善,于是就參看recyclerview中adapter的實作方式進行對baseadapter進行了簡單的封裝,封裝的目一是為了少寫代碼,另外一個就是讓邏輯看上去更清晰一些。我們知道在recyclerview的adapter實作中它将視圖建立與資料綁定進行了分離,同時将對view的查找建立也剝離開來了,本文就主要介紹如何将baseadapter的使用封裝為跟recyclerview的adapter使用方式一緻。由于很多時候在adapter中我們都是使用的簡單的視圖類型,即單類型視圖,是以本文将單視圖類型的adapter單獨封裝了一下,比使用多視圖類型的adapter使用了更嚴格的資料類型檢查,同時在使用上也友善了許多。

Android 自定義 BaseAdapter 最佳實踐
Android 自定義 BaseAdapter 最佳實踐

recyclerview中adapter的使用

在使用recyclerview的adapter的時候我們首先需要繼承recyclerview的一個靜态内部類adapter,然後重寫三個方法,實際上下面三個方法是必須要重寫的,因為都是抽象方法。

getitemcount()

onbindviewholder(vh holder, int position)

oncreateviewholder(viewgroup parent, int viewtype)

一般情況下重寫上面三個方法就可以,但是如果存在多視圖類型,在第三個方法

oncreateviewholder()方法中我們也可以看到有一個參數是viewtype,該參數作用就是針對不同的viewtype需要建立不同的viewholder,是以還需要重寫一個方法getitemviewtype(int position),針對多視圖類型同baseadapter實作方式倒是很像,在baseadapter中這是需要除此之外還要重寫一個方法getviewtypecount(),但是在recyclerview的adapter中不需要該方法。

簡單類型adapter

private class myadapter extends recyclerview.adapter<myviewholder> { 

    @override 

    public int getitemcount() { 

        return count; 

    } 

    public void onbindviewholder(myviewholder holder, int position) { 

        holder.textview.settext("text_" + position); 

    public myviewholder oncreateviewholder(viewgroup parent, int viewtype) { 

        view view = layoutinflater.from(getapplicationcontext()).inflate(r.layout.item_text, parent, false); 

        myviewholder holder = new myviewholder(view); 

        return holder; 

private static class myviewholder extends recyclerview.viewholder { 

    private textview textview; 

    public myviewholder(view itemview) { 

        super(itemview); 

        textview = (textview) itemview.findviewbyid(r.id.textview); 

複雜類型adapter

private class myadapter extends recyclerview.adapter<viewholder> { 

    public int getitemviewtype(int position) { 

        return position % 2 == 0 ? type_image : type_text; 

    public void onbindviewholder(viewholder holder, int position) { 

        int type = getitemviewtype(position); 

        switch (type) { 

        case type_text: 

            ((mytextholder) holder).textview.settext("text_" + position); 

            break; 

        case type_image: 

            ((myimageholder) holder).imageview.setimageresource(r.drawable.image); 

        } 

    public viewholder oncreateviewholder(viewgroup parent, int viewtype) { 

        view view; 

        viewholder holder = null; 

        switch (viewtype) { 

            view = layoutinflater.from(getapplicationcontext()).inflate(r.layout.item_text, parent, false); 

            holder = new mytextholder(view); 

            view = layoutinflater.from(getapplicationcontext()).inflate(r.layout.item_image, parent, false); 

            holder = new myimageholder(view); 

private class mytextholder extends recyclerview.viewholder { 

    public mytextholder(view itemview) { 

private class myimageholder extends viewholder { 

    private imageview imageview; 

    public myimageholder(view itemview) { 

        imageview = (imageview) itemview.findviewbyid(r.id.imageview); 

自定義baseadapter

在自定義基類之前,先簡單分析一下,我們需要自定義一個支援單種視圖的adapter,還要自定義一個支援多種視圖類型的adapter,兩個類都要繼承baseadapter,先将兩個類都公用的部分抽取出來定義為myadapter。

public abstract class myadapter<t> extends baseadapter { 

    protected list<t> datalist = new arraylist<>(); 

    protected context context; 

    protected layoutinflater inflater; 

    public myadapter(context context) { 

        this.context = context; 

        inflater = layoutinflater.from(context); 

    public void setdatalist(list<t> datalist) { 

        this.datalist = datalist; 

        notifydatasetchanged(); 

    public int getcount() { 

        if (null == datalist) { 

            return 0; 

        return datalist.size(); 

    public t getitem(int position) { 

        return datalist.get(position); 

    public long getitemid(int position) { 

        return position; 

在recyclerview的adapter實作中是沒有getview()方法的,下面我們就分析一下getview()方法如何拆分,一般情況下我們在實作getview()方法都是如下流程。

public view getview(int position, view convertview, viewgroup parent) { 

    viewholder holder = null; 

    if (null == convertview) { 

        //填充布局 

        convertview=inflater.inflate(r.layout.item_layout, parent,false); 

        holder = new viewholder(); 

        //通過id查詢控件 

        holder.textview=(textview)convertview.findviewbyid(r.id.textview); 

        holder.imageview=(imageview)convertview.findviewbyid(r.id.imageview); 

        convertview .settag(holder); 

    } else { 

        holder = (viewholder) convertview.gettag(); 

    //指派邏輯 

    return convertview; 

//一個空的viewholder 

public static class viewholder{ 

    textview textview; 

    imageview imageview; 

java程式設計比較流行的一種程式設計方式不是說面向接口程式設計嗎,在android開發中也有一個開發方式叫做面向holder的程式設計,上面代碼是傳統的實作viewholder的方式,說句實作話就沒做什麼事,就是作為一個載體承載着我們需要的控件。我們讓viewholder多做一些事情,讓它在convertview==null情況下需要做的多數邏輯都放到viewholder中去。

public class viewholder { 

    private final view itemview; 

    public viewholder(view itemview) { 

        if (null == itemview) { 

            throw new illegalargumentexception("itemview must not be null"); 

        } else { 

            this.itemview = itemview; 

            itemview.settag(this); 

    public view getitemview() { 

        return itemview; 

在viewholder中的itemview就是getview()方法中的convertview,這裡剛好是條目的根view,類似recyclerview中viewholder構造方法中itemview。由于不同的視圖需要建立不同的viewholder,是以我們可以将建立viewholder的方法設定為抽象的方法暴露出去,另外指派的時候我們也需要根據具體的業務進行指派,同樣設定一個抽象方法。

public abstract class simpleadapter<t,vh extends viewholder> extends myadapter<t> { 

    public simpleadapter(context context) { 

        super(context); 

    public view getview(int position, view convertview, viewgroup parent) { 

        vh holder = null; 

        if (null == convertview) { 

            holder = oncreateviewholder(parent); 

            convertview = holder.getitemview(); 

            holder = (vh) convertview.gettag(); 

        onbindviewholder(holder, position); 

        return convertview; 

    public abstract void onbindviewholder(vh holder, int position); 

    public abstract vh oncreateviewholder(viewgroup parent); 

在設定多視圖類型的adapter的時候隻需要在建立viewholder的時候多傳入一個viewtype的參數即可。

public abstract class multiadapter<t> extends myadapter<t> { 

    public multiadapter(context context) { 

            holder = oncreateviewholder(parent, getitemviewtype(position)); 

            holder = (viewholder) convertview.gettag(); 

    public abstract void onbindviewholder(viewholder holder, int position); 

    public abstract viewholder oncreateviewholder(viewgroup parent, int viewtype); 

自定義baseadapter的使用

單視圖類型simpleadapter使用

public class textadapter extends simpleadapter<string, textadapter.textholder> { 

    public textadapter(context context) { 

    public void onbindviewholder(textholder holder, int position) { 

        holder.textview.settext(getitem(position)); 

    public textholder oncreateviewholder(viewgroup parent) { 

        view convertview=inflater.inflate(r.layout.item_text, parent, false); 

        return new textholder(convertview); 

    static class textholder extends viewholder{ 

        public textview textview; 

        public textholder(view itemview) { 

            super(itemview); 

            textview=(textview) itemview.findviewbyid(r.id.textview); 

這裡我們使用了兩個泛型,一個是viewholder中支援的資料類型string,另外一個就是我們需要建立的viewholder,這樣在oncreateviewholder方法的傳回值就會自動傳回我們自定義的viewholder,有關泛型更多的知識可以參看java泛型使用解析,單視圖類型adapter的使用比recyclerview的adapter還要友善許多。

多視圖類型的使用

public class richadapter extends multiadapter<string> { 

    private static final int text = 0; 

    private static final int pic = 1; 

    public richadapter(context context) { 

    public int getviewtypecount() { 

        return 2; 

        if (position % 3 == 0) { 

            return pic; 

            return text; 

        switch (getitemviewtype(position)) { 

        case text: 

            textholder textholder=(textholder) holder; 

            textholder.textview.settext(getitem(position)); 

        case pic: 

            imageholder imageholder=(imageholder) holder; 

            imageholder.imageview.setimageresource(r.drawable.image); 

        view itemview = null; 

            itemview = inflater.inflate(r.layout.item_text, parent, false); 

            holder = new textholder(itemview); 

            itemview = inflater.inflate(r.layout.item_image, parent, false); 

            holder = new imageholder(itemview); 

    private static class textholder extends viewholder { 

        textview textview; 

            textview = (textview) itemview.findviewbyid(r.id.textview); 

    private static class imageholder extends viewholder { 

        imageview imageview; 

        public imageholder(view itemview) { 

            imageview = (imageview) itemview.findviewbyid(r.id.imageview); 

這裡的使用情況跟recyclerview的使用幾乎是一模一樣,唯一不一樣的地方就是多寫了一個getviewtypecount()方法,在listview或者gridview使用baseadapter實作多種類型視圖的時候該方法必須要重寫。

本文作者:佚名

來源:51cto

繼續閱讀