Android 一起來封裝一個簡單易用的Adapter

前言

還記得我初學(xué) Android 沒多久又需要用到 ListView 的時(shí)候還不會寫 Adapter掩幢,結(jié)果我居然硬生生的用 TableLayout 和 LinearLayout 把 ListView 給替代了。現(xiàn)在回過神了想想苛坚,我當(dāng)時(shí)還真是厲害啊妓羊,在另一種意義上胯究。。

其實(shí)要會寫 Adapter躁绸,乃至于封裝一個能提高生產(chǎn)效率的 Adapter裕循,是離不開對 ListView、GridView 到 RecyclerView 等一系列視圖的Item復(fù)用機(jī)制的理解和掌握的净刮。不熟悉的朋友們請移駕
http://blog.csdn.net/lmj623565791/article/details/24333277

正文

我還依稀記得最早是這么寫一個 Adapter 的(為什么是依稀呢剥哑?)

BaseAdapter adapter = new BaseAdapter() {
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public String getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            convertView = inflater.inflate(layoutId, parent, false);
            
            TextView textView = convertView.findViewById(textViewId);
            Button button = convertView.findViewById(buttonId);
            
            String data = getItem(position);
            textView.setText(data);
            button.setOnClickListener(onClickListener);
            
            return convertView;
        }
    };

后來我才意識到,這樣子寫在滑動的時(shí)候會很頻繁的去 inflate淹父,開銷很大株婴,是不合格的寫法,于是改成了下面這樣:

BaseAdapter adapter = new BaseAdapter() {
        @Override
        public int getCount() {
            return list.size();
        }

        @Override
        public String getItem(int position) {
            return list.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if (convertView == null) {
                convertView = inflater.inflate(layoutId, parent, false);

                viewHolder = new ViewHolder();
                viewHolder.textView = convertView.findViewById(textViewId);
                viewHolder.button = convertView.findViewById(buttonId);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            String data = getItem(position);
            viewHolder.textView.setText(data);
            viewHolder.button.setOnClickListener(onButtonClickeListener);

            convertView.setTag(viewHolder);
            convertView.setOnClickListener(onConvertViewClickedListener);
            return convertView;
        }
    };
class ViewHolder {  
        TextView textView;  
        Button button;  
    } 

這樣子寫基本上沒有遇到遇到太大問題,也就最初容易遇到因?yàn)?item 復(fù)用導(dǎo)致的各種混亂困介,在理解了復(fù)用機(jī)制之后其實(shí)很容易避免大审。我也曾經(jīng)見到過將 convertView 傳入 ViewHolder 中,在 viewHolder 里去實(shí)現(xiàn)大部分邏輯的寫法座哩,這里就不一一贅述了徒扶。

現(xiàn)在來想一下,寫一個 Adapter根穷,我們要做些什么姜骡?大致有這些吧:
1.實(shí)現(xiàn) BaseAdapter 的 getCount、getItem屿良、getItemId 這三個方法圈澈;
2.新建一個 ViewHolder 類,當(dāng)中要包含一個 item 中需要操作的所有 view管引;
3.在 BaseAdapter 的 getView 方法中實(shí)現(xiàn) item 復(fù)用士败,處理好 viewHolder 與 convertView 的關(guān)系;
4.處理 item褥伴,設(shè)置數(shù)據(jù)至 view 谅将,添加監(jiān)聽等。

看上去也不太多嘛重慢,就4步饥臂。但是實(shí)際上多寫幾次就很難受了,要是項(xiàng)目中 ListView似踱、GridView 特別多隅熙,估計(jì)能把一只猿活活寫吐了!

因?yàn)?核芽、2囚戚、3的代碼基本上每次都差不多,不同之處大多都在于4轧简。一個復(fù)雜的 ListView 寫下來感覺腰酸背痛手抽筋驰坊,簡單的則感覺不會再愛了,畢竟花在寫4的時(shí)間上好像遠(yuǎn)遠(yuǎn)小于123的重復(fù)工作哮独。久而久之拳芙,我一聽到要做 ListView 就會這樣 --> T_T

直到在一個月黑風(fēng)高的夜晚,我意外得到了一件神器

public class ViewHolder {  
    public static <T extends View> T get(View view, int id) {  
        SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();  
        if (viewHolder == null) {  
            viewHolder = new SparseArray<View>();  
            view.setTag(viewHolder);  
        }  
        View childView = viewHolder.get(id);  
        if (childView == null) {  
            childView = view.findViewById(id);  
            viewHolder.put(id, childView);  
        }  
        return (T) childView;  
    }  
} 

從代碼上看皮璧,這個 ViewHolder 類會往傳入的 view 中存一系列 id-view 的鍵值對作為 tag舟扎,代碼并不復(fù)雜,相信大家都看得懂悴务。
我們再看看這神器到底能干什么

BaseAdapter adapter = new BaseAdapter() {  
        @Override  
        public int getCount() {  
            return list.size();  
        }  
  
        @Override  
        public String getItem(int position) {  
            return list.get(position);  
        }  
  
        @Override  
        public long getItemId(int position) {  
            return position;  
        }  
  
        @Override  
        public View getView(int position, View convertView, ViewGroup parent) {  
            if (convertView == null) {  
                convertView = inflater.inflate(layoutId, parent, false);  
            }  
              
            TextView textView = ViewHolder.get(convertView, textViewId);  
            Button button = ViewHolder.get(convertView, buttonId);  
              
            String data = getItem(position);  
            textView.setText(data);  
            button.setOnClickListener(onButtonClickListener);  
              
            return convertView;  
        }  
    }; 

完全不用專門寫 ViewHolder 類了有木有睹限,對于我這樣的懶人簡直就是福音得意
以上就是神器 ViewHolder 的功效。

難道這是全部了嗎?作為一個懶惰的程序猿邦泄,相信大家肯定和我一樣不滿足于此删窒。放眼上述的代碼,不管是哪個 adapter 都寫了幾個重復(fù)的方法顺囊,那些方法要怎么干掉呢?

其實(shí)蕉拢,一個簡單的基類加上對泛型的支持就可以了

public abstract class ListAdapter<T> extends BaseAdapter {  
  
    protected abstract void setItem(View convertView, T data, int position);  
      
    protected List<T> mData;  
    protected Context mContext;  
    protected LayoutInflater mInflater;  
    protected int mLayoutRes;  
  
    public ListAdapter(Context context, List<T> data, int layoutRes) {  
        this.mData = data;  
        this.mContext = context;  
        this.mLayoutRes = layoutRes;  
          
        this.mInflater = LayoutInflater.from(mContext);  
    }  
  
    /** 
     * 刷新 adapter 
     * 考慮到可能會發(fā)生數(shù)據(jù)完全改變的情況特碳,故提供此方法 
     * @param data 
     */  
    public void refresh(List<T> data) {  
        try {  
            this.mData = data;  
            notifyDataSetChanged();  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  
  
    @Override  
    public int getCount() {  
        return mData.size();  
    }  
  
    @Override  
    public T getItem(int position) {  
        return mData.get(position);  
    }  
  
    @Override  
    public long getItemId(int position) {  
        return position;  
    }  
  
    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        try {  
            if (convertView == null) {  
                convertView = mInflater.inflate(mLayoutRes, null);  
            }  
  
            setItem(convertView, getItem(position), position);  
  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        return convertView;  
    }  
  
    public <V extends View> V getChildView(View view, int id) {  
        return ViewHolder.get(view, id);  
    }  
} 

封裝到此結(jié)束,讓我們看看是怎么使用的吧

BaseAdapter adapter = new ListAdapter<String>(MainActivity.this, list, layoutId) {  
        @Override  
        protected void setItem(View convertView, String data, int position) {  
            TextView textView = getChildView(convertView, textViewId);  
            textView.setText(data);  
              
            getChildView(convertView, buttonId)  
                    .setOnClickListener(onButtonClickListener);  
        }  
    };  

沒仔細(xì)看前面代碼的圍觀群眾:WTF晕换?這就完了午乓?

沒錯,傳入上下文闸准、數(shù)據(jù)集合益愈,還有布局文件,重寫一個 setItem 方法就完了夷家。相比最初的 adapter蒸其,我們只需要專注于布局、數(shù)據(jù)和視圖的綁定库快,已經(jīng)事件的監(jiān)聽摸袁,不需要重寫相同的代碼,不需要寫累贅的 ViewHolder 類义屏,不需要寫八股文一般的 Item 復(fù)用代碼靠汁。

雖然并不完美,也還沒做到極簡闽铐,但一個簡單好用的 Adapter 已經(jīng)初步成型了蝶怔。謝謝各位看官賞臉看到現(xiàn)在。

下一篇文章打算在這次的 ListAdapter 的基礎(chǔ)上封裝 RecyclerView 的 Adapter兄墅,并且提供對 ItemType 的支持方式的參考思路踢星。

項(xiàng)目源碼:https://github.com/neverwoodsS/zy-open

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市察迟,隨后出現(xiàn)的幾起案子斩狱,更是在濱河造成了極大的恐慌,老刑警劉巖扎瓶,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件所踊,死亡現(xiàn)場離奇詭異,居然都是意外死亡概荷,警方通過查閱死者的電腦和手機(jī)秕岛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人继薛,你說我怎么就攤上這事修壕。” “怎么了遏考?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵慈鸠,是天一觀的道長。 經(jīng)常有香客問我灌具,道長青团,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任咖楣,我火速辦了婚禮督笆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诱贿。我一直安慰自己娃肿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布珠十。 她就那樣靜靜地躺著料扰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宵睦。 梳的紋絲不亂的頭發(fā)上记罚,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音壳嚎,去河邊找鬼桐智。 笑死,一個胖子當(dāng)著我的面吹牛烟馅,可吹牛的內(nèi)容都是我干的说庭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼郑趁,長吁一口氣:“原來是場噩夢啊……” “哼刊驴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寡润,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤捆憎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后梭纹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲惰,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年变抽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了础拨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氮块。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖诡宗,靈堂內(nèi)的尸體忽然破棺而出滔蝉,到底是詐尸還是另有隱情,我是刑警寧澤塔沃,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布蝠引,位于F島的核電站,受9級特大地震影響芳悲,放射性物質(zhì)發(fā)生泄漏立肘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一名扛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茧痒,春花似錦肮韧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至区拳,卻和暖如春拘领,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背樱调。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工约素, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人笆凌。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓圣猎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親乞而。 傳聞我的和親對象是個殘疾皇子送悔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容