日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

代码优化Android ListView适配器三级优化详解

發(fā)布時(shí)間:2024/8/26 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 代码优化Android ListView适配器三级优化详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

轉(zhuǎn)載本專欄每一篇博客請(qǐng)注明轉(zhuǎn)載出處地址,尊重原創(chuàng)。此博客轉(zhuǎn)載鏈接地址:點(diǎn)擊打開(kāi)鏈接? http://blog.csdn.net/qq_32059827/article/details/52718489

對(duì)ListView的優(yōu)化,也就是對(duì)其封裝:抽取方法共性,封裝 BaseAdapter 和 ViewHolder

大多App都會(huì)使用到的基本控件 ——- Listiew,特別像新聞瀏覽類的比如說(shuō)“今日關(guān)注”,或者“應(yīng)用寶”這種匯集手機(jī)軟件集合的。而且大家都知道 需要給每個(gè)單獨(dú)的 ListView 搭配相應(yīng)的適配器 Adapter 。如果你的項(xiàng)目中使用ListView 的頻率很少甚至沒(méi)有,那我不建議你對(duì) ListView 進(jìn)行抽取封裝,但是!如果它的使用滲透到App中大多頁(yè)面時(shí),你必須考慮 對(duì)Adapter的公共方法進(jìn)行抽取封裝到單獨(dú)的類中,來(lái)避免整個(gè)項(xiàng)目代碼的冗雜,使代碼結(jié)構(gòu)規(guī)范化

首先,我們?cè)趯?xiě)ListView的時(shí)候一般會(huì)這么寫(xiě):

一. 未封裝版 ListView@Override public View onCreateSuccessView() {ListView view = new ListView(UIUtils.getContext());initData();view.setAdapter(new MyListViewAdapter());return view; }private void initData() {for (int i = 0; i < 50; i++) {mList.add("測(cè)試數(shù)據(jù)" + i);} }class MyListViewAdapterextends BaseAdapter {@Overridepublic int getCount() {return mList.size();}@Overridepublic String getItem(int position) {return mList.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;if (convertView == null) {convertView = View.inflate(UIUtils.getContext(),R.layout.list_item_home, null);holder = new ViewHolder();holder.mListTextView= (TextView) convertView.findViewById(R.id.tv_content);convertView.setTag(holder);} else {holder = (ViewHolder) convertView.getTag();}holder.mListTextView.setText(getItem(position));return convertView;} }static class ViewHolder {public TextView mListTextView; }

這里我們主要 把重點(diǎn)放在 AdapterViewHolder 的抽取封裝,首先簡(jiǎn)單分析其邏輯組成

1.首先看到我自定義的 HomeAdapter 繼承的是 BaseAdapter 。繼承的四個(gè)方法中,前三個(gè):getCountgetItemgetItemId 看的出來(lái)方法及其簡(jiǎn)單,

但是getView方法中步驟略復(fù)雜,首先梳理清楚方法里的邏輯,才好進(jìn)一步的封裝:
(1)加載布局文件,布局轉(zhuǎn)換(xml —> view)
(2)初始化控件(finViewById)
(3)給ViewHolder設(shè)置標(biāo)記(setTag),便于下次復(fù)用
(4)給控件設(shè)置數(shù)據(jù)

那么就開(kāi)始對(duì)市面上60%的項(xiàng)目中的封裝方法,進(jìn)行普通封裝。

封裝一》》》普通封裝:方式,保留getView(),getCountgetItemgetItemId 先封裝。

方法:抽取類名:MyBaseAdapter。基類是需要一個(gè)數(shù)據(jù)源的,因此通過(guò)構(gòu)造方法得到這個(gè)數(shù)據(jù)源;注意:數(shù)據(jù)源是什么類型,通過(guò)泛型指定。

public class MyBaseAdapter<T> extends BaseAdapter {//在類旁邊聲明一下泛型private ArrayList<T> mList;/*** 我需要一個(gè)集合,那么就由孩子傳遞過(guò)來(lái)。但是孩子這么多,集合可以有好多類型,那么集合到底寫(xiě)什么類型?泛型更好用,孩子什么類型,我就什么類型*/public MyBaseAdapter(ArrayList<T> list){this.mList = list;}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn mList.size();}@Overridepublic T getItem(int position) {// TODO Auto-generated method stubreturn mList.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubreturn null;}}

因?yàn)槌槿×艘恍┓椒?#xff0c;我們?cè)瓉?lái)的Adapter類繼承自MyBaseAdapter的代碼就簡(jiǎn)單了一些,如下:

