揭秘在ListView等AdapterView上动态添加删除项的陷阱

2019-12-10 18:17:33王振洲

ListView,AdapterView

  界面成功显示,可是每次点击“添加”,就会抛出java.lang.UnsupportedOperationException,这看似很匪夷所思,因为按照android的api文档,确实能通过adapter上的add、remove等方法在运行时增删列表项啊,那问题到底出在哪里呢?

  借助google的帮助,找到如下解答:

  ListView,AdapterView

  这里说到,如果要动态的改变列表的大小,必须使用一个可变大小的List(如ArrayList),而不能使用固定长度的array,否则将会得到UnsupportedOperationException。也就是说,由于需要从资源文件中加载内容,所以我自然就想到调用ArrayAdapter.createFromResource(Context context, int textArrayResId, int textViewResId)方法来构造adapter,而此方法导致ArrayAdapter中维护的是一个定长数组!对数组进行add,当然就会出错了。看到这里我不禁感慨,好一个陷阱!!!真相仿佛离我越来越近了,让我们再进入ArrayAdapter源码探个究竟。

public class ArrayAdapter<T> extends BaseAdapter implements Filterable {
   // 代表ArrayAdapter中的数据
 private List<T> mObjects;
 // 其他fields
 
 public ArrayAdapter(Context context, int textViewResourceId, List<T> objects) {
 init(context, textViewResourceId, 0, objects);
 }

 public ArrayAdapter(Context context, int textViewResourceId, T[] objects) {
 // 注意这里的Arrays.asList(...)方法!!!
 init(context, textViewResourceId, 0, Arrays.asList(objects)); 
 }

 public static ArrayAdapter<CharSequence> createFromResource(Context context,
  int textArrayResId, int textViewResId) {
 CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
 return new ArrayAdapter<CharSequence>(context, textViewResId, strings);
 }

 private void init(Context context, int resource, int textViewResourceId, List<T> objects) {
 mContext = context;
 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 mResource = mDropDownResource = resource;
 mObjects = objects;
 mFieldId = textViewResourceId;
 }

 public void add(T object) {
 if (mOriginalValues != null) {
  synchronized (mLock) {
  mOriginalValues.add(object);
  if (mNotifyOnChange) notifyDataSetChanged();
  }
 } else {
  mObjects.add(object); // 若该mObjects为固定长度List,此处将抛异常!!!
  if (mNotifyOnChange) notifyDataSetChanged();
 }
 }
 // 其他方法
}