在日常開(kāi)發(fā)中,ListView是我們常用的控件,也是遇到坑比較多的一個(gè)控件。在之前的項(xiàng)目中,有這樣的一個(gè)布局需求,在ListView的item中包含有EditText,第一個(gè)問(wèn)題就是焦點(diǎn)問(wèn)題,會(huì)發(fā)現(xiàn)edittext獲取不到焦點(diǎn)。
1.焦點(diǎn)問(wèn)題
比如我們有如下的代碼:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>```
####MainActivity.java
public class MainActivity extends Activity {
ListView mListView;
MyAdapter mMyAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.listView);
mMyAdapter = new MyAdapter(this);
mListView.setAdapter(mMyAdapter);
}}
####MyAdapter.java
public class MyAdapter extends BaseAdapter {
private Context mContext;
public MyAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return 20;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
EditText editText;
if (convertView == null) {
editText = new EditText(mContext);
convertView = editText;
} else {
editText = (EditText) convertView;
}
System.out.println("current pos:" + position);
return convertView;
}}
當(dāng)你運(yùn)行上述簡(jiǎn)單的代碼后發(fā)現(xiàn)EditText是無(wú)法獲取焦點(diǎn)的,導(dǎo)致無(wú)法輸入任何東東,那么原因何在呢?
####其實(shí),是listview先于子item搶占了焦點(diǎn),那么我們首先想到的就是讓listview失去焦點(diǎn),讓子item獲取焦點(diǎn)(當(dāng)然,listview 的onitem相關(guān)監(jiān)聽(tīng)事件會(huì)失效)。
mListView.setFocusable(false);
這是再運(yùn)行發(fā)現(xiàn)鍵盤(pán)彈出了,可是editText獲取到焦點(diǎn)然后又失去了,需要你手動(dòng)再次點(diǎn)擊才能獲取到,然后才能輸入。
而且當(dāng)你輸入完畢,關(guān)閉軟鍵盤(pán),發(fā)現(xiàn)輸入的東西不見(jiàn)了,自動(dòng)清空。這又產(chǎn)生了兩個(gè)問(wèn)題。
第一個(gè)問(wèn)題是listview每次調(diào)用getview都會(huì)使EditText失去焦點(diǎn),第二個(gè)問(wèn)題歸結(jié)于下面要講的listview的item復(fù)用產(chǎn)生的問(wèn)題。
第一種方式行不通,查詢(xún)相關(guān)資料發(fā)現(xiàn),可以通過(guò)給listview的item的根布局設(shè)置descendantFocusability屬性。
#####android:descendantFocusability屬性有三個(gè)值:
beforeDescendants:viewgroup會(huì)優(yōu)先其子類(lèi)控件而獲取到焦點(diǎn)
afterDescendants:viewgroup只有當(dāng)其子類(lèi)控件不需要獲取焦點(diǎn)時(shí)才獲取焦點(diǎn)
blocksDescendants:viewgroup會(huì)覆蓋子類(lèi)控件而直接獲得焦點(diǎn)
####那么我們修改adapter中的getView方法
@Override
public View getView(int position, View convertView, ViewGroup parent) {
EditText editText;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_edittext, parent, false);
editText = (EditText) convertView.findViewById(R.id.editText);
convertView.setTag(editText);
} else {
editText = (EditText) convertView.getTag();
}
System.out.println("current pos:" + position);
return convertView;
}
####list_edittext.xml
<?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:descendantFocusability="beforeDescendants"
android:orientation="vertical">
<EditText android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
發(fā)現(xiàn)還是無(wú)效果,其實(shí)我們少了一句關(guān)鍵的代碼,就是給相應(yīng)的 activity 設(shè)置 windowSoftInputMode= adjustPan 即可。。
##終上所述,我認(rèn)為的解決方案就是給ListView或者ListView的item的根布局添加android:descendantFocusability="beforeDescendants",然后設(shè)置相應(yīng)的activity 的windowSoftInputMode屬性為adjustPan 。
##2.數(shù)據(jù)問(wèn)題
解決完焦點(diǎn)問(wèn)題后,另一個(gè)問(wèn)題就是edittext的數(shù)據(jù)問(wèn)題了。當(dāng)我們?cè)诋?dāng)前屏幕的edittext中輸入東東后,往下滑,發(fā)現(xiàn)下面的edittext自動(dòng)輸入了我們輸入過(guò)得東東,這明顯是我們不愿意看到的。
其實(shí)這是由于getView方法的復(fù)用view導(dǎo)致的,加入你在position=0的edittext中輸入了內(nèi)容,當(dāng)你往下滑時(shí),當(dāng)position為0的view完全消失時(shí),該view會(huì)被加入到 mActiveViews[]中,當(dāng)下方的item檢測(cè)到由可用的view,則從該數(shù)組中取出,所以下方的edittext的內(nèi)容會(huì)跟上面你輸入的一樣,其實(shí)就是同一個(gè)edittext。關(guān)于listview源碼級(jí)解析詳見(jiàn)[鏈接](http://blog.csdn.net/guolin_blog/article/details/44996879)
#####解決方案——保存edittext的內(nèi)容
修改adapter代碼:
//新增一個(gè)數(shù)組用于保存edittext的內(nèi)容
private SparseArray<String> mStringSparseArray;
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
EditTextHolder editTextHolder;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_edittext, parent, false);
editTextHolder = new EditTextHolder();
editTextHolder.mEditText = (EditText) convertView.findViewById(R.id.editText);
editTextHolder.mMyTextWatcher = new MyTextWatcher(position, mStringSparseArray);
//給edittext設(shè)置watcher
editTextHolder.mEditText.addTextChangedListener(editTextHolder.mMyTextWatcher);
convertView.setTag(editTextHolder);
} else {
editTextHolder = (EditTextHolder) convertView.getTag();
//由于復(fù)用了edittext,導(dǎo)致他的watcher里的position還是之前的positiono,所以需要通知
//watcher更新positon,才能保存正確的positon的值
editTextHolder.updatePosition(position);
}
System.out.println(position);
editTextHolder.mEditText.setText(mStringSparseArray.get(position));
return convertView;
}
static class EditTextHolder {
EditText mEditText;
MyTextWatcher mMyTextWatcher;
public void updatePosition(int position) {
mMyTextWatcher.updatePosition(position);
}
}
static class MyTextWatcher implements TextWatcher {
private int position;
private SparseArray<String> sparseArray;
//更新postion
public void updatePosition(int position) {
this.position = position;
}
public MyTextWatcher(int position, SparseArray<String> sparseArray) {
this.position = position;
this.sparseArray = sparseArray;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
@Override
public void afterTextChanged(Editable s) {
//保存edittext的值
sparseArray.put(position, s.toString());
}
}
運(yùn)行代碼,發(fā)現(xiàn)edittext數(shù)據(jù)錯(cuò)亂問(wèn)題解決,此方法同樣適用于checkbox錯(cuò)亂等問(wèn)題。
##### demo工程已上傳至github,有需要的可去[download](https://github.com/dengyuaner/ListViewDemo)