首頁  >  文章  >  Java  >  Android雜談--ListView之BaseAdapter的使用

Android雜談--ListView之BaseAdapter的使用

高洛峰
高洛峰原創
2016-12-13 16:41:251351瀏覽

話說開發用了各種Adapter之後感覺用的最舒服的還是BaseAdapter,儘管使用起來比其他適配器有些麻煩,但是使用它卻能實現很多自己喜歡的列表佈局,比如ListView、GridView、Gallery、Spinner等等。它是直接繼承自接口類Adapter的,使用BaseAdapter時需要重寫很多方法,其中最重要的當屬getView,因為這會涉及到ListView優化等問題,其他的方法可以參考鏈接的文章

BaseAdapter與其他Adapter有些不一樣,其他的Adapter可以直接在其構造方法中進行資料的設置,例如

SimpleAdapter adapter = new SimpleAdapter(this, getData(), R.layout.list_item, new String[]{"img","title","info",newint[]{R.id.img, R.id.title, R.id.info}});

但是在BaseAdapter中需要實現一個繼承自BaseAdapter的類,並且重寫裡面的很多方法,例如

class MyAdapter extends BaseAdapter 
    { 
        private Context context; 
        public MyAdapter(Context context) 
        { 
            this.context = context; 
        } 
        @Override 
        publicint getCount() { 
            // How many items are in the data set represented by this Adapter.(在此适配器中所代表的数据集中的条目数) 
            return0; 
        } 
 
        @Override 
        public Object getItem(int position) { 
            // Get the data item associated with the specified position in the data set.(获取数据集中与指定索引对应的数据项) 
            returnnull; 
        } 
 
        @Override 
        publiclong getItemId(int position) { 
            // Get the row id associated with the specified position in the list.(取在列表中与指定索引对应的行id) 
            return0; 
        } 
 
        @Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
            // Get a View that displays the data at the specified position in the data set. 
            returnnull; 
        } 
         
    }

這裡面沒什麼難度,但這個getView方法必須好好處理,也是最麻煩的

第一種:沒有任何處理,不建議這樣寫。如果資料量少看將就,但是如果清單項目資料量很大的時候,會每次都重新建立View,設定資源,嚴重影響效能,所以從一開始就不要用這種方式

@Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
            View item = mInflater.inflate(R.layout.list_item, null); 
            ImageView img = (ImageView)item.findViewById(R.id.img)  
            TextView title = (TextView)item.findViewById(R.id.title); 
            TextView info = (TextView)item.findViewById(R.id.info); 
            img.setImageResource(R.drawable.ic_launcher); 
            title.setText("Hello"); 
            info.setText("world"); 
             
            return item; 
        }

第二種ListView最佳化:透過快取convertView,這種利用快取contentView的方式可以判斷如果快取中不存在View才建立View,如果已經存在可以利用快取中的View,提升了效能

public View getView(int position, View convertView, ViewGroup parent) { 
            if(convertView == null) 
            { 
                convertView = mInflater.inflate(R.layout.list_item, null); 
            } 
             
            ImageView img = (ImageView)convertView.findViewById(R.id.img)  
            TextView title = (TextView)convertView.findViewById(R.id.title); 
            TextView info = (TextView)ConvertView.findViewById(R.id.info); 
            img.setImageResource(R.drawable.ic_launcher); 
            title.setText("Hello"); 
            info.setText("world"); 
             
            return convertView; 
        }

第三種ListView最佳化:透過convertView +ViewHolder來實現,ViewHolder就是一個靜態類,使用ViewHolder 的關鍵好處是快取了顯示資料的視圖(View),加快了UI 的回應速度。

當我們判斷 convertView == null  的時候,如果為空,就會根據設計好的List的Item佈局(XML),來為convertView賦值,並產生一個viewHolder來綁定converView裡面的各個View控件(XML佈局裡面的那些控制)。再用convertView的setTag將viewHolder設定到Tag中,以便系統第二次繪製ListView時從Tag取出。 (看下面程式碼中)

如果convertView不為空的時候,就會直接用convertView的getTag(),來取得一個ViewHolder。

//在外面先定义,ViewHolder静态类 
    staticclass ViewHolder 
    { 
        public ImageView img; 
        public TextView title; 
        public TextView info; 
    } 
