ListView 編程: 如何最佳化自訂 Adapter

來源:互聯網
上載者:User

使用自訂的 Adapter,需要最佳化,說到底是最佳化我們自訂的適配器類!

再說到底就是最佳化回調方法 getView 方法。


 ListView 編程: Adapter 何方神聖? 部落格中,只是簡單的介紹了如何去自訂一個適配器以及注意事項。


但是.......

如果像 ListView 編程: Adapter 何方神聖? 中的範例程式碼那樣去寫程式的話,那麼估計要被老大BS的,呵呵!


那麼,結合 Google IO 的建議、APIDemo 代碼以及 個人見解,作進一步的最佳化工作。


說明:該部落格使用的布局檔案與 ListView 編程: Adapter 何方神聖? 附錄中一致。


整理代碼,為最佳化做準備


Activity 代碼

package mark.zhang;import java.util.ArrayList;import android.app.ListActivity;import android.os.Bundle;public class FileActivity extends ListActivity {    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);                // 模擬添加數據        ArrayList<String> arrays = new ArrayList<String>();        for(int i=0; i<30; i++) {        arrays.add("" + i);        }        // 執行個體化自訂 Adapter        FileViewAdapter adapter = new FileViewAdapter(this, arrays);        // 設定適配器        setListAdapter(adapter);    }}

FileViewAdapter 代碼

package mark.zhang;import java.util.ArrayList;import android.content.Context;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;public class FileViewAdapter extends BaseAdapter {private LayoutInflater inflater = null;private ArrayList<String> arrays = null;public FileViewAdapter(Context context, ArrayList<String> arrays) {this.arrays = arrays;inflater = LayoutInflater.from(context);}@Overridepublic int getCount() {Log.d("mark", "getCount() is invoked!");// 返回需要顯示的 item 數目// 這次是外界提供的數據,與上次代碼有差異return arrays.size();}@Overridepublic Object getItem(int position) {Log.d("mark", "getItem() is invoked!");return position;}@Overridepublic long getItemId(int position) {Log.d("mark", "getItemId() is invoked!");Log.d("mark", "position = " + position);return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {Log.d("mark", "getView() is invoked!");View v = inflater.inflate(R.layout.custom_fileview, null);((ImageView) v.findViewById(R.id.image_pic)).setImageResource(R.drawable.file);((TextView) v.findViewById(R.id.text_content)).setText("fileName");return v;}}


運行效果:

ok,對代碼的重新整理算是為下面的最佳化做準備。


在接下來的最佳化方案裡,主要是針對 FileViewAdapter 的 getView 方法進行最佳化,其他代碼不變。


說最佳化之前,先搞明白一個問題:getView 方法的三個參數


getView 方法有三個參數,各個參數的含義可以諮詢 SDK API 文檔。


修改代碼(就是修改 Log 輸出):

@Overridepublic View getView(int position, View convertView, ViewGroup parent) {Log.d("mark", "getView() is invoked!" + "position = " + position + ","+ "convertView = " + convertView + "," + "parent = " + parent);View v = inflater.inflate(R.layout.custom_fileview, null);((ImageView) v.findViewById(R.id.image_pic)).setImageResource(R.drawable.file);((TextView) v.findViewById(R.id.text_content)).setText("fileName");return v;}

運行 APP,列印資訊:


可以看出,getView 方法被調用了 16(0-15) 次,不是 30 次。


仔細看看模擬器就可以知道當前顯示的 item 

是 16(下面還有一個顯示不全,應該是 17 個,從下面的列印資訊可以看出) 個.

不是 30 個。


那麼,我們滑動當前視圖中的捲軸,試一試。




可以看出,getView 方法確實是調用了 30 次(0-29)。
你還可以發現,(滾動)顯示的列印資訊中,convertView 不是 null 了。

我們再次將捲軸滾動到頂部,發現列印資訊中 convertView 也不是 null 了。


換句話說:

利用 convertView 這個參數,而不去在 getView 方法中重新建立一個臨時的變數 View 了,

那麼可以減輕虛擬機器(回收)的負擔,從而提高效率。


方案 1_ 最佳化代碼: 使用 convertView 


@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.d("mark", "getView() is invoked!" + "position = " + position + ","
        + "convertView = " + convertView + "," + "parent = " + parent);
    if (convertView == null) {
       convertView = inflater.inflate(R.layout.custom_fileview, null);
    }


    ((ImageView) convertView.findViewById(R.id.image_pic))
        .setImageResource(R.drawable.file);
    ((TextView) convertView.findViewById(R.id.text_content))
        .setText("fileName");

    return convertView;
}

可以看出,只有 convertView == null 為真(上面測試已經說明 convertView 何時為 null),

才去建立 View 對象。

如果你有興趣的話,可以再次運行 APP 可以看出從底部再次滾動到頂部,

反覆幾次,你會發現幾乎很少建立 View 對象,而是重複利用原來已經存在的 View 對象。

下面還有一種方式來最佳化代碼,不說是最好但至少是 even better  (Google 推薦)!

方案 2_ 最佳化代碼:hold 一把

在 android 提供的 APIDemo 中(List14.java)使用了 ViewHolder ,

所以 ViewHolder 不是 android 內建的 api,也不是什麼詭異的東西。


@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.d("mark", "getView() is invoked!" + "position = " + position + ","
        + "convertView = " + convertView + "," + "parent = " + parent);

    ViewHolder vHolder = null;

    if (convertView == null) {
         convertView = inflater.inflate(R.layout.custom_fileview, null);
         // 創建 ViewHodler 對象
         vHolder = new ViewHolder();
         vHolder.pic = (ImageView) convertView.findViewById(R.id.image_pic);
         vHolder.content = (TextView) convertView
             .findViewById(R.id.text_content);
         // 設置 Tag
        convertView.setTag(vHolder);
        } else {
            vHolder = (ViewHolder) convertView.getTag();
       }
      vHolder.pic.setImageResource(R.drawable.file);
      vHolder.content.setText("fileName");
      return convertView;
}

