Android ListView – Holder模式和getItemViewType
在Mobile开发:Android-ListView中介绍了在Android中如何使用ListView,以及为数据模型自定义Item的UI. 这一篇要将两个方面的内容:Holder模式和getItemViewType的应用。
Holder模式
模式这个词语是通过GOF的《设计模式》这本书而引入软件设计领域的。在软件设计领域里面,模式是指为解决特定问题而存在的通用解决方案。这个特定的问题如果在软件设计领域广泛存在,那么该解决方案就有记录和传播的价值。
Holder模式要解决的是性能问题:
场景:在我们的Adapter中每次都要从row中通过findViewById来找到子控件,然后设置值。如果row的布局比较复杂,或者row的数目特别多。这个查找就要不断发生。从而导致性能问题。
方案:在row第一次被构建出来的时候,调用findViewById, 通过Holder对象存储起来,然后把Holder对象通过row.setTag方法,直接缓存在row上。这样下次就不用在查找了。
看看下面的代码:
@Override public View getView(int position, View convertView, ViewGroup parent) { try { Restaurant item = this.getItem(position); View row = convertView; RestaurantListViewItemHolder holder=null; if (row == null) { LayoutInflater inflater = LayoutInflater .from(this.getContext()); if(!item.isGoldenBrand()) row = inflater .inflate(R.layout.restaurant_list_view_item, null); else row=inflater.inflate(R.layout.restaurant_list_view_item_golden, null); holder = createRestaurantListViewItemHolder(row,item.isGoldenBrand()); row.setTag(holder); }else{ holder=(RestaurantListViewItemHolder)row.getTag(); } holder.UpdateUI(item); return row; } catch (Exception e) { Log.e(LogTags.RestaurantListAdapter, e.getLocalizedMessage(), e); } return super.getView(position, convertView, parent); } /** * Create a holder for a row in list,when the row is first loaded. * @param row * @return */ private RestaurantListViewItemHolder createRestaurantListViewItemHolder( View row,boolean isGoldenBrand) { TextView txtName = (TextView) row .findViewById(R.id.restaurant_name); TextView txtPhoneNR = (TextView) row .findViewById(R.id.restaurant_phone_nr); TextView txtLocation = (TextView) row .findViewById(R.id.restaurant_address); ImageView imgIcon=(ImageView) row.findViewById(R.id.restaurent_icon); TextView txtDescription=null; if(isGoldenBrand) txtDescription=(TextView)row.findViewById(R.id.restaurant_description_direct); return new RestaurantListViewItemHolder(txtPhoneNR,txtName,txtLocation, imgIcon,txtDescription); }
上面的代码由于混合了和getItemViewType有关的内容。所以看起来有点和上篇中不同。其主要代码在第15,16,18,21行。
- 第15行:通过调用createRestaurantListViewItemHolder来构造一个Holder对象。
- 第16行:调用row.setTag将Holder关联到row对象上。
- 第18行:如果row对象不是新构建的,直接获取row上的Tag,这个Tag对象就是我们在第16行存储的RestaurantListViewItemHolder对象。
- 第21行:调用holder.UpdateUI来更新UI。
上面的代码中已经用到了RestaurantListViewItemHolder,下面是代码文件。逻辑非常的简单。
package net.lxzhu.mobile; import android.widget.ImageView; import android.widget.TextView; public class RestaurantListViewItemHolder { private TextView phoneNumberUI; private TextView nameUI; private TextView addressUI; private ImageView iconUI; private TextView descriptionUI; public RestaurantListViewItemHolder(TextView phoneNumberUI, TextView nameUI, TextView addressUI, ImageView iconUI,TextView descriptionUI) { this.phoneNumberUI=phoneNumberUI; this.nameUI=nameUI; this.addressUI=addressUI; this.iconUI=iconUI; this.descriptionUI=descriptionUI; } public void UpdateUI(Restaurant data){ this.phoneNumberUI.setText(data.getPhoneNR()); this.nameUI.setText(data.getName()); this.addressUI.setText(data.getAddress()); this.iconUI.setImageResource(R.drawable.ic_launcher); if(this.descriptionUI!=null) this.descriptionUI.setText(data.getDescription()); } }
getItemViewType
假设我们的UI设计有这样一个需求。如果一家餐馆被评为金牌场馆,那么它可以直接将最近的一条点评显示在列表中,这样用户不用选中或者点击就可以看到评价了。
这就是为什么我们上面的代码中出现了isGoldenBrand这样的代码。到目前为止,我们的ListView中的每个Item都是一样的。而这个需求却让我们根据数据的不同出现了不同的布局。AdapterView已经提供了支持,我们只需要:
- 重写getViewTypeCount方法,返回可能出现的布局的数目。
- 重写getItemViewType方法,这个方法返回0到getViewTypeCount()-1之间的数字或者IGNORE_ITEM_VIEW_TYPE. 这个方法返回的id必须要和getView方法中返回的View匹配:在getView中返回相同类型View的position, getItemViewType应该返回同一个id.
- 在getView中根据数据不同,返回不同的View。
下面是getViewTypeCount和getItemViewType的代码,getView的代码前面已经给出,这里就不在重复了。
@Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { Restaurant data=this.getItem(position); if(!data.isGoldenBrand()) return 0; else return 1; }
其实我们这里的需求不使用这种模式也可以开发出来。但是如果需求中对金牌餐馆的布局和普通餐馆布局有很大的不同的话。这个功能就非常实用了。下面是修改后的代码的执行结果。
最后更新:2017-04-02 15:28:26