private class MyListViewAdapter extends MyBaseAdapter<String>{//在這里告知父親,我繼承你我要給你的集合傳遞什么類型//通過(guò)構(gòu)造函數(shù),把孩子的集合傳給父親public MyListViewAdapter(ArrayList<String> list) {super(list);}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// 自定義listview界面ViewHolder holder = null;if(convertView == null){holder = new ViewHolder();//1、加載item布局convertView = UIUtils.inflate(R.layout.homefragment_listview_item);//2、獲取item布局實(shí)例holder.mListTextView = (TextView) convertView.findViewById(R.id.tv_listview_item_text);//3、viewholder設(shè)置到tagconvertView.setTag(holder);}else{//若不為空,在緩存對(duì)象里取出holder = (ViewHolder) convertView.getTag();}//4、設(shè)置listview布局上的控件數(shù)據(jù)holder.mListTextView.setText(getItem(position));return convertView;}}
同時(shí),原來(lái)實(shí)例化適配器代碼也要稍作修改,傳遞數(shù)據(jù)源到基類里面:

//獲取適配器對(duì)象 MyListViewAdapter listAdapter = new MyListViewAdapter(mListDatas);

現(xiàn)在運(yùn)行程序,跟以前的效果是一樣的。如下:

到目前為止,叫做是普通抽取,也就是60%應(yīng)用可能是這么做的,對(duì)于每個(gè)子類的getView()都是自己實(shí)現(xiàn)自己的。

但是要想再高大上一些,要想根那些牛逼點(diǎn)的app一樣,就要抽取getView(),對(duì)于這一點(diǎn),比較復(fù)雜。接下來(lái)就一點(diǎn)點(diǎn)的演變整個(gè)過(guò)程。

在演變之前,要對(duì)ListView的加載流程幾個(gè)問(wèn)題要清楚,只有理解了ListView的加載流程,才能更好的而理解抽取getView()的思想。

HomeHolder抽取前思考問(wèn)

  • 為什么想到去抽取?
  • convertView的作用 ?
  • ViewHolder是什么 ?
  • ViewHolder的作用 ?
  • ViewHolder里面需要持有什么對(duì)象?
  • 做ViewHolder需要什么條件?
  • 一個(gè)ListView會(huì)創(chuàng)建幾個(gè)convertView以及幾個(gè)ViewHolder的思考?
  • getView其實(shí)主要是做啥?
  • HomeHolder抽取前思考答

  • 為什么想到去抽取?

