shell google 云计算 Android centos mysql wordpress 编程 开源 linux Ubuntu java php 程序员 Firefox 微软 Windows apache nginx Python

高性能ListViews

原文鏈接 : Performance ListViews
原文作者 : Brandon
譯文出自 : 開發技術前線 www.devtf.cn。未經允許,不得轉載!
譯者 : liuling07
校對者: desmond1121
狀態 : 完成

譯文連接:高性能ListViews

列 表展示功能幾乎在所有app中都會被用到,使用列表可以很方便的展示一些列表項,比如菜譜、聯系人,或者任意類型的類別。所以Android有一個內置的 方式來展示此類型的數據,也是在情理之中的。RecyclerView是一種最新的展示列表數據的方式,它非常高效,因為它重用視圖而不是每一行出現在屏 幕上都重新創建。在RecyclerView出現之前,我們可以使用ListView,即使到了現在,ListView也是廣泛的被開發者所使用。雖然 ListView也是可以回收視圖的,但它也一直都是Android中最容易被錯誤使用的一個控件。我們知道在此之前這個話題已經被寫過無數遍了,但是今 天我還是要在博客中提出來,因為我們仍然發現很多app在錯誤的使用它們。

關於ListView中ArrayAdapter的用法,標準的新手寫法是這樣子的:

@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.view_test_row, parent, false);
 
    TextView testName = (TextView)rowView.findViewById(R.id.text_view_test_name);
    TextView testDesc = (TextView)rowView.findViewById(R.id.text_view_test_desc);
 
    //modify TextViews, in some arbitrary way
 
    return rowView;
}

當所有列表項都能夠一次性在一屏中顯示的時候,這種寫法並沒有什麽問題,但這樣你就創建了一個基本視圖,並完全避免了ArrayAdapter的麻 煩了嗎?當ListView需要顯示一個很大的列表集,而且列表子項是一個非常復雜的視圖的時候,上面的方式會消耗大量的性能。當用戶滑動屏幕的時候,每 個視圖都會被inflate並且調用findViewById()方法。當findViewById()方法被調用的時候,會遍歷整個視圖層級,直到找到 正確的Id。每個子視圖都要執行上述過程!並且用戶滑動的越快,卡頓現象愈加明顯。為了解決這個問題,我們可以使用一個靜態類來綁定還沒被使用的 convertView。

static class ViewHolder(){
 
        TextView testName;
        TextView testDesc;
 
}
 
@Override
 public View getView(int position, View convertView, ViewGroup parent) {
 
    View rowView = convertView;  //reference to one of the previous Views in the list that we can reuse.
 
    if(convertView == null) {
 
        LayoutInflater inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        rowView = inflater.inflate(R.layout.view_test_row, parent, false);
 
        ViewHolder viewHolder = new ViewHolder();
        viewHolder.testName = (TextView) rowView.findViewById(R.id.text_view_test_name);
        viewHolder.testDesc = (TextView) rowView.findViewById(R.id.text_view_test_desc);
 
        rowView.setTag(viewHolder);
    }
 
    ViewHolder holder = (ViewHolder) rowView.getTag();
 
    //in real code these strings should be in res
    holder.testName.setText("test"+position);
    holder.testDesc.setText("This is number "+position);
 
    return rowView;
}

那convertView又是什麽呢?它可以讓ListView跳過一些顯示一行內容所需要的設置。如果某一行的視圖不在屏幕中顯示,我們可以重復 使用這個視圖來顯示一個新行。當ListView剛開始顯示的時候,一切都是正常的。既然沒有視圖可以被用來復用,convertView為空。視圖也像 前面版本一樣被inflate,但是TextViews會被找到且它的引用被保存在一個ViewHolder中。然後我們可以調用setTag()方法將 ViewHolder存儲在視圖中。正如修訂過後的getView()方法中後半段代碼所示,我們可以在視圖中存儲後面我們需要用到的數據。

我們所做的更改可能看起來並沒有太大的效果,但是隨著布局越來越復雜並且數量也越來越多,效果將變得越來越明顯。作為開發者,我最不想做的事就是開 發一個用戶體驗很差的app。所以請記住,僅僅一個低水平的ListView都有可能讓一個app死掉,我們一定得避免這種情況發生。

延伸阅读

    评论