//然后重写getView 
        @Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
            ViewHolder holder; 
            if(convertView == null) 
            { 
                holder = new ViewHolder(); 
                convertView = mInflater.inflate(R.layout.list_item, null); 
                holder.img = (ImageView)item.findViewById(R.id.img)  
                holder.title = (TextView)item.findViewById(R.id.title); 
                holder.info = (TextView)item.findViewById(R.id.info); 
                convertView.setTag(holder); 
            }else 
            { 
                holder = (ViewHolder)convertView.getTag(); 
                holder.img.setImageResource(R.drawable.ic_launcher); 
                holder.title.setText("Hello"); 
                holder.info.setText("World"); 
            } 
             
            return convertView; 
        }

到這裡,可能會有人問ViewHolder靜態類別結合快取convertView與直接使用convertView有什麼區別嗎,是否重複了

在這裡,官方給出了解釋

提升Adapter的兩種方法

To work efficiently the adapter implemented here uses two techniques: -It reuses the convertView passed to getView() to avoid inflating View when it is not necessary

)

(:重用快取式快取式的方法-It uses the ViewHolder pattern to avoid calling findViewById() when it is not necessary

(譯:使用ViewHolder模式來避免沒有必要的調用findViewById():因為太多的findViewById也會影響性能) The ViewHolder pattern consists in storing a data structure in the tag of the view returned by getView().This data structures contains references to the views we want to bind data to, thus avoiding calling to findViewBytime ) to bind.

(譯:ViewHolder模式透過getView()方法傳回的視圖的標籤(Tag)中儲存一個資料結構,這個資料結構包含了指向我們

要綁定資料的視圖的引用,從而避免每次呼叫getView ()的時候呼叫findViewById())

實例一:用BaseAdapter來自訂ListView佈局

main.xml

<?xmlversion="1.0"encoding="utf-8"?> 
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical"> 
 
    <ListView 
        android:id="@+id/lv" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:fastScrollEnabled="true" 
        /> 
 
</LinearLayout>

list_item.xml

<?xmlversion="1.0"encoding="utf-8"?> 
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="horizontal"> 
 
    <ImageView 
        android:id="@+id/img" 
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" 
        /> 
    <LinearLayout  
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:orientation="vertical" 
        > 
        <TextView 
            android:id="@+id/tv" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:textSize="20sp" 
        /> 
        <TextView  
            android:id="@+id/info" 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:textSize="14sp" 
            /> 
    </LinearLayout> 
     
 
</LinearLayout>

Activity

package com.loulijun.demo17; 
 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
 
import android.app.Activity; 
import android.content.Context; 
import android.os.Bundle; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.BaseAdapter; 
import android.widget.ImageView; 
import android.widget.ListView; 
import android.widget.TextView; 
 
publicclass Demo17Activity extends Activity { 
    private ListView lv; 
    private List<Map<String, Object>> data; 
    @Override 
    publicvoid onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        lv = (ListView)findViewById(R.id.lv); 
        //获取将要绑定的数据设置到data中 
        data = getData(); 
        MyAdapter adapter = new MyAdapter(this); 
        lv.setAdapter(adapter); 
    } 
     
    private List<Map<String, Object>> getData() 
    { 
        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(); 
        Map<String, Object> map; 
        for(int i=0;i<10;i++) 
        { 
            map = new HashMap<String, Object>(); 
            map.put("img", R.drawable.ic_launcher); 
            map.put("title", "跆拳道"); 
            map.put("info", "快乐源于生活..."); 
            list.add(map); 
        } 
        return list; 
    } 
     
    //ViewHolder静态类 
    staticclass ViewHolder 
    { 
        public ImageView img; 
        public TextView title; 
        public TextView info; 
    } 
     
    publicclass MyAdapter extends BaseAdapter 
    {     
        private LayoutInflater mInflater = null; 
        private MyAdapter(Context context) 
        { 
            //根据context上下文加载布局,这里的是Demo17Activity本身,即this 
            this.mInflater = LayoutInflater.from(context); 
        } 
 
        @Override 
        publicint getCount() { 
            //How many items are in the data set represented by this Adapter. 
            //在此适配器中所代表的数据集中的条目数 
            return data.size(); 
        } 
 
        @Override 
        public Object getItem(int position) { 
            // Get the data item associated with the specified position in the data set. 
            //获取数据集中与指定索引对应的数据项 
            return position; 
        } 
 
        @Override 
        publiclong getItemId(int position) { 
            //Get the row id associated with the specified position in the list. 
            //获取在列表中与指定索引对应的行id 
            return position; 
        } 
         
        //Get a View that displays the data at the specified position in the data set. 
        //获取一个在数据集中指定索引的视图来显示数据 
        @Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
            ViewHolder holder = null; 
            //如果缓存convertView为空,则需要创建View 
            if(convertView == null) 
            { 
                holder = new ViewHolder(); 
                //根据自定义的Item布局加载布局 
                convertView = mInflater.inflate(R.layout.list_item, null); 
                holder.img = (ImageView)convertView.findViewById(R.id.img); 
                holder.title = (TextView)convertView.findViewById(R.id.tv); 
                holder.info = (TextView)convertView.findViewById(R.id.info); 
                //将设置好的布局保存到缓存中,并将其设置在Tag里,以便后面方便取出Tag 
                convertView.setTag(holder); 
            }else 
            { 
                holder = (ViewHolder)convertView.getTag(); 
            } 
            holder.img.setBackgroundResource((Integer)data.get(position).get("img")); 
            holder.title.setText((String)data.get(position).get("title")); 
            holder.info.setText((String)data.get(position).get("info")); 
             
            return convertView; 
        } 
         
    } 
}

