揭秘在ListView等AdapterView上動(dòng)態(tài)添加刪除項(xiàng)的陷阱
如何避開在ListView等AdapterView上動(dòng)態(tài)添加刪除項(xiàng)的陷阱,下面就為大家分享,具體內(nèi)容如下
首先,定義如下array資源,作為列表的加載內(nèi)容:
<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>
然后簡(jiǎn)單地寫下布局文件,由于我需要不管列表有多長(zhǎng),始終在底部顯示編輯框和按鈕,所以將ListView中的layout_weight設(shè)為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的代碼,似乎沒什么問(wèn)題了,運(yùn)行一下。
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("");
}
}
};
}

界面成功顯示,可是每次點(diǎn)擊“添加”,就會(huì)拋出java.lang.UnsupportedOperationException,這看似很匪夷所思,因?yàn)榘凑誥ndroid的api文檔,確實(shí)能通過(guò)adapter上的add、remove等方法在運(yùn)行時(shí)增刪列表項(xiàng)啊,那問(wèn)題到底出在哪里呢?
借助google的幫助,找到如下解答:

這里說(shuō)到,如果要?jiǎng)討B(tài)的改變列表的大小,必須使用一個(gè)可變大小的List(如ArrayList),而不能使用固定長(zhǎng)度的array,否則將會(huì)得到UnsupportedOperationException。也就是說(shuō),由于需要從資源文件中加載內(nèi)容,所以我自然就想到調(diào)用ArrayAdapter.createFromResource(Context context, int textArrayResId, int textViewResId)方法來(lái)構(gòu)造adapter,而此方法導(dǎo)致ArrayAdapter中維護(hù)的是一個(gè)定長(zhǎng)數(shù)組!對(duì)數(shù)組進(jìn)行add,當(dāng)然就會(huì)出錯(cuò)了??吹竭@里我不禁感慨,好一個(gè)陷阱?。?!真相仿佛離我越來(lái)越近了,讓我們?cè)龠M(jìn)入ArrayAdapter源碼探個(gè)究竟。
public class ArrayAdapter<T> extends BaseAdapter implements Filterable {
// 代表ArrayAdapter中的數(shù)據(jù)
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為固定長(zhǎng)度List,此處將拋異常!??!
if (mNotifyOnChange) notifyDataSetChanged();
}
}
// 其他方法
}
通過(guò)源碼可以發(fā)現(xiàn),我上面的思考還是有誤的。ArrayAdapter并沒有單獨(dú)維護(hù)array類型的數(shù)據(jù),而是統(tǒng)一轉(zhuǎn)換成了List,并存在了mObjects對(duì)象中?! ?/p>
createFromResource(...)調(diào)用了ArrayAdapter(Context context, int textViewResourceId, T[] objects)構(gòu)造方法,而在該方法的內(nèi)部實(shí)現(xiàn)中,android使用Arrays的靜態(tài)方法asList(...)將一個(gè)數(shù)組轉(zhuǎn)換為L(zhǎng)ist。熟悉Java的程序員都知道,Arrays.asList(...)方法所返回的并不是一個(gè)java.util.ArrayList,而是一個(gè)Arrays類的內(nèi)部類,該List實(shí)現(xiàn)是不能進(jìn)行增刪操作的??!(見jdk文檔),對(duì)該List進(jìn)行add將拋出UnsupportedOperationException!
哈哈,這下終于真相大白了,這樣一來(lái)稍微改動(dòng)一下原來(lái)的代碼即可成功運(yùn)行: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("");
}
}
};
}
以上就是關(guān)于Android Listview相關(guān)內(nèi)容介紹,希望對(duì)大家的學(xué)習(xí)有所幫助。
- Android自定義Adapter的ListView的思路及代碼
- android開發(fā)中ListView與Adapter使用要點(diǎn)介紹
- ListView的Adapter使用 之 初學(xué)ArrayAdapter String
- ListView的Adapter使用(綁定數(shù)據(jù)) 之 自定義每一項(xiàng)的布局去綁定數(shù)據(jù)
- Adapter實(shí)現(xiàn)ListView帶多選框等狀態(tài)的自定義控件的注意事項(xiàng)
- Android listview與adapter詳解及實(shí)例代碼
- Android開發(fā)中ListView自定義adapter的封裝
- ListView Adapter優(yōu)化 實(shí)例
- Android ListView適配器(Adapter)優(yōu)化方法詳解
- Android ListView自定義Adapter實(shí)現(xiàn)仿QQ界面
相關(guān)文章
Android仿QQ空間主頁(yè)面的實(shí)現(xiàn)
今天模仿安卓QQ空間,打開程序的啟動(dòng)畫面和導(dǎo)航頁(yè)面我就不做了,大家可以模仿微信的那個(gè)做一下,很簡(jiǎn)單。這次主要做一下主頁(yè)面的實(shí)現(xiàn),感興趣的朋友可以參考下2013-01-01
Android程序開發(fā)通過(guò)HttpURLConnection上傳文件到服務(wù)器
這篇文章主要介紹了Android程序開發(fā)通過(guò)HttpURLConnection上傳文件到服務(wù)器的相關(guān)資料,需要的朋友可以參考下2016-01-01
Android 滾動(dòng)時(shí)間選擇的示例代碼
這篇文章主要介紹了Android 滾動(dòng)時(shí)間選擇的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
發(fā)布?Android?library?到?Maven?解析
這篇文章主要介紹了發(fā)布?Android?library到Maven解析,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
Android adb.exe程序啟動(dòng)不起來(lái) 具體解決方法
這篇文章主要介紹了Android adb.exe程序啟動(dòng)不起來(lái) 具體解決方法,有需要的朋友可以參考一下2013-12-12
Android中invalidate()和postInvalidate() 的區(qū)別及使用方法
Android中實(shí)現(xiàn)view的更新有兩組方法,一組是invalidate,另一組是postInvalidate,其中前者是在UI線程自身中使用,而后者在非UI線程中使用。本文給大家介紹Android中invalidate()和postInvalidate() 的區(qū)別及使用方法,感興趣的朋友一起學(xué)習(xí)吧2016-05-05
解析后臺(tái)進(jìn)程對(duì)Android性能影響的詳解
本篇文章是對(duì)Android中后臺(tái)進(jìn)程對(duì)Android性能的影響進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
Android自定義帶圓點(diǎn)的半圓形進(jìn)度條
這篇文章主要為大家詳細(xì)介紹了Android自定義帶圓點(diǎn)的半圓形進(jìn)度條,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12

