Table of contents


ListView是android裡一個很常用的元件,但他的使用上並不是那麼直接,而且有許多事項要去注意。

ListView元件被設計時,考慮到ListView主要是要來呈現許多類似的內容列表,但因為要運在手機上,所以 就算是有大量的內容,也不能影響到操作的效能。

基於以上原因,在使用ListView時的API時,有一些潛規則及patterns要遵守,當然,你也可以去hack這個行為,但 通常這樣做的後果,就是比較差的效能,以及可能會破壞一些本來可以跟ListView可以互動良好的機制。

建議在使用ListView前,先閱讀一下Resource中Google IO大會的相關資訊,可以避免掉很常見的陷阱

Item Properties

  • enabled/disabled → make item selectabled/unselectabled
  • choice mode
    1. Single choice mode → getCheckedItemPositions()
    2. Multiple choice mode → getCheckedItemPositions() or getCheckedItemIds()
  • focusabled
    1. true : select whole row
    2. false : select component inside view

ListView的list item預設的情況下,如果被clicked(selected)整個item會變highlight,但是如果list item上有其他的元件,像是button, 就不會觸發到click事件(highlight的效果不見了),如果要讓list item變成clickable,那只需要將會載取fouce的元件(此到中的button)的focusable屬性設定成false,EX:android:focusable=“false” 這樣便可以恢復ListItem預設的click功能

Headers And Footers

  • ListView.addHeaderView() called before setAdapter()
  • ListView.addFooterView() called before setAdapter()

List Selectors

  • Highlight selected item
  • Not Shown in touch mode (no selection in touch mode)
  • Shown behind list items → android:drawSelectOnTop=“true”

Transcript Mode

當content changes的行為。

  1. disabled
  2. normal
  3. alwaysScoll

TBD

Stack Frame

ListItem是由下而上成長

View Recycler

ListView是用來處理大量類似的資料,但是,如果資料量一次是一萬筆、十萬筆、甚至百萬筆時,勢必會造成記憶不足的錯誤,那麼,andoird如何有效的減少記憶體的使用呢? 所有GUI對像ListView這樣需要處理大量資料的元件,大多數的解決方式都是只針對顯示在畫面上的資料建立視覺化的元件,其他的部份不建立,這種方式常被稱View Window (View Part)

比方說,畫面上一次最多只能顯示出5個list item,那麼android只需要建立5個相對的view,顯示item 1~5
 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0  
|---------|


當使用者向上捲動時2個單位時,畫面也是只有5個相對的view,顯示item 3~7的內容
 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0  
    |---------|

這樣的方式可以解決記憶體問題,但是如果當畫面從顯示 1~5,捲動到3~7時,1跟2是消失在畫面,而6跟7是出現在畫面的下面,如果只是單純的把1個2的view做destory(),6跟7做view的create() ,那會造成需要大量的建立/消滅物件的動作,這樣會消耗不少cpu資源,所以,android採用回收的機制來避免大量物件建立。

ListView的ListItem有一個特色,就是每個item view大多長得差不多,只是顯示的內容不大一樣,所以,android用一個Recycler來重複使用畫面上的物件。

如果每個一item都是類似的layout,那麼回收再使用相對簡單,但layout不見得每個item都一樣,就要利用ViewType的輔助功能。

典型的BaseAdapter.getView()的實作。

    public class MyAdapter extends BaseAdapter {
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null) {
                convertView = mInflater.inflate(R.layout.item1, null);
                holder = new ViewHolder();
                holder.textView = (TextView)convertView.findViewById(R.id.text);
                convertView.setTag(holder);
            } else {
                holder = (ViewHolder)convertView.getTag();
            }
            holder.textView.setText(mData.get(position));
            return convertView;    
        }
    }

但如果要用在不同layout的listview,那adapter要實作ViewType相關的methods。

  1. getViewTypeCount() 改寫這個method,決定總共會幾種類型的layout
  2. getItemViewType(position) - 改寫這個method,每個layout的id
  3. 改寫getView,利用getViewTypeCount(),getItemViewType(position)決定view的外觀(layout)
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        int type = getItemViewType(position);
        System.out.println("getView " + position + " " + convertView + " type = " + type);
        if (convertView == null) {
            holder = new ViewHolder();
            switch (layoutType) {
                case TYPE_1:
                    convertView = mInflater.inflate(layout1, null);
                    break;
                case TYPE_2:
                    convertView = mInflater.inflate(layout2, null);
                    break;
            }
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder)convertView.getTag();
        }
        holder.textView.setText(mData.get(position));
        return convertView;
    }

不要這樣用android:layout_height=“wrap_content”,因為

  1. ListView每一個children的高度的一致的
  2. Android Framework實際上只會量測前三個children

如果資料量不大,就不要用ListView,ListView算是android widget中比較複雜的元件,資料量少時,可以用LinerLayout加上動態產生view來模擬

Do Not

請不要試著這樣做,這樣做只是拿石頭砸自已的腳,並不會達到你想要的那個效果

  • Local view cache
  • Accessing views from the adapter
  • Change convertView’s structure
  • Assumptions about getView calls

基本上這些都是跟ViewItems回收機制有關,很多行為都讓你驚訝,怎麼跟想像的不一樣。

Resource