1. 程式人生 > >Android學習心得第五課

Android學習心得第五課

今天算是遇到了Android學習上面的一塊硬骨頭了,目前算是基本理解了,後面還是要時不時的來回顧一下,溫故而知新,下面就來回顧一下我今天學習的知識, 今天的知識點就一個那就是關於BaseAdapter,
上一節心得,我專門對資料介面卡進行了一個解釋,今天在慕課上看到一個比較好的圖,這裡分享一下。
這裡寫圖片描述
從圖中我們可以看出為什麼需要資料介面卡這麼一個東西,因為資料來源是各種各樣的,但是listview它所接受的格式卻是固定的,這個時候我們就需要資料介面卡這麼一箇中間件,通過資料介面卡來將資料來源轉換成llistview所能夠接受的那種資料。

那麼BaseAdapter究竟希望它能夠幹什麼能,它主要是比較靈活,可以進行各種的改寫,listview有一個快取機制,如下圖:
這裡寫圖片描述


通過這種機制我們可以大大的減輕記憶體的開銷。
下面我就來具體解釋一下關於BaseAdapter的事情。首先在這裡有一個小技巧,就是利用bean物件,將需要的資料先封裝起來。

public class MyBaseAdapter extends BaseAdapter {

    // 對映資料
    List<ItemBean> datalist;
    private long mSumTime;

    /*
     * LayoutInflater這個類它的作用類似於findViewById()。
     * 不同點是LayoutInflater是用來找res/layout/下的xml佈局檔案,並且例項化;
     * 而findViewById()是找xml佈局檔案下的具體widget控制元件(如Button、TextView等)。
     */
private LayoutInflater mLayoutInflater; public MyBaseAdapter(Context context, List<ItemBean> datalist) { this.datalist = datalist; mLayoutInflater = LayoutInflater.from(context); } // 獲取資料量 @Override public int getCount() { // TODO Auto-generated method stub
return datalist.size(); } // 獲取對應ID的對應Item @Override public Object getItem(int position) { // TODO Auto-generated method stub return datalist.get(position); } // 獲取對應的ID @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { TODO Auto-generated method stub //第一種方式(不加任何的優化)---------------------------------------------- //該函式的作用是為了將xml檔案轉換成View,這裡因為不涉及第二個引數,所以第二個引數為空 View view = mLayoutInflater.inflate(R.layout.item, null); // 例項化控制元件 ImageView itemImage = (ImageView) view.findViewById(R.id.iv_image); TextView itemTitle = (TextView) view.findViewById(R.id.tv_title); TextView itemContent = (TextView) view.findViewById(R.id.tv_content); // 去除bean物件 ItemBean bean = datalist.get(position); // 設定控制元件的資料 itemImage.setImageResource(bean.itemImageResId); itemTitle.setText(bean.itemTitle); itemContent.setText(bean.itemContent); return view;

第一種方法的一個巨大的缺點就在於,它每次呼叫getView函式的時候,都要新建一個View物件,這樣的話十分佔用資源,而且也沒有用到ListView的快取機制,這裡我們對其進行一個改進,下面放入getView的程式碼。
(2)第二種方式 利用 ListView 的快取機制進行改進

/*
  * 第二種方式(加入優化)--第一種方法的弊端是每次呼叫getview的時候都要新建一個View物件
  * 這種方法十分佔用記憶體,因為每次都需要新建一個View物件,這裡運用到了listview的快取機制,也就是
  * 該函式的第二個引數convertView,通過使用它可以大大的減少記憶體的開銷 當listview物件非常複雜的時候這種方法十分有效。
 */
         if (convertView == null) {
         // 這種邏輯的意思就是當convertView為空時,說明是第一次呼叫,快取為空,所以將該佈局
        // 轉換為View放入convertView中,當再次呼叫時不用再像第一種方法一樣每次都新建一個view
            convertView = mLayoutInflater.inflate(R.layout.item, null);
         }
              ImageView itemImage = (ImageView) convertView
                                    .findViewById(R.id.iv_image);
              TextView itemTitle = (TextView)convertView
                                    .findViewById(R.id.tv_title);
               TextView itemContent = (TextView) convertView
                                    .findViewById(R.id.tv_content);
               ItemBean bean = datalist.get(position);
               itemImage.setImageResource(bean.itemImageResId);
               itemTitle.setText(bean.itemTitle);
               itemContent.setText(bean.itemContent);
               return convertView;

這裡和上式相比,利用convertView引數來避免了每次重複新建View物件,但是這種物件還沒達到最佳的優化,因為findViewById這個函式每次在執行的時候是需要遍歷整個檢視樹,當檢視樹比較複雜的時候,這個遍歷的時間將會相當的可觀。所以這裡可以用到Google提出的一個新的方法—-ViewHolder。下面我們來看看程式碼。

        /*
         * 第三種方法(最佳版本)這種版本在第二種版本的基礎上進一步的優化了findViewById這一個過程
         * 因為其實每做一次findViewById的時候,都要遍歷整個檢視樹,如果檢視樹比較複雜的話
         * 遍歷的時間將會想當的客觀,所以Google專門給了一種資料結構叫ViewHolder
         * 同樣是運用了快取的機制,將已經遍歷過的主鍵的ID給存起來
         */
         // 獲取納秒時間 更加精確

        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            convertView = mLayoutInflater.inflate(R.layout.item, null);
            holder.image = (ImageView) convertView.findViewById(R.id.iv_image);
            holder.title = (TextView) convertView.findViewById(R.id.tv_title);
            holder.content = (TextView) convertView
                    .findViewById(R.id.tv_content);
            //將Viewholder與convertView通過setTag繫結起來
            convertView.setTag(holder);
        }
        else {
            //getTag()取出關聯的ViewHolder
            //通過ViewHolder物件找到對應的控制元件
            holder = (ViewHolder) convertView.getTag();
        }
        ItemBean bean = datalist.get(position);
        holder.image.setImageResource(bean.itemImageResId);
        holder.title.setText(bean.itemTitle);
        holder.content.setText(bean.itemContent);
        return convertView;

    }

    class ViewHolder {
        public ImageView image;
        public TextView title;
        public TextView content;
    }
}

最後一種方法不僅利用了ListView的快取機制,更是通過ViewHolder來實現資料檢視的快取,避免了多次通過findViewById來尋找控制元件,在今後的程式思路中也應該借用這種思想。
最後對利用ViewHolder來優化BaseAdapter的思路做一下總結。
(1)建立Bean物件,用於封裝資料
(2)在構造方法中初始化用於對映的資料list
(3)建立ViewHolder類,建立佈局對映關係
(4)判斷convertView,為空則建立,並設定tag,如果不為空則通過tag取出對應的ViewHolder
(5)給ViewHolder中的控制元件設定資料