揭秘在ListView等AdapterView上动态添加删除项的陷阱
如何避开在listview等adapterview上动态添加删除项的陷阱,下面就为大家分享,具体内容如下
首先,定义如下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>
然后简单地写下布局文件,由于我需要不管列表有多长,始终在底部显示编辑框和按钮,所以将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的代码,似乎没什么问题了,运行一下。
public class mylistview extends listactivity { private arrayadapter<charsequence> madapter; private listview mlistview; private edittext mlanguagetext; private button maddbutton; /** called when the activity is first created. */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.mylist1); //get the view mlistview = getlistview(); mlanguagetext = (edittext) findviewbyid(r.id.addlangedit); maddbutton = (button) findviewbyid(r.id.addbutton); //array adapter created from string array resources madapter = arrayadapter.createfromresource( this, r.array.language, android.r.layout.simple_list_item_1); //set the adapter mlistview.setadapter(madapter); //add listener maddbutton.setonclicklistener(monclicklistener); } private onclicklistener monclicklistener = new view.onclicklistener() { @override public void onclick(view v) { string text = mlanguagetext.gettext().tostring(); if(null == text || "".equals(text.trim())) { toast.maketext(mylistview.this, "输入不能为空", toast.length_short).show(); }else { madapter.add(text); madapter.notifydatasetchanged(); mlanguagetext.settext(""); } } }; }
界面成功显示,可是每次点击“添加”,就会抛出java.lang.unsupportedoperationexception,这看似很匪夷所思,因为按照android的api文档,确实能通过adapter上的add、remove等方法在运行时增删列表项啊,那问题到底出在哪里呢?
借助google的帮助,找到如下解答:
这里说到,如果要动态的改变列表的大小,必须使用一个可变大小的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(); } } // 其他方法 }
通过源码可以发现,我上面的思考还是有误的。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:
/** * * @author codingmyworld * 2011-7-31 下午04:43:48 */ public class mylistview extends listactivity { private arrayadapter<charsequence> madapter; private listview mlistview; private edittext mlanguagetext; private button maddbutton; /** called when the activity is first created. */ @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.mylist1); //get the view mlistview = getlistview(); mlanguagetext = (edittext) findviewbyid(r.id.addlangedit); maddbutton = (button) findviewbyid(r.id.addbutton); //array adapter created from string array resources list<charsequence> objects = new arraylist<charsequence>( arrays.aslist(getresources().gettextarray(r.array.language))); madapter = new arrayadapter<charsequence>( this, android.r.layout.simple_list_item_1, objects); //set the adapter mlistview.setadapter(madapter); //add listener maddbutton.setonclicklistener(monclicklistener); } private onclicklistener monclicklistener = new view.onclicklistener() { @override public void onclick(view v) { string text = mlanguagetext.gettext().tostring(); if(null == text || "".equals(text.trim())) { toast.maketext(mylistview.this, "输入不能为空", toast.length_short).show(); }else { madapter.add(text); madapter.notifydatasetchanged(); //not required mlanguagetext.settext(""); } } }; }
以上就是关于android listview相关内容介绍,希望对大家的学习有所帮助。
上一篇: MySQL中修改库名的操作教程
下一篇: 广告放在文章页左上角的解决办法二