Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> ListView和性能

ListView和性能

編輯:關於Android編程


一談起ListView,我想大家都不陌生。而且最近該控件特別紅,像QQ,人人和新浪客戶端裡都有它的影子。
其實實現ListView非常的簡單。
我想大家都用過各種各樣的控件,比如說一個最簡單的TextView,我們都是在布局文件裡加入TextView標簽,然後在Activity裡通過findViewById(int id)方法得到該對象的引用,最後調用TextView類的setText(CharSequence s)方法設置該控件的值。
同樣,對於ListView,我們先在布局文件裡這樣添加標簽:

[html] 
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:orientation="vertical" > 
 
    <ListView 
        android:id="@+id/mylist" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" > 
    </ListView> 
 
</LinearLayout> 

有了布局文件,然後我們在Activity裡通過findViewById(int id)方法得到ListView對象的引用

[java] 
ListView listView = (ListView) findViewById(R.id.mylist); 

有了控件還不行,我們可以把ListView看作是一個可以伸縮的容器,我們需要往裡添加內容。作為數據傳輸的橋梁,Adapter封裝了所需的數據,通過調用ListView的方法setAdapter(Adapter a)將數據綁定到ListView中,這樣屏幕上就有數據顯示了。

Adapter是一個接口,定義了許多規范。Android提供了實現該接口的一些方便的類,如ArrayAdapter,CursorAdapter。下面以ArrayAdapter類為例講解如何創建一個Adapter。

[java]
String[] values = new String[] { "Android", "iPhone", "WindowsMobile", 
  "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", 
  "Linux", "OS/2" }; 
 
// First paramenter - Context 
// Second parameter - Layout for the row 
// Third parameter - ID of the TextView to which the data is written 
// Forth - the Array of data 
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, 
  android.R.layout.simple_list_item_1, android.R.id.text1, values); 

是不是很簡單?在上面的那個構造方法中,一共有四個參數,第一個參數很簡單,就是一個上下文對象Context,第二個參數是描述每一行的布局,這裡使用的是Android自帶的一個簡單布局,第三個參數是該View的id,最後一個是加入的數組。

上面的ArrayAdapter只能在每一行顯示一些文本信息,如果想豐富一下,比如增加圖片等,就需要繼承該類,實現自己的自定義類。

[java
public class MySimpleArrayAdapter extends ArrayAdapter<String> { 
  private final Context context; 
  private final String[] values; 
 
  public MySimpleArrayAdapter(Context context, String[] values) { 
    super(context, R.layout.rowlayout, values); 
    this.context = context; 
    this.values = values; 
  } 
 
  @Override 
  public View getView(int position, View convertView, ViewGroup parent) { 
    LayoutInflater inflater = (LayoutInflater) context 
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    View rowView = inflater.inflate(R.layout.rowlayout, parent, false); 
    TextView textView = (TextView) rowView.findViewById(R.id.label); 
    ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); 
    textView.setText(values[position]); 
    String s = values[position]; 
    imageView.setImageResource(R.drawable.ok); 
    return rowView; 
  } 
}  

繼承該類最關鍵的就是復寫getView()方法,因為ListView是通過該方法得到視圖然後顯示在屏幕上的。在本方法中,我們自定義了一個XML布局文件,裡面有TextView標簽和ImageView標簽,分別用來顯示文字和圖片信息,這裡是先得到系統服務LayoutInflater,調用該方法的inflate得到該布局的View,最後通過findViewById()方法獲取TextView和ImageView的對象引用,再給它們賦值返回就結束了。


但是本章的討論不是講解如何實現ListView,但是考慮到有些沒有接觸過ListView的同志,就大概寫了一點demo,同時以此例子為引子,指出該方法存在的一些性能問題。

由於通過調用LayoutInflater的inflate方法獲得的View,其實會產生新的對象,創建對象是很耗時和資源的(內存),另外調用getViewById()方法也會相對耗時和耗資源,雖然其強度不如前者。
所以Android決定,如果代表每一行的View不可見(向下滑動,上面的View被遮住了,即為不可見),那麼它將允許getView方法通過convertView復用該View,達到提升性能的目的。
我們先來看下ArrayAdapter是如何進行優化的。

[java]
public View getView(int position, View convertView, ViewGroup parent) { 
       return createViewFromResource(position, convertView, parent, mResource); 
   } 
 
   private View createViewFromResource(int position, View convertView, ViewGroup parent, 
           int resource) { 
       View view; 
       TextView text; 
 
       if (convertView == null) { 
           view = mInflater.inflate(resource, parent, false); 
       } else { 
           view = convertView; 
       } 
 
       try { 
           if (mFieldId == 0) { 
               //  If no custom field is assigned, assume the whole resource is a TextView 
               text = (TextView) view; 
           } else { 
               //  Otherwise, find the TextView field within the layout 
               text = (TextView) view.findViewById(mFieldId); 
           } 
       } catch (ClassCastException e) { 
           Log.e("ArrayAdapter", "You must supply a resource ID for a TextView"); 
           throw new IllegalStateException( 
                   "ArrayAdapter requires the resource ID to be a TextView", e); 
       } 
 
       T item = getItem(position); 
       if (item instanceof CharSequence) { 
           text.setText((CharSequence)item); 
       } else { 
           text.setText(item.toString()); 
       } 
 
       return view; 
   } 

該方法首先判斷傳給該方法的convertView是否為null,如果為null,那麼就調用耗時的inflate方法創建View對象,如果不為空(該convertView是以前inflate過的,只不過被遮住了),就復用該對象,達到了部分優化。
上面之所以說是部分優化,是因為只考慮了優化inflate帶來的負載,而忽略了getViewById()方法引起的性能問題。解決辦法是在自定義Adapter類裡引進靜態內部類ViewHolder,如其名字,該類裡存放我們需要顯示每一行的所有控件,比如TextView,ImageView等。當convertView為空時,我們創建布局文件的View,然後分別得到布局裡的各種控件,再把它們存放在ViewHolder類裡,最後再調用convertView的 setTag(Object o)方法把該類綁定到該類裡。

[java]
public class MyPerformanceArrayAdapter extends ArrayAdapter<String> { 
  private final Activity context; 
  private final String[] names; 
 
  static class ViewHolder { 
    public TextView text; 
    public ImageView image; 
  } 
 
  public MyPerformanceArrayAdapter(Activity context, String[] names) { 
    super(context, R.layout.rowlayout, names); 
    this.context = context; 
    this.names = names; 
  } 
 
  @Override 
  public View getView(int position, View convertView, ViewGroup parent) { 
    View rowView = convertView;  www.2cto.com
    if (rowView == null) { 
      LayoutInflater inflater = context.getLayoutInflater(); 
      rowView = inflater.inflate(R.layout.rowlayout, null); 
      ViewHolder viewHolder = new ViewHolder(); 
      viewHolder.text = (TextView) rowView.findViewById(R.id.TextView01); 
      viewHolder.image = (ImageView) rowView 
          .findViewById(R.id.ImageView01); 
      rowView.setTag(viewHolder); 
    } 
 
    ViewHolder holder = (ViewHolder) rowView.getTag(); 
    String s = names[position]; 
    holder.text.setText(s); 
    if (s.startsWith("Windows7") || s.startsWith("iPhone") 
        || s.startsWith("Solaris")) { 
      holder.image.setImageResource(R.drawable.no); 
    } else { 
      holder.image.setImageResource(R.drawable.ok); 
    } 
 
    return rowView; 
  } 
}  

根據統計信息,這樣的優化設計,比最初的方法效率上要快15%以上。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved