Android中Adapter中edittext,checkbox記住狀態解決方案(二)
Android中Adapter中edittext,checkbox記住狀態解決方案(一)
在上篇文章解決了adapter中checkbox記住狀態和edittext可編輯的問題,下面談談怎麼解決記住edittext中的內容和保證在操作加、減按鈕的時候,操作的edittext對象是沒有錯位的問題。
一、記住edittext中的內容
解決的思路和checkbox差不多,不過還是有些差別,checkbox只有兩種狀態,而edittext的值是不固定的。checkbox我們是用一個enum類型的list來儲存狀態的,所以edittext就不能了,可以用map和實體類,我為了方便就用了hashMap。
// 用來儲存editext中資料的listprivate List> mData = new ArrayList>();
在初始化的時候先類比資料
for (CartBean cartBean : list) {mData.add(new HashMap());}
edittext的監聽,並且有個log
mHolder.num.addTextChangedListener(new TextWatcher() {@Overridepublic void onTextChanged(CharSequence s, int start, int before,int count) {}@Overridepublic void beforeTextChanged(CharSequence s, int start, int count,int after) {}@Overridepublic void afterTextChanged(Editable s) {if (!TextUtils.isEmpty(s.toString())) { if(!TextUtils.isEmpty(s.toString())){mData.get(position).put(etValue,s.toString()); Log.i(afterTextChanged, position+position);}}}});
根據list對應位置的position取出edittext的值
String value = mData.get(position).get(etValue);if (!TextUtils.isEmpty(value)) {mHolder.num.setText(value);} else {mHolder.num.setText(1);}
代碼出來了,然後在測試的時候可以發現仍然會出現錯亂的問題,後來當多次來回滑動列表之後,再滑動列表,讓第一個剛好完全出來,下一個剛好進來,這時候第一個item會被剛進來的item重用,執行上面的賦值代碼的時候log應該是這樣
01-27 15:55:46.612: I/afterTextChanged(4784): position001-27 15:55:46.622: I/afterTextChanged(4784): position101-27 15:55:46.632: I/afterTextChanged(4784): position201-27 15:55:46.642: I/afterTextChanged(4784): position301-27 15:55:46.642: D/AbsListView(4784): unregisterIRListener() is called 01-27 15:55:46.642: D/AbsListView(4784): unregisterIRListener() is called 01-27 15:55:46.662: D/AbsListView(4784): unregisterIRListener() is called 01-27 15:55:46.682: D/AbsListView(4784): unregisterIRListener() is called 01-27 15:55:56.882: I/afterTextChanged(4784): position4
一個操作只觸發一個監聽
結果發現是這樣
01-27 15:53:43.772: I/afterTextChanged(4784): position001-27 15:53:43.772: I/afterTextChanged(4784): position501-27 15:53:43.772: I/afterTextChanged(4784): position501-27 15:53:43.772: I/afterTextChanged(4784): position901-27 15:53:43.772: I/afterTextChanged(4784): position501-27 15:53:43.772: I/afterTextChanged(4784): position001-27 15:53:43.772: I/afterTextChanged(4784): position601-27 15:53:43.772: I/afterTextChanged(4784): position1501-27 15:53:43.772: I/afterTextChanged(4784): position1501-27 15:53:43.772: I/afterTextChanged(4784): position1101-27 15:53:43.772: I/afterTextChanged(4784): position601-27 15:53:43.772: I/afterTextChanged(4784): position201-27 15:53:43.772: I/afterTextChanged(4784): position801-27 15:53:43.772: I/afterTextChanged(4784): position1201-27 15:53:43.772: I/afterTextChanged(4784): position1801-27 15:53:43.772: I/afterTextChanged(4784): position1301-27 15:53:43.772: I/afterTextChanged(4784): position901-27 15:53:43.772: I/afterTextChanged(4784): position401-27 15:53:43.772: I/afterTextChanged(4784): position4
一個操作觸發了很多個監聽,觸發了多個監聽,就必然會導致多個地方的edittext的值被改變,所以才出現錯亂的問題。後來我想到可能是在adapter反覆執行
mHolder.num.addTextChangedListener(new TextWatcher()
的緣故,為了證實自己的想法,我點進去看了下監聽的源碼,發現
public void addTextChangedListener(TextWatcher watcher) { if (mListeners == null) { mListeners = new ArrayList(); } mListeners.add(watcher); }
原來android是用了arraylist把所有加進來的監聽都存起來了,所以才會有一個操作觸發多個監聽的問題。那麼checkbox沒出現這樣的原因應該是每次都重新設定監聽對象了吧
public void setOnClickListener(OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
果然每次都重新設定一個新的。其實根據方法名能夠看出來方法的用途的,一個是add,一個是set。
問題找到那就好辦了,其實adapter中的viewholder對象數目是固定的,在多也只會重用了,所以我們也可以設定和viewholder一樣數量的監聽就行了,可以這樣做
if (convertView == null) { .... class MyTextWatcher implements TextWatcher {public MyTextWatcher() {} @Overridepublic void onTextChanged(CharSequence s, int start,int before, int count) {} @Overridepublic void beforeTextChanged(CharSequence s, int start,int count, int after) {}@Overridepublic void afterTextChanged(Editable s) {if (!TextUtils.isEmpty(s.toString())) {mData.get(position).put(etValue, s.toString()); //Log.i(afterTextChanged, position + position);} }}mHolder.num.addTextChangedListener(new MyTextWatcher(mHolder));}
這樣只在convertview為null的時候才添加監聽,這樣保證了一個edittext只會有一個監聽。再試試,發現還是不對,改變過的值都無法儲存。出了問題還是看log
01-27 16:59:40.512: I/afterTextChanged(12344): position001-27 16:59:40.532: I/afterTextChanged(12344): position101-27 16:59:40.542: I/afterTextChanged(12344): position201-27 16:59:40.552: I/afterTextChanged(12344): position301-27 16:59:42.772: I/afterTextChanged(12344): position401-27 16:59:43.712: I/afterTextChanged(12344): position001-27 16:59:44.502: I/afterTextChanged(12344): position101-27 16:59:45.392: I/afterTextChanged(12344): position201-27 16:59:46.632: I/afterTextChanged(12344): position301-27 16:59:47.492: I/afterTextChanged(12344): position401-27 16:59:47.842: I/afterTextChanged(12344): position001-27 16:59:49.142: I/afterTextChanged(12344): position101-27 16:59:51.662: I/afterTextChanged(12344): position201-27 16:59:52.822: I/afterTextChanged(12344): position301-27 16:59:53.192: I/afterTextChanged(12344): position401-27 17:00:06.662: I/afterTextChanged(12344): position0
看到監聽裡面的position的值是0-4,也就是說無論列表怎麼滑動,position的值都只是convertview為空白的時候初始化好的。這樣當第一項滾出螢幕,底下一項進入螢幕的時候,執行到這段代碼
//這時代碼position肯定是大於4的,假設是5,但mData中位置為5的地方是空值,這就導致第一項和第6項就都被設為1了String value = mData.get(position).get(etValue);if (!TextUtils.isEmpty(value)) {mHolder.num.setText(value);} else {mHolder.num.setText(1);}
想解決這個問題,就得在監聽回調的方法裡能夠動態擷取到position的值。這裡我們可以這樣做
if (convertView == null) {......class MyTextWatcher implements TextWatcher {public MyTextWatcher(ViewHolder holder) {mHolder = holder;}/** * 這裡其實是緩衝了一屏數目的viewholder, 也就是說一屏能顯示10條資料,那麼記憶體中就會有10個viewholder * 在這的作用是通過edittext的tag拿到對應的position,用於儲存edittext的值 */private ViewHolder mHolder;@Overridepublic void onTextChanged(CharSequence s, int start,int before, int count) {}@Overridepublic void beforeTextChanged(CharSequence s, int start,int count, int after) {}@Overridepublic void afterTextChanged(Editable s) {if (!TextUtils.isEmpty(s.toString())) {//通過tag來取position int position = (Integer) mHolder.num.getTag();mData.get(position).put(etValue, s.toString()); //Log.i(afterTextChanged, position + position);}}}mHolder.num.addTextChangedListener(new MyTextWatcher(mHolder));convertView.setTag(mHolder);} else {mHolder = (ViewHolder) convertView.getTag();} //每次用position動態更新edittext的tag mHolder.num.setTag(position);
雖然viewholder就固定的那幾個,但是我們可以通過edittext的tag從而達到動態更新position值的效果。
然後再測試就會發現edittext能夠記住內容了。
二、保證在操作加、減按鈕的時候,操作的edittext對象是沒有錯位的
正常監聽是這樣寫
mHolder.add.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {strNum = mData.get(position).get(etValue);int intnum = Integer.parseInt(strNum);intnum++;mHolder.num.setText( + intnum);}});
但這樣寫在adapter中是行不通的,
因為當觸發回調的時候,edittext已經不是add按鈕註冊監聽時候的那個了,而是最後一個載入的item中的edittext。edittext的對象已經變了就是導致操作edittext錯位的根本原因。這個問題的解決方案我在上篇文章裡面已經說過了,就是把edittext存起來。代碼我就不貼了,下面我會貼出demo連結,感興趣的可以下載看看。有什麼好的建議可以留言分享出來,共同學習。