工作中遇到的listview相關(guān)問(wèn)題(一)——item包含EditText

在日常開(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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容