Android開發:優化ListView實踐解析
在看了一些vogella的文章之後,發現關於android listview性能優化這一段很有意思,於是實踐了一下,經過優化,性能確實提升不少!
先看看優化前和優化後的比較:
優化前的log截圖:
優化後的log截圖:
並且,在不停滾動ListView的過程中,優化之前會出現ANR現象,在AVD上特別容易復現:
然後,優化後顯得很流暢,附上對於的log截圖:
下面附上相關代碼分析:
ListView中的每一個Item由一個ImageView 和一個TextView組成
Layout:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="horizontal" >
- <ImageView android:id="@+id/imageView"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent" />"
- <TextView android:id="@+id/textView"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:layout_marginLeft="15dp"
- android:gravity="center_vertical" />
- </LinearLayout>
Activity繼承自ListActivity,我故意增加了Item,方便測試,效果更明顯:
- public class ListViewDemo extends ListActivity{
- private final String[] mItems = new String[] { "Android", "iPhone",
- "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
- "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu",
- "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
- "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu",
- "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
- "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu",
- "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
- "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu",
- "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
- "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu",
- "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2",
- "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu",
- "Windows7", "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7",
- "Max OS X", "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X",
- "Linux", "OS/2", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ListViewArrayAdapter adapter = new ListViewArrayAdapter(this, mItems);
- getListView().setAdapter(adapter);
- }
- }
然後custom Adapter,優化之前的adapter:
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- long start = System.currentTimeMillis();
- LayoutInflater inflater = (LayoutInflater) mContext.getLayoutInflater();
- View rowView = inflater.inflate(mViewResourceId, parent, false);
- TextView textView = (TextView) rowView
- .findViewById(mTextViewResourceId);
- ImageView imageView = (ImageView) rowView
- .findViewById(mImageViewResourceId);
- textView.setText(mNames[position]);
- String s = mNames[position];
- if (s.startsWith("Windows7") || s.startsWith("iPhone")) {
- imageView.setImageResource(R.drawable.no);
- } else {
- imageView.setImageResource(R.drawable.yes);
- }
-
- Log.v("jerikc","cost time = " + (System.currentTimeMillis() - start));
- return rowView;
- }
優化之後的Adapter:
- public class ListViewArrayAdapter extends ArrayAdapter<String>{
- private final Activity mContext;
- private final String[] mNames;
- private final static int mViewResourceId = R.layout.text_image_row_layout;
- private final static int mTextViewResourceId = R.id.textView;
- private final static int mImageViewResourceId = R.id.imageView;
- static class ViewHolder {
- public TextView text;
- public ImageView image;
- }
- public ListViewArrayAdapter(Activity context, String[] names) {
- super(context, mViewResourceId, names);
- this.mContext = context;
- this.mNames = names;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- long start = System.currentTimeMillis();
- View rowView = convertView;
- if (rowView == null) {
- LayoutInflater inflater = mContext.getLayoutInflater();
- rowView = inflater.inflate(mViewResourceId, null);
- ViewHolder viewHolder = new ViewHolder();
- viewHolder.text = (TextView) rowView.findViewById(mTextViewResourceId);
- viewHolder.image = (ImageView) rowView.findViewById(mImageViewResourceId);
- rowView.setTag(viewHolder);
- }
- ViewHolder holder = (ViewHolder) rowView.getTag();
- String s = mNames[position];
- holder.text.setText(s);
- if (s.startsWith("Windows7") || s.startsWith("iPhone")) {
- holder.image.setImageResource(R.drawable.no);
- } else {
- holder.image.setImageResource(R.drawable.yes);
- }
- Log.v("jerikc","cost time = " + (System.currentTimeMillis() - start));
- return rowView;
- }
- }
優化的大致思想就是:優化之前,每次加載item的時候,都要加載一下布局文件,然後生成一個新的row View對象,然後通過View找到對應的ImageView和TextView,正如我們所知道的那樣,加載布局文件時很耗時的,特別是在操作比較頻繁 情況下,這是不可忍受的,所以會導致ANR現象。
因此,我們可以重復利用已不可見的row View對象。Android中,當它決定讓row View對象不可見的時候,它允許通過getView方法中的convertView參數來重復利用剛剛不可見的row View對象。
在優化的過程中,第一次加載的時候,我們需要把相關的數據保存起來,而View有一個方法setTag,該方法可用來保存一些數據結構。我們一個row View對象是由ImageView和TextView空間組成的,因此定義一個ViewHolder來保存ImageView和TextView對象。 在重復利用的過程中,只需簡單修改它們的值,而不用再次findViewById。