  • 總是要?jiǎng)?chuàng)建ViewHolder
  • 有重復(fù)代碼
  • convertView的作用 ?

  • 減少view對(duì)象的創(chuàng)建
  • ViewHolder是什么 ?
  • 就是一個(gè)普通的類,成員變量有根布局里面的孩子對(duì)象.
  • ViewHolder的作用 ?
  • 減少孩子對(duì)象的創(chuàng)建,減少findViewById的調(diào)用
  • ViewHolder里面需要持有什么對(duì)象?
  • 持有根布局里面的孩子對(duì)象
  • 做ViewHolder需要什么條件?
  • 一個(gè)類持有根布局里面的孩子對(duì)象即可
  • 其實(shí)只要能有根布局就可以了,有根布局就有了對(duì)應(yīng)的孩子.孩子無(wú)非就是調(diào)用根布局的findViewById初始化即可
  • 一個(gè)ListView會(huì)創(chuàng)建幾個(gè)convertView以及幾個(gè)ViewHolder的思考
  • 如果一個(gè)屏幕正好顯示6個(gè)itemView那么會(huì)創(chuàng)建6+1個(gè)convertView和6+1個(gè)ViewHolder
  • getView其實(shí)主要是做啥?
  • 決定根布局
  • 得到數(shù)據(jù)
  • 填充數(shù)據(jù)
  • 相信您已經(jīng)理解了LitView的加載流程,就看一下第一次演變:

    對(duì)ViewHolder抽取,定義為HomeHolder。

    getView()有兩層:視圖層V、數(shù)據(jù)源模型層M。其實(shí)屬于典型的MVC。抽取的過(guò)程,也按照MVC模式

    整體的步驟,都詳細(xì)的注釋在代碼中了,一目了然:

    public class HomeHolder {//根據(jù)getView的設(shè)置過(guò)程來(lái)寫(xiě)這里的代碼//getView()過(guò)程://0、初始化ViewHolder//1、初始化布局//2、viewholder設(shè)置到根布局的tag。這里根布局給了mHolderView【viewHolder可以綁定tag的條件:該viewHolder要有孩子的布局,或者所有根布局的控件實(shí)例】//3、初始化孩子對(duì)象,即item布局上的控件實(shí)例//4、給孩子(item的布局控件實(shí)例)設(shè)置數(shù)據(jù)/*******************初始化視圖************************/public View mHolderView;//條件:做Holder需要有孩子對(duì)象。該viewHolder要有孩子所有根布局的控件實(shí)例,該布局只有一個(gè)控件,就是展示文本TextView mListTextView;private String mData;//0、初始化ViewHolderpublic HomeHolder(){/**可見(jiàn),構(gòu)造方法一調(diào)用,初始化了viewholder、初始化了布局、布局控件實(shí)例化、此viewHolder綁定到了當(dāng)前布局的tag中**/mHolderView = initView();//2、viewholder設(shè)置到根布局的tag。這里根布局給了mHolderViewmHolderView.setTag(this);//this指當(dāng)前的viewHolder}//1、初始化布局private View initView() {//return UIUtils.inflate(R.layout.homefragment_listview_item);View view = UIUtils.inflate(R.layout.homefragment_listview_item);//3、初始化孩子對(duì)象,即item布局上的控件實(shí)例mListTextView = (TextView) view.findViewById(R.id.tv_listview_item_text);return view;}/***********************初始化數(shù)據(jù)***********************///4、給孩子(item的布局控件實(shí)例)設(shè)置數(shù)據(jù)public void setDataAndRefreshHolderView(String data) {//保存數(shù)據(jù)this.mData = data;//刷新顯示,設(shè)置數(shù)據(jù)refreshHolderView(data);}private void refreshHolderView(String data) {// 刷新數(shù)據(jù)顯示mListTextView.setText(data);} }
    經(jīng)過(guò)抽取ViewHolder后,再看一看getView()代碼怎么寫(xiě):

    @Overridepublic View getView(int position, View convertView, ViewGroup parent) {/**********************初始化視圖 決定根布局***********************/HomeHolder holder = null;if(convertView == null){holder = new HomeHolder();}else{holder = (HomeHolder) convertView.getTag();}/**********************數(shù)據(jù)刷新顯示***********************/holder.setDataAndRefreshHolderView(getItem(position));return holder.mHolderView;}}
    可見(jiàn),getView()明顯的少了好多代碼,一下子變得輕松了許多。運(yùn)行程序,結(jié)果一模一樣。

    這個(gè)時(shí)候,明顯輕松了很多,但是上邊的抽取僅僅是針對(duì)HomeHolder的,如果頁(yè)面很多的話,顯然要寫(xiě)很多的Holder類。為了節(jié)省Holder的代碼,在網(wǎng)上抽取。

    》》》》HomeHolder抽取成BaseHolder。該類的抽取,是基于上邊HomeHolder做修改的。把覺(jué)得基類取不到的具體東西定義為抽象方法。例如:加載具體的布局、設(shè)置刷新具體的數(shù)據(jù)。在基類里面都無(wú)法得知,交給子類實(shí)現(xiàn)。并且,由于成了基類,所以具體的設(shè)置的數(shù)據(jù)類型也不清楚,所以設(shè)置傳遞數(shù)據(jù)的時(shí)候,使用泛型

    public abstract class BaseHolder<T> {//根據(jù)getView的設(shè)置過(guò)程來(lái)寫(xiě)這里的代碼//getView()過(guò)程://0、初始化ViewHolder//1、初始化布局//2、viewholder設(shè)置到根布局的tag。這里根布局給了mHolderView【viewHolder可以綁定tag的條件:該viewHolder要有孩子的布局,或者所有根布局的控件實(shí)例】//3、初始化孩子對(duì)象,即item布局上的控件實(shí)例//4、給孩子(item的布局控件實(shí)例)設(shè)置數(shù)據(jù)/*******************初始化視圖************************/public View mHolderView;//條件:做Holder需要有孩子對(duì)象。該viewHolder要有孩子所有根布局的控件實(shí)例,該布局只有一個(gè)控件,就是展示文本//TextView mListTextView;基類不知道孩子對(duì)象private T mData;//0、初始化ViewHolderpublic BaseHolder(){/**可見(jiàn),構(gòu)造方法一調(diào)用,初始化了viewholder、初始化了布局、布局控件實(shí)例化、此viewHolder綁定到了當(dāng)前布局的tag中**/mHolderView = initHolderView();//2、viewholder設(shè)置到根布局的tag。因?yàn)槟塬@取item的根布局也可以設(shè)置tagmHolderView.setTag(this);//this指當(dāng)前的viewHolder}/*** 初始化holderView/根視圖* @call BaseHolder初始化的時(shí)候調(diào)用* @return*///1、初始化布局public abstract View initHolderView();/***********************初始化數(shù)據(jù)***********************//*** 設(shè)置數(shù)據(jù)和刷新視圖* @call 需要設(shè)置數(shù)據(jù)和刷新數(shù)據(jù)的時(shí)候調(diào)用* @param data*///4、給孩子(item的布局控件實(shí)例)設(shè)置數(shù)據(jù)public void setDataAndRefreshHolderView(T data) {//只有HomeHolder子類是String類型,其他的孩子不一定也是String,有可能還是bean等。因此使用泛型//保存數(shù)據(jù)this.mData = data;//刷新顯示,設(shè)置數(shù)據(jù)refreshHolderView(data);}/*** 刷新Holder視圖* @call setDataAndRefreshHolderView(T data) 調(diào)用的時(shí)候就被調(diào)用了吧* 具體的布局我不知道,布局實(shí)例更不可能知道。定義為抽象* @param data*/public abstract void refreshHolderView(T data);}此時(shí),HomeHolder繼承自BaseHolder。看一下HomeHolder的代碼簡(jiǎn)單了多少吧!

    public class HomeHolder extends BaseHolder<String> {private TextView mListTextView;@Overridepublic View initHolderView() {View view = UIUtils.inflate(R.layout.homefragment_listview_item);mListTextView = (TextView) view.findViewById(R.id.tv_listview_item_text);return view;}@Overridepublic void refreshHolderView(String data) {// 刷新數(shù)據(jù)顯示mListTextView.setText(data);} }只需要實(shí)現(xiàn)兩個(gè)方法,就搞定了。運(yùn)行程序,還是跟原來(lái)一樣的效果。

    那么最后,再回到getView()所在的MyListViewAdapter適配器類。當(dāng)然記得,它也是有父類的MyBaseAdapter。

    父類里面還有一個(gè)getView()沒(méi)去寫(xiě)代碼,最后就去搞一搞父親的這個(gè)方法,讓兩個(gè)基類MyBaseAdapter和BaseHolder打招呼整合一下。把子類的getView()放到基類里面去

    在基類里面做如下修改(與BaseHolder建立了連接)

    public abstract class MyBaseAdapter<T> extends BaseAdapter {private ArrayList<T> mList;public MyBaseAdapter(ArrayList<T> list){this.mList = list;}@Overridepublic int getCount() {// TODO Auto-generated method stubreturn mList.size();}@Overridepublic T getItem(int position) {// TODO Auto-generated method stubreturn mList.get(position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {/**********************初始化視圖 決定根布局***********************///基類根基類交談,不能使用具體的實(shí)現(xiàn)類BaseHolder<T> holder = null;if(convertView == null){//該holder應(yīng)該是具體哪個(gè)Holder不清楚。如:HomeHolder、AppHolder等。因此定義抽象方法獲取holder = getSpecialHolder();}else{holder = (BaseHolder) convertView.getTag();//注意:從緩存里面取tag}/**********************數(shù)據(jù)刷新顯示***********************/holder.setDataAndRefreshHolderView(getItem(position));return holder.mHolderView;}/*** 返回具體的BaseHolder的子類* @call getView()方法中,如果沒(méi)有converView的時(shí)候被創(chuàng)建* @return*/public abstract BaseHolder<T> getSpecialHolder();}
    Adapter的基類抽象方法,且在獲取Holder對(duì)象時(shí),調(diào)用子類具體哪個(gè)Holder,只需要在子類中實(shí)現(xiàn)這個(gè)方法,并且返回具體的哪個(gè)Holder就好了。代碼如下:

    private class MyListViewAdapter extends MyBaseAdapter<String>{//在這里告知父親,我繼承你我要給你的集合傳遞什么類型//通過(guò)構(gòu)造函數(shù),把孩子的集合傳給父親public MyListViewAdapter(ArrayList<String> list) {super(list);}@Overridepublic BaseHolder<String> getSpecialHolder() {// TODO Auto-generated method stubreturn new HomeHolder();}}這個(gè)時(shí)候,您發(fā)現(xiàn)getView方法的代碼以及適配器里面的代碼少了太多太多了。而且運(yùn)行結(jié)果還是一樣的。

    可能僅僅一個(gè)Adapter看不出有多強(qiáng)大,如果說(shuō)有100個(gè)類需要適配器的話,那么只需要一個(gè)基類,其他的只要繼承自基類,每個(gè)適配器類里面的方法就上邊那幾行,很顯然,簡(jiǎn)化了大量的冗余的代碼。

    最后再總結(jié)一下此時(shí)的調(diào)用加載流程。

    當(dāng)ListView想要關(guān)聯(lián)適配器的時(shí)候,創(chuàng)建自己的adapter適配器類對(duì)象,同時(shí)把集合數(shù)據(jù)源數(shù)據(jù)傳遞過(guò)去。

    例如此例中的MyListViewAdapter listAdapter = new MyListViewAdapter(mListDatas);這樣把mListDatas傳給了adapter的基類,是通過(guò)帶參構(gòu)造函數(shù)的形式傳給adapter父類的,他把以前孩子自己要寫(xiě)的getCountgetItemgetItemId方法幫孩子完成,孩子簡(jiǎn)寫(xiě)三大方法。同時(shí),在ListVIew的item加載布局和數(shù)據(jù)的時(shí)候,getView方法被調(diào)用,此時(shí)的getView()方法在adapter基類里面呢,對(duì)于item的布局的顯示和布局實(shí)例的數(shù)據(jù)刷新,都封裝在了對(duì)應(yīng)的Holder類里面;先執(zhí)行holder = getSpecialHolder();方法來(lái)看該adapter配套的Holder是誰(shuí)。馬上調(diào)用該adapter的實(shí)現(xiàn)方法

    ??????? @Override
    ?? ??? ?public BaseHolder<String> getSpecialHolder() {
    ?? ??? ??? ?// TODO Auto-generated method stub
    ?? ??? ??? ?return new HomeHolder();
    ?? ??? ?}

    看到返回的是HomeHolder,在new HomeHolder();的同時(shí)BaseHolder的構(gòu)造也會(huì)被加載(子類初始化先初識(shí)化父類構(gòu)造),此時(shí)父類的構(gòu)造函數(shù)

    ?????????? public BaseHolder(){/**可見(jiàn),構(gòu)造方法一調(diào)用,初始化了viewholder、初始化了布局、布局控件實(shí)例化、此viewHolder綁定到了當(dāng)前布局的tag中**/
    ?? ??? ??? ?mHolderView = initHolderView();
    ?? ??? ??? ?//2、viewholder設(shè)置到根布局的tag。因?yàn)槟塬@取item的根布局也可以設(shè)置tag
    ?? ??? ??? ?mHolderView.setTag(this);//this指當(dāng)前的viewHolder
    ?? ??? ?}

    加載了布局、設(shè)置了tag。注意此時(shí)public abstract View initHolderView();是調(diào)用對(duì)應(yīng)子類實(shí)現(xiàn)類HomeHolder的具體方法(調(diào)用父類抽象實(shí)際調(diào)用子類實(shí)現(xiàn)方法)。

    此時(shí)HomeHolder中的
    @Override
    ?? ?public View initHolderView() {
    ?? ??? ?View view = UIUtils.inflate(R.layout.homefragment_listview_item);
    ?? ??? ?mListTextView = (TextView) view
    ?? ??? ??? ??? ?.findViewById(R.id.tv_listview_item_text);
    ?? ??? ?return view;
    ?? ?}

    被調(diào)用,終于看到在哪里初始化布局了!

    這一系列方法走完之后,再回到adapter基類的getView()方法,繼續(xù)往下執(zhí)行到holder.setDataAndRefreshHolderView(getItem(position));。同上,先調(diào)用holder的
    ?? ??? //4、給孩子(item的布局控件實(shí)例)設(shè)置數(shù)據(jù)
    ?? ??? ?public void setDataAndRefreshHolderView(T data) {//只有HomeHolder子類是String類型,其他的孩子不一定也是String,有可能還是bean等。因此使用泛型
    ?? ??? ??? ?//保存數(shù)據(jù)
    ?? ??? ??? ?this.mData = data;
    ?? ??? ??? ?//刷新顯示,設(shè)置數(shù)據(jù)
    ?? ??? ??? ?refreshHolderView(data);
    ?? ??? ?}

    ?refreshHolderView(data);抽象,調(diào)用實(shí)現(xiàn)類HomeHolder的實(shí)現(xiàn)類方法,完成了數(shù)據(jù)的設(shè)置和刷新。

    到此,此次ListView的優(yōu)化,以及詳細(xì)的解析終于結(jié)束了。

    9點(diǎn)半開(kāi)始寫(xiě)博客,現(xiàn)在0:36,搞了3個(gè)多小時(shí)。由衷佩服自己,看完您記得關(guān)注本博客,或者點(diǎn)贊留下包括意見(jiàn)哦。


    轉(zhuǎn)載于:https://www.cnblogs.com/wanghang/p/6299577.html

    總結(jié)

    以上是生活随笔為你收集整理的代码优化Android ListView适配器三级优化详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。