揭秘在ListView等AdapterView上动态添加删除项的陷阱
2013-03-18 22:22
477 查看
今天遇到这么个需求,需要在运行时动态添加ListView的item,看起来很简单,实际操作过程中却遇到了麻烦。
首先,定义如下array资源,作为列表的加载内容:
然后简单地写下布局文件,由于我需要不管列表有多长,始终在底部显示编辑框和按钮,所以将ListView中的layout_weight设为1。
最后添上Activity的代码,似乎没什么问题了,运行一下。
View
Code
界面成功显示,可是每次点击“添加”,就会抛出java.lang.UnsupportedOperationException,这看似很匪夷所思,因为按照android的api文档,确实能通过adapter上的add、remove等方法在运行时增删列表项啊,那问题到底出在哪里呢?
借助google的帮助,我在StackOverFlow上看了如下解答:
这里说到,如果要动态的改变列表的大小,必须使用一个可变大小的List(如ArrayList),而不能使用固定长度的array,否则将会得到UnsupportedOperationException。也就是说,由于需要从资源文件中加载内容,所以我自然就想到调用ArrayAdapter.createFromResource(Contextcontext,
int textArrayResId, int textViewResId)方法来构造adapter,而此方法导致ArrayAdapter中维护的是一个定长数组!对数组进行add,当然就会出错了。看到这里我不禁感慨,好一个陷阱!!!真相仿佛离我越来越近了,让我们再进入ArrayAdapter源码探个究竟。
通过源码可以发现,我上面的思考还是有误的。ArrayAdapter并没有单独维护array类型的数据,而是统一转换成了List,并存在了mObjects对象中。
createFromResource(...)调用了ArrayAdapter(Context context, int textViewResourceId,
T[] objects)构造方法,而在该方法的内部实现中,android使用Arrays的静态方法asList(...)将一个数组转换为List。熟悉Java的程序员都知道,Arrays.asList(...)方法所返回的并不是一个java.util.ArrayList,而是一个Arrays类的内部类,该List实现是不能进行增删操作的!!(见jdk文档),对该List进行add将抛出UnsupportedOperationException!
哈哈,这下终于真相大白了,这样一来稍微改动一下原来的代码即可成功运行:D
FInal Solution:
View
Code
原文地址:点击打开链接
首先,定义如下array资源,作为列表的加载内容:
<resources> <string name="app_name">MyListView</string> <string-array name="language"> <item>Java</item> <item>C</item> <item>C++</item> <item>PHP</item> </string-array> </resources>
然后简单地写下布局文件,由于我需要不管列表有多长,始终在底部显示编辑框和按钮,所以将ListView中的layout_weight设为1。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@android:id/list" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/addLangEdit" android:layout_width="200px" android:layout_height="wrap_content" /> <Button android:id="@+id/addButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="添加" /> </LinearLayout> </LinearLayout>
最后添上Activity的代码,似乎没什么问题了,运行一下。
View
Code
界面成功显示,可是每次点击“添加”,就会抛出java.lang.UnsupportedOperationException,这看似很匪夷所思,因为按照android的api文档,确实能通过adapter上的add、remove等方法在运行时增删列表项啊,那问题到底出在哪里呢?
借助google的帮助,我在StackOverFlow上看了如下解答:
这里说到,如果要动态的改变列表的大小,必须使用一个可变大小的List(如ArrayList),而不能使用固定长度的array,否则将会得到UnsupportedOperationException。也就是说,由于需要从资源文件中加载内容,所以我自然就想到调用ArrayAdapter.createFromResource(Contextcontext,
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(); } } // 其他方法 }
通过源码可以发现,我上面的思考还是有误的。ArrayAdapter并没有单独维护array类型的数据,而是统一转换成了List,并存在了mObjects对象中。
createFromResource(...)调用了ArrayAdapter(Context context, int textViewResourceId,
T[] objects)构造方法,而在该方法的内部实现中,android使用Arrays的静态方法asList(...)将一个数组转换为List。熟悉Java的程序员都知道,Arrays.asList(...)方法所返回的并不是一个java.util.ArrayList,而是一个Arrays类的内部类,该List实现是不能进行增删操作的!!(见jdk文档),对该List进行add将抛出UnsupportedOperationException!
哈哈,这下终于真相大白了,这样一来稍微改动一下原来的代码即可成功运行:D
FInal Solution:
View
Code
原文地址:点击打开链接
相关文章推荐
- 揭秘在ListView等AdapterView上动态添加删除项的陷阱
- 揭秘在ListView等AdapterView上动态添加删除项的陷阱
- 揭秘在ListView等AdapterView上动态添加删除项的陷阱
- 揭秘在ListView等AdapterView上动态添加删除项的陷阱(和在listview下添加按钮的布局设计)
- 揭秘在ListView等AdapterView上动态添加删除项的陷阱
- android ListView内数据的动态添加与删除
- 动态添加和删除ListView
- android ListView内数据的动态添加与删除
- 动态添加删除ExpandableListView的item的例子
- 动态添加删除ExpandableListView的item的例子
- Android中ListView动态添加删除项
- Android实战简易教程<十一>(树形组件:ExpandableListView显示和动态添加删除)
- Android – ListView 中添加按钮,动态删除添加ItemView的操作
- Android中ListView动态添加删除项
- Listview动态添加/删除数据
- Android – ListView 中添加按钮,动态删除添加ItemView的操作
- Android – ListView 中添加按钮,动态删除添加ItemView的操作
- ListView 中添加按钮,动态删除添加ItemView的操作