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

作者:lijiao 时间:2022-11-26 16:02:27 

如何避开在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,AdapterView
0
投稿

猜你喜欢

  • Maven引入本地Jar包并打包进War包中的方法

    2023-06-16 12:43:28
  • Java中缀表达式转后缀表达式实现方法详解

    2021-08-25 02:57:20
  • 以实例简介Java中线程池的工作特点

    2023-08-11 23:04:19
  • Spring Cloud基于zuul实现网关过程解析

    2021-09-11 06:52:19
  • SpringBoot使用Redis实现分布式锁

    2021-11-06 20:10:22
  • Android开发悬浮按钮 Floating ActionButton的实现方法

    2023-05-02 10:44:07
  • 简单谈谈Java中的栈和堆

    2022-07-30 05:33:01
  • JDK8 中Arrays.sort() 排序方法详解

    2023-01-17 10:38:30
  • android采用FFmpeg实现音视频合成与分离

    2022-03-05 09:18:16
  • Android 自定义View实现单击和双击事件的方法

    2022-03-23 19:21:18
  • 解决Android应用冷启动时出现的白屏问题的方法

    2023-12-16 02:57:36
  • android tv列表焦点记忆实现的方法

    2023-08-25 19:36:53
  • spring声明式事务 @Transactional 不回滚的多种情况以及解决方案

    2023-07-06 10:50:33
  • c#创建windows服务入门教程实例

    2023-02-24 11:31:31
  • C#实现合并多张图片为GIF动态图

    2022-12-13 04:16:35
  • C#访问SQLServer增删改查代码实例

    2021-10-08 14:39:03
  • Spring Bean创建和循环依赖

    2023-10-21 09:17:51
  • C#中重载重写和覆盖的定义与区别

    2022-04-23 09:25:34
  • Android WebP 图片压缩与传输

    2022-05-30 16:18:16
  • Android Studio 透明状态栏的实现示例

    2022-12-06 21:44:23
  • asp之家 软件编程 m.aspxhome.com