欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-02-28 10:22:52
如何避开在listview等adapterview上动态添加删除项的陷阱,下面就为大家分享,具体内容如下 首先,定义如下array资源,作为列表的加载内容: &...

如何避开在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("");
  }
 }
 };
}

揭秘在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();
 }
 }
 // 其他方法
}


  通过源码可以发现,我上面的思考还是有误的。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相关内容介绍,希望对大家的学习有所帮助。