android:提升 ListView 的執行效率

yufan發表於2016-01-08

之所以說 ListView 這個控制元件很難用,就是因為它有很多的細節可以優化,其中執行效率 就是很重要的一點。目前我們 ListView 的執行效率是很低的,因為在 FruitAdapter 的 getView() 方法中每次都將佈局重新載入了一遍,當 ListView 快速滾動的時候這就會成為效能的瓶頸。

仔細觀察,getView()方法中還有一個 convertView 引數,這個引數用於將之前載入好的 佈局進行快取,以便之後可以進行重用。修改 FruitAdapter 中的程式碼,如下所示:

 

public class FruitAdapter extends ArrayAdapter<Fruit> {

……

@Override

public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position);

View view;

if (convertView == null) {

view = LayoutInflater.from(getContext()).inflate(resourceId, null);

} else {

 

 

 

view = convertView;

}

ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image); TextView fruitName = (TextView) view.findViewById(R.id.fruit_name); fruitImage.setImageResource(fruit.getImageId()); fruitName.setText(fruit.getName());

return view;

}

}

可以看到,現在我們在 getView()方法中進行了判斷,如果 convertView 為空,則使用 LayoutInflater 去載入佈局,如果不為空則直接對 convertView 進行重用。這樣就大大提高了 ListView 的執行效率,在快速滾動的時候也可以表現出更好的效能。

不過,目前我們的這份程式碼還是可以繼續優化的,雖然現在已經不會再重複去載入佈局, 但是每次在 getView()方法中還是會呼叫 View 的 findViewById()方法來獲取一次控制元件的例項。 我們可以藉助一個 ViewHolder 來對這部分效能進行優化,修改 FruitAdapter 中的程式碼,如下 所示:

 

public class FruitAdapter extends ArrayAdapter<Fruit> {

……

@Override

public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position);

View view;

ViewHolder viewHolder;

if (convertView == null) {

view = LayoutInflater.from(getContext()).inflate(resourceId, null);

viewHolder = new ViewHolder();

viewHolder.fruitImage = (ImageView) view.findViewById

(R.id.fruit_image);

viewHolder.fruitName = (TextView) view.findViewById

(R.id.fruit_name);

view.setTag(viewHolder); // ViewHolder儲存在View

} else {

view = convertView;

viewHolder = (ViewHolder) view.getTag(); // 重新獲取ViewHolder

} viewHolder.fruitImage.setImageResource(fruit.getImageId()); viewHolder.fruitName.setText(fruit.getName());

return view;

 

 

 

}

class ViewHolder { ImageView fruitImage; TextView fruitName;

 

}

}

我們新增了一個內部類 ViewHolder,用於對控制元件的例項進行快取。當 convertView 為空 的時候,建立一個 ViewHolder 物件,並將控制元件的例項都存放在 ViewHolder 裡,然後呼叫 View 的 setTag()方法,將 ViewHolder 物件儲存在 View 中。當 convertView 不為空的時候則呼叫 View 的 getTag()方法,把 ViewHolder 重新取出。這樣所有控制元件的例項都快取在了 ViewHolder 裡,就沒有必要每次都通過 findViewById()方法來獲取控制元件例項了。

通過這兩步的優化之後,我們 ListView 的執行效率就已經非常不錯了。

 

3.5.4    ListView 的點選事件

 

話說回來,ListView 的滾動畢竟只是滿足了我們視覺上的效果,可是如果 ListView 中的 子項不能點選的話,這個控制元件就沒有什麼實際的用途了。因此,本小節中我們就來學習一下 ListView 如何才能響應使用者的點選事件。

修改 MainActivity 中的程式碼,如下所示:

 

public class MainActivity extends Activity {

 

 

private List<Fruit> fruitList = new ArrayList<Fruit>();

 

 

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits();

FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);

ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); listView.setOnItemClickListener(new OnItemClickListener() {

@Override

 

 

 

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

Fruit fruit = fruitList.get(position); Toast.makeText(MainActivity.this, fruit.getName(),

Toast.LENGTH_SHORT).show();

}

});

}

……

}

可以看到,我們使用了 setOnItemClickListener()方法來為 ListView 註冊了一個監聽器, 當使用者點選了 ListView 中的任何一個子項時就會回撥 onItemClick()方法,在這個方法中可以 通過 position 引數判斷出使用者點選的是哪一個子項,然後獲取到相應的水果,並通過 Toast 將水果的名字顯示出來。

重新執行程式,並點選一下西瓜,效果如圖 3.31 所示。

 

圖   3.31

相關文章