其中 ViewHolder 是 FileViewAdapter 的 一個靜態內部類。

static class ViewHolder {
    TextView content;
    ImageView pic;
}

使用 ViewHolder 的關鍵好處是緩衝了顯示資料的視圖,加快了 UI 的響應速度。


到目前為止,彷彿最佳化工作已經 ok,其實,

還有一個小問題: ImageView 使用的圖片資源需要預先處理。

FileViewAdapter 完整的代碼如下:

package mark.zhang;import java.util.ArrayList;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;public class FileViewAdapter extends BaseAdapter {private LayoutInflater inflater = null;private ArrayList<String> arrays = null;private Bitmap showIcon = null;public FileViewAdapter(Context context, ArrayList<String> arrays) {this.arrays = arrays;inflater = LayoutInflater.from(context);// 處理圖片資源showIcon = BitmapFactory.decodeResource(context.getResources(),R.drawable.file);}@Overridepublic int getCount() {// Log.d("mark", "getCount() is invoked!");// 返回需要顯示的 item 數目// 這次是外界提供的數據,與上次代碼有差異return arrays.size();}@Overridepublic Object getItem(int position) {// Log.d("mark", "getItem() is invoked!");return position;}@Overridepublic long getItemId(int position) {// Log.d("mark", "getItemId() is invoked!");// Log.d("mark", "position = " + position);return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {Log.d("mark", "getView() is invoked!" + "position = " + position + ","+ "convertView = " + convertView + "," + "parent = " + parent);ViewHolder vHolder = null;if (convertView == null) {convertView = inflater.inflate(R.layout.custom_fileview, null);// 創建 ViewHodler 對象vHolder = new ViewHolder();vHolder.pic = (ImageView) convertView.findViewById(R.id.image_pic);vHolder.content = (TextView) convertView.findViewById(R.id.text_content);// 設置 TagconvertView.setTag(vHolder);} else {vHolder = (ViewHolder) convertView.getTag();}// 設置位圖vHolder.pic.setImageBitmap(showIcon);vHolder.content.setText("fileName");return convertView;}static class ViewHolder {TextView content;ImageView pic;}}

最後強調一下,要在布局檔案裡面將 ListView 控制項的屬性設定為:


    android:layout_width="fill_parent"
    android:layout_height="fill_parent"

關於 ListView 的工作原理(Recycler)以及如何進一步最佳化 ListView,可以參考

http://mzh3344258.blog.51cto.com/1823534/889879

Google IO 文檔下載

http://download.csdn.net/detail/androidbluetooth/3783925










聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.