reee Gallery上應用BaseAdapter

main.xmlAndroid雜談--ListView之BaseAdapter的使用

<?xmlversion="1.0"encoding="utf-8"?> 
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical"> 
     
    <ImageView  
        android:id="@+id/img" 
        android:layout_width="480px" 
        android:layout_height="480px" 
        android:layout_gravity="center" 
        /> 
    <Gallery  
        android:id="@+id/gallery" 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:spacing="3dp" 
        android:layout_gravity="bottom" 
        /> 
 
</LinearLayout>

Activity:這部分裡的getView沒有優化,調試了很久還沒調通,暫時還是用的最基本的方法。會特別找個時間把Gallery內存洩漏的部分寫一下,因為圖片資源很多的時候會引起out of memory的錯誤

package com.loulijun.demo16; 
 
import android.app.Activity; 
import android.content.Context; 
import android.os.Bundle; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.AdapterView; 
import android.widget.BaseAdapter; 
import android.widget.Gallery; 
import android.widget.ImageView; 
 
publicclass Demo16Activity extends Activity { 
    private Gallery mGallery; 
    private ImageView mImg; 
    //图片数组 
    privateint[] pics = { 
            R.drawable.pic1, 
            R.drawable.pic2, 
            R.drawable.pic3, 
            R.drawable.pic4, 
            R.drawable.pic5, 
            R.drawable.pic6 
    }; 
    @Override 
    publicvoid onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        mImg = (ImageView)findViewById(R.id.img); 
        mGallery = (Gallery)findViewById(R.id.gallery); 
        MyAdapter adapter = new MyAdapter(this); 
        mGallery.setAdapter(adapter); 
        mGallery.setOnItemClickListener(new Gallery.OnItemClickListener() 
        { 
 
            @Override 
            publicvoid onItemClick(AdapterView<?> adapter, View view, int position, 
                    long arg3) { 
                mImg.setImageResource(pics[position]); 
            } 
             
        }); 
    } 
     
    //内部类 
    class MyAdapter extends BaseAdapter 
    { 
        //用来接收传递过来的Context上下文对象 
        private Context context; 
 
        //构造函数 
        public MyAdapter(Context context) 
        { 
            this.context = context; 
        } 
        @Override 
        publicint getCount() { 
            //返回图片数组大小 
            return pics.length; 
        } 
 
        @Override 
        public Object getItem(int position) { 
            //根据选中项返回索引位置 
            return position; 
        } 
 
        @Override 
        publiclong getItemId(int position) { 
            //根据选中项id返回索引位置 
            return position; 
        } 
        //未优化的getView,这部分可以使用recycle()释放内存、或者BitmapFacotry.Options缩小,或者软引用,或者控制图片资源大小等等很多方法,找时间专门写 
        @Override 
        public View getView(int position, View convertView, ViewGroup parent) { 
            ImageView img = new ImageView(context); 
            img.setAdjustViewBounds(true); 
            img.setImageResource(pics[position]); 
            img.setScaleType(ImageView.ScaleType.FIT_XY); 
            img.setLayoutParams(new Gallery.LayoutParams(120,120)); 
             
            return img; 
        }     
    } 
}

運行效果:原理都是一樣,只不過是佈局加載的時候會有區別,不過就這個小差別也讓人夠惱火的了

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn