1. 程式人生 > >ListView使用和優化措施

ListView使用和優化措施

1. 基本使用

直接程式碼:

list_view_item.xml

<?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="horizontal">

    <ImageView
        android:id
="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:src="@mipmap/ic_launcher"/>
<TextView android:id="@+id/text" android:layout_margin="10dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_weight="1" android:text="1" android:textSize="20sp"/>
</LinearLayout>

activity_list_view.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.shuidi.listviewtest.ListViewActivity">
<ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </LinearLayout>

ListViewActivity.java

public class ListViewActivity extends AppCompatActivity {

    ListView mListView;
    ListAdapter mAdatper;
    List<String> list;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list_view);
        mListView = (ListView) findViewById(R.id.listview);
        list = getListData();
        mAdatper = new ListViewAdapter(this,list);
        mListView.setAdapter(mAdatper);
    }

    private List<String> getListData(){
        List<String> list = new ArrayList<>();
        list.add("One");
        list.add("Two");
        list.add("Three");
        list.add("Four");
        list.add("Five");
        list.add("Six");
        list.add("Seven");
        list.add("Eight");
        return list;
    }

    private class ListViewAdapter extends BaseAdapter {

        LayoutInflater inflater;
        List<String> list;


        public ListViewAdapter(Context context,List<String> stringList){
            inflater = LayoutInflater.from(context);
            list = stringList;
        }

        @Override
        public Object getItem(int i) {
            return null;
        }

        @Override
        public long getItemId(int i) {
            return 0;
        }

        @Override
        public int getCount() {
            return list==null? 0:list.size();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            View view;
            if (convertView==null) {
                view = inflater.inflate(R.layout.list_view_item,null);
                TextView textView = (TextView)view.findViewById(R.id.text);
                viewHolder = new ViewHolder();
                viewHolder.tx = textView;
                view.setTag(textView);
            } else {
                view = convertView;
                viewHolder = (ViewHolder)view.getTag();
            }
            viewHolder.tx.setText(list.get(position));
            return view;
        }
    }

    private static class ViewHolder{
        public TextView tx;
    }

}

2. 優化措施

2.1 複用ConvertView

2.2 使用View Holder模式

2.3 分批載入與分頁載入相結合

我們需要進行分批載入,比如說1000條新聞的List集合,我們一次載入20條,等到使用者翻頁到底部的時候,我們再新增下面的20條到List中,再使用Adapter重新整理ListView,這樣使用者一次只需要等待20條資料的傳輸時間,不需要一次等待好幾分鐘把資料都載入完再在ListView上顯示。其次這樣也可以緩解很多條新聞一次載入進行產生OOM應用崩潰的情況。

實際上,分批載入也不能完全解決問題,因為雖然我們在分批中一次只增加20條資料到List集合中,然後再重新整理到ListView中去,假如有10萬條資料,如果我們順利讀到最後這個List集合中還是會累積海量條數的資料,還是可能會造成OOM的情況,這時候我們就需要用到分頁,比如說我們將這10萬條資料分為1000頁,每一頁100條資料,每一頁載入時都覆蓋掉上一頁中List集合中的內容,然後每一頁內再使用分批載入,這樣使用者的體驗就會相對好一些。

文章 已經說明了如何進行分批載入, 可以在這個基礎之上再實現分頁載入.

2.4 將ListView的scrollingCache和animateCache屬性設定為false(預設是true)

3. 設計模式

3.1. Adapter模式

ListView充當Client角色,介面Adapter充當Target,BaseAdapter充當Adapter,而ListView的資料物件就是Adaptee。
Adapter介面抽象出ListView需要的介面getItem,getCount,getView。
BaseAdapter子類中實現的Adapter介面,其實是對資料物件的操作,也就是對Adaptee依賴關係。

3.2. Observer模式

當ListView的資料發生變化時,呼叫Adapter的notifyDataSetChanged函式,這個函式又會呼叫DataSetObservable的notifyChanged函式,這個函式會呼叫所有觀察者 (AdapterDataSetObserver) 的onChanged方法。這就是一個觀察者模式!

最後我們再捋一捋,AdapterView中有一個內部類AdapterDataSetObserver,在ListView設定Adapter時會構建一個AdapterDataSetObserver,並且註冊到Adapter中,這個就是一個觀察者。而Adapter中包含一個數據集可觀察者DataSetObservable,在資料數量發生變更時開發者手動呼叫Adapter中notifyDataSetChanged函式,而notifyDataSetChanged實際上會呼叫DataSetObservable的notifyChanged函式,該函式會遍歷所有觀察者的onChanged函式。在AdapterDataSetObserver的onChanged函式中會獲取Adapter中資料集的新數量,然後呼叫ListView的requestLayout()方法重新進行佈局,更新使用者介面。

4. 參考文件

ListView與設計模式: