Android數據適配器(Adapter)優(yōu)化:高效ViewHolder
在使用Listview或GridView的時候,往往需要自定義數據適配器,一般都要覆寫getView(),在該方法中有一個convertView參數,該參數就是用來加載數據時的View。
初學者簡單但低效的方式
- public View getView(int position, View convertView, ViewGroup parent) {
- View item= inflater.inflate(R.layout.good_list_item, null, false);
- ImageView img = (ImageView) item.findViewById(R.id.img);
- TextView price = (TextView) item.findViewById(R.id.price);
- img.setImageResource(R.drawable.ic_launcher);
- price.setText("$"+list.get(position).price);
- return item;
- }
每次加載view,都要重新建立很多view對象,如果某條listview中有一萬條數據,這種加載方式就歇菜了。
利用convertView
利用Android的Recycler機制,利用convertView來重新回收View,效率有了本質提高。View的每次創(chuàng)建是比較耗時的,因此對于getview方法傳入的convertView應充分利用 != null的判斷 。
- public View getView(int position, View convertView, ViewGroup parent) {
- if(convertView==null){
- convertView = inflater.inflate(R.layout.good_list_item, null, false);
- }
- TextView tv_price = (TextView)convertView.findViewById(R.id.price)
- ImageView iv = (ImageView)convertView.findViewByID(R.id.img);
- return convertView;
- }
使用ViewHolder
ViewHolder將需要緩存的view封裝好,convertView的setTag才是將這些緩存起來供下次調用。 當你的listview里布局多樣化的時候 viewholder的作用體現明顯,效率再一次提高。 View的findViewById()方法也是比較耗時的,因此需要考慮只調用一次,之后就用View.getTag()方法來獲得ViewHolder對象。
- class ViewHolder{
- ImageView img;
- TextView price;
- }
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder = new ViewHolder();
- if(convertView==null){
- convertView = inflater.inflate(R.layout.good_list_item, null, false);
- holder.img = (ImageView) convertView.findViewById(R.id.img);
- holder.price = (TextView) convertView.findViewById(R.id.price);
- convertView.setTag(holder);
- }else{
- holder = (ViewHolder) convertView.getTag();
- }
- //設置holder
- holder.img.setImageResource(R.drawable.ic_launcher);
- holder.price.setText("$"+list.get(position).price);
- return convertView;
- }
優(yōu)雅的使用ViewHolder
使用ViewHolder時,每次一遍一遍的findViewById,一遍一遍在ViewHolder里面添加View的定義,view一多,是不是感覺煩爆了,base-adapter-helper這個類庫似乎***的解決了這個問題。
其設計思想是使用 SparseArray來存儲view的引用,代替了原本的ViewHolder,不用聲明一大堆View,簡潔明了。
我也自己動手寫了一個簡單版的ViewHolder。
- public class ViewHolder{
- private final SparseArray<View> views;
- private View convertView;
- private ViewHolder(View convertView){
- this.views = new SparseArray<View>();
- this.convertView = convertView;
- convertView.setTag(this);
- }
- public static ViewHolder get(View convertView){
- if (convertView == null) {
- return new ViewHolder(convertView);
- }
- ViewHolder existedHolder = (ViewHolder) convertView.getTag();
- return existedHolder;
- }
- public <T extends View> T getView(int viewId) {
- View view = views.get(viewId);
- if (view == null) {
- view = convertView.findViewById(viewId);
- views.put(viewId, view);
- }
- return (T) view;
- }
- }
使用的話就超級簡單和簡潔了:
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = LayoutInflater.from(context)
- .inflate(R.layout.good_list_item, null, false);
- }
- ViewHolder mViewHolder = ViewHolder.get(convertView);
- TextView price = mViewHolder.getView(R.id.price);
- //...其他getView
- return convertView;
- }