Android中Adapter中edittext,checkbox记住状态解决方案(一)

转载地址:http://blog.csdn.net/footballclub/article/details/43123105




一、问题原因

我们都知道Android中的adapter的view是用了重用机制的,简单地说就是当屏幕中的item占满屏幕以后,我们滑动listview的时候,第一个item退出屏幕,最后一个item进入屏幕

[java]  view plain copy
  1. View getView(final int position, View convertView, ViewGroup parent)  
这个时候,getview中convertview就不为空了,它的值就是第一个item的view,我们一般都会通过ViewHolder来缓存item,就不用重复findid了,直接就可以使用缓存的控件了。

这个缓存机制就是导致问题出现的根本原因。可能有人就说了,只要不用convertview来缓存不就能解决问题了么。对于这样的解决方案,我只能说,能使用这种方案的都是用屁股在思考问题!

二、问题解决

1.上面有两个问题,分别是editText和checkBox,我们先从简单的来,checkbox只需要考虑两种状态就行了,所以就先解决checkBox

首先用enum来标记下选中和未选中这两种状态

[java]  view plain copy
  1. public enum Type {  
  2.     Checked, UnCheck;  
  3. }  
接着在构造方法里面模拟数据,我这里用typeList来保存checkbox的状态,默认都不选中。 [java]  view plain copy
  1. private List list;  
  2. private List typeList = new ArrayList();  
  3. public CartAdapter(Context context, List list) {  
  4.         this.list = list;  
  5.         this.context = context;  
  6.         initData();  
  7.     }  
  8.   
  9.     private void initData() {  
  10.         for (CartBean cartBean : list) {  
  11.             Type type = Type.UnCheck;  
  12.             typeList.add(type);  
  13.         }  
接下来,
[java]  view plain copy
  1.       mHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {  
  2.   
  3.     @Override  
  4.     public void onCheckedChanged(CompoundButton buttonView,  
  5.             boolean isChecked) {  
  6.         if (isChecked) {  
  7.             typeList.set(position, Type.Checked);  
  8.         } else {  
  9.             typeList.set(position, Type.UnCheck);  
  10.         }  
  11.     }  
  12.   
  13. });  
  14.   
  15. f (typeList.get(position) == Type.Checked) {  
  16.     mHolder.cb.setChecked(true);  
  17. else if (typeList.get(position) == Type.UnCheck) {  
  18.     mHolder.cb.setChecked(false);  
  19. else {  
  20.     mHolder.cb.setChecked(false);  
  21. }  
我是先给checkbox注册了监听,当checkbox状态变化的时候,我也相应的改变typeList中对应位置的状态,随后是根据我typeList中存储的状态来设置checkbox是否选中,这样问题就可以解决了。 不过有一点需要强调一下,如果把监听的代码放到设置checkbox状态的代码之后的话,仍然会 出现checkbox状态丢失的问题。 这是为什么?文章开头有说过viewholder会缓存item,所以如果监听写在后面的话,当初始化checkBox属性时,由于可能改变其状态,导致调用了onCheckedChange()方法,而这个监听器是在上一次初始化的时候添加的,那么position就是上一次的,不是本次的position,从而导致typeList中的状态错了。

我就举个例子吧,譬如说我选中了第一个item的checkbox,屏幕能显示10个item,当滑动item的时候,第十一个出来,它重用的第一个item的view,checkbox也是第一个的,监听也是第一个的。那么系统在执行这段代码的时候

[java]  view plain copy
  1. if (typeList.get(position) == Type.Checked) {  
  2.             mHolder.cb.setChecked(true);  
  3.         } else if (typeList.get(position) == Type.UnCheck) {  
  4.             mHolder.cb.setChecked(false);  
  5.         } else {  
  6.             mHolder.cb.setChecked(false);  
  7.         }  

就得把这个checkbox的状态从选中改成未选中的状态,这样就会触发监听事件

[java]  view plain copy
  1. public void onCheckedChanged(CompoundButton buttonView,  
  2.                 boolean isChecked) {  
  3.             if (isChecked) {  
  4.                 typeList.set(position, Type.Checked);  
  5.             } else {  
  6.                 typeList.set(position, Type.UnCheck);  
  7.             }  
  8.         }  
根据这段代码就会把position为0的位置的checkbox的状态设为uncheck。这样一来,当我们滑回去,第一个item重新进入屏幕的时候,我们会发现item的状态丢了,又变成未选中了。

所以为了避免这个问题,每次都必须先注册监听,再根据typelist中存储的状态来设置item是否选中。


checkbox的问题解决了,接下来就是editText的了,这个有点麻烦,大家看我文章开头的图片,我不仅要保存edittext里面的值,还要保证在操作加、减button的时候,操作的edittext对象是正确的,最后还有焦点问题。


2.先说说焦点问题,就是在listview中我们点击editText会发现,软键盘是弹出来了,光标也移到EditText上面了但是,不管输入什么editText里面都不显示!对于这个,我们可以在Activity里面这么配置

android:windowSoftInputMode="adjustPan",这个就是设置输入法的弹出模式,不挤压布局,输入法会覆盖在布局之上,同时editText也会获得焦点。其实之前editText不能输入也是因为没有焦点的缘故,这样就可以解决问题了。也不需要自己动态的获取editText的焦点了。

这个时候edittext能够输入内容了,接下来就是保存内容和button能操作对位的edittext。

我的做法是把edittext这个对象保存在list中,这样的做法不是很好,因为edittext不能序列化存储,我们没法把它持久化存储,这就造成这个对象容易丢。我们都知道系统在横竖屏切换的时候,Activity会重启或者在应用长时间处于后台的时候,系统会把Activity给清掉,等用户回到应用的时候会再重启Activity,如果我们无法存储状态的话,从用户体验上来说就不是很好。或许可以把这个list放在application中静态存储,就和项目中管理所有Activity的list一样。

还没有说完,下篇文章会有demo。


Android中Adapter中edittext,checkbox记住状态解决方案(二) 



一、问题原因

我们都知道Android中的adapter的view是用了重用机制的,简单地说就是当屏幕中的item占满屏幕以后,我们滑动listview的时候,第一个item退出屏幕,最后一个item进入屏幕

[java]  view plain copy
  1. View getView(final int position, View convertView, ViewGroup parent)  
这个时候,getview中convertview就不为空了,它的值就是第一个item的view,我们一般都会通过ViewHolder来缓存item,就不用重复findid了,直接就可以使用缓存的控件了。

这个缓存机制就是导致问题出现的根本原因。可能有人就说了,只要不用convertview来缓存不就能解决问题了么。对于这样的解决方案,我只能说,能使用这种方案的都是用屁股在思考问题!

二、问题解决

1.上面有两个问题,分别是editText和checkBox,我们先从简单的来,checkbox只需要考虑两种状态就行了,所以就先解决checkBox

首先用enum来标记下选中和未选中这两种状态

[java]  view plain copy
  1. public enum Type {  
  2.     Checked, UnCheck;  
  3. }  
接着在构造方法里面模拟数据,我这里用typeList来保存checkbox的状态,默认都不选中。 [java]  view plain copy
  1. private List list;  
  2. private List typeList = new ArrayList();  
  3. public CartAdapter(Context context, List list) {  
  4.         this.list = list;  
  5.         this.context = context;  
  6.         initData();  
  7.     }  
  8.   
  9.     private void initData() {  
  10.         for (CartBean cartBean : list) {  
  11.             Type type = Type.UnCheck;  
  12.             typeList.add(type);  
  13.         }  
接下来,
[java]  view plain copy
  1.       mHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {  
  2.   
  3.     @Override  
  4.     public void onCheckedChanged(CompoundButton buttonView,  
  5.             boolean isChecked) {  
  6.         if (isChecked) {  
  7.             typeList.set(position, Type.Checked);  
  8.         } else {  
  9.             typeList.set(position, Type.UnCheck);  
  10.         }  
  11.     }  
  12.   
  13. });  
  14.   
  15. f (typeList.get(position) == Type.Checked) {  
  16.     mHolder.cb.setChecked(true);  
  17. else if (typeList.get(position) == Type.UnCheck) {  
  18.     mHolder.cb.setChecked(false);  
  19. else {  
  20.     mHolder.cb.setChecked(false);  
  21. }  
我是先给checkbox注册了监听,当checkbox状态变化的时候,我也相应的改变typeList中对应位置的状态,随后是根据我typeList中存储的状态来设置checkbox是否选中,这样问题就可以解决了。 不过有一点需要强调一下,如果把监听的代码放到设置checkbox状态的代码之后的话,仍然会 出现checkbox状态丢失的问题。 这是为什么?文章开头有说过viewholder会缓存item,所以如果监听写在后面的话,当初始化checkBox属性时,由于可能改变其状态,导致调用了onCheckedChange()方法,而这个监听器是在上一次初始化的时候添加的,那么position就是上一次的,不是本次的position,从而导致typeList中的状态错了。

我就举个例子吧,譬如说我选中了第一个item的checkbox,屏幕能显示10个item,当滑动item的时候,第十一个出来,它重用的第一个item的view,checkbox也是第一个的,监听也是第一个的。那么系统在执行这段代码的时候

[java]  view plain copy
  1. if (typeList.get(position) == Type.Checked) {  
  2.             mHolder.cb.setChecked(true);  
  3.         } else if (typeList.get(position) == Type.UnCheck) {  
  4.             mHolder.cb.setChecked(false);  
  5.         } else {  
  6.             mHolder.cb.setChecked(false);  
  7.         }  

就得把这个checkbox的状态从选中改成未选中的状态,这样就会触发监听事件

[java]  view plain copy
  1. public void onCheckedChanged(CompoundButton buttonView,  
  2.                 boolean isChecked) {  
  3.             if (isChecked) {  
  4.                 typeList.set(position, Type.Checked);  
  5.             } else {  
  6.                 typeList.set(position, Type.UnCheck);  
  7.             }  
  8.         }  
根据这段代码就会把position为0的位置的checkbox的状态设为uncheck。这样一来,当我们滑回去,第一个item重新进入屏幕的时候,我们会发现item的状态丢了,又变成未选中了。

所以为了避免这个问题,每次都必须先注册监听,再根据typelist中存储的状态来设置item是否选中。


checkbox的问题解决了,接下来就是editText的了,这个有点麻烦,大家看我文章开头的图片,我不仅要保存edittext里面的值,还要保证在操作加、减button的时候,操作的edittext对象是正确的,最后还有焦点问题。


2.先说说焦点问题,就是在listview中我们点击editText会发现,软键盘是弹出来了,光标也移到EditText上面了但是,不管输入什么editText里面都不显示!对于这个,我们可以在Activity里面这么配置

android:windowSoftInputMode="adjustPan",这个就是设置输入法的弹出模式,不挤压布局,输入法会覆盖在布局之上,同时editText也会获得焦点。其实之前editText不能输入也是因为没有焦点的缘故,这样就可以解决问题了。也不需要自己动态的获取editText的焦点了。

这个时候edittext能够输入内容了,接下来就是保存内容和button能操作对位的edittext。

我的做法是把edittext这个对象保存在list中,这样的做法不是很好,因为edittext不能序列化存储,我们没法把它持久化存储,这就造成这个对象容易丢。我们都知道系统在横竖屏切换的时候,Activity会重启或者在应用长时间处于后台的时候,系统会把Activity给清掉,等用户回到应用的时候会再重启Activity,如果我们无法存储状态的话,从用户体验上来说就不是很好。或许可以把这个list放在application中静态存储,就和项目中管理所有Activity的list一样。

还没有说完,下篇文章会有demo。


Android中Adapter中edittext,checkbox记住状态解决方案(二) 



Android中Adapter中edittext,checkbox记住状态解决方案(二)


一、问题原因

我们都知道Android中的adapter的view是用了重用机制的,简单地说就是当屏幕中的item占满屏幕以后,我们滑动listview的时候,第一个item退出屏幕,最后一个item进入屏幕

[java]  view plain copy
  1. View getView(final int position, View convertView, ViewGroup parent)  
这个时候,getview中convertview就不为空了,它的值就是第一个item的view,我们一般都会通过ViewHolder来缓存item,就不用重复findid了,直接就可以使用缓存的控件了。

这个缓存机制就是导致问题出现的根本原因。可能有人就说了,只要不用convertview来缓存不就能解决问题了么。对于这样的解决方案,我只能说,能使用这种方案的都是用屁股在思考问题!

二、问题解决

1.上面有两个问题,分别是editText和checkBox,我们先从简单的来,checkbox只需要考虑两种状态就行了,所以就先解决checkBox

首先用enum来标记下选中和未选中这两种状态

[java]  view plain copy
  1. public enum Type {  
  2.     Checked, UnCheck;  
  3. }  
接着在构造方法里面模拟数据,我这里用typeList来保存checkbox的状态,默认都不选中。 [java]  view plain copy
  1. private List list;  
  2. private List typeList = new ArrayList();  
  3. public CartAdapter(Context context, List list) {  
  4.         this.list = list;  
  5.         this.context = context;  
  6.         initData();  
  7.     }  
  8.   
  9.     private void initData() {  
  10.         for (CartBean cartBean : list) {  
  11.             Type type = Type.UnCheck;  
  12.             typeList.add(type);  
  13.         }  
接下来,
[java]  view plain copy
  1.       mHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {  
  2.   
  3.     @Override  
  4.     public void onCheckedChanged(CompoundButton buttonView,  
  5.             boolean isChecked) {  
  6.         if (isChecked) {  
  7.             typeList.set(position, Type.Checked);  
  8.         } else {  
  9.             typeList.set(position, Type.UnCheck);  
  10.         }  
  11.     }  
  12.   
  13. });  
  14.   
  15. f (typeList.get(position) == Type.Checked) {  
  16.     mHolder.cb.setChecked(true);  
  17. else if (typeList.get(position) == Type.UnCheck) {  
  18.     mHolder.cb.setChecked(false);  
  19. else {  
  20.     mHolder.cb.setChecked(false);  
  21. }  
我是先给checkbox注册了监听,当checkbox状态变化的时候,我也相应的改变typeList中对应位置的状态,随后是根据我typeList中存储的状态来设置checkbox是否选中,这样问题就可以解决了。 不过有一点需要强调一下,如果把监听的代码放到设置checkbox状态的代码之后的话,仍然会 出现checkbox状态丢失的问题。 这是为什么?文章开头有说过viewholder会缓存item,所以如果监听写在后面的话,当初始化checkBox属性时,由于可能改变其状态,导致调用了onCheckedChange()方法,而这个监听器是在上一次初始化的时候添加的,那么position就是上一次的,不是本次的position,从而导致typeList中的状态错了。

我就举个例子吧,譬如说我选中了第一个item的checkbox,屏幕能显示10个item,当滑动item的时候,第十一个出来,它重用的第一个item的view,checkbox也是第一个的,监听也是第一个的。那么系统在执行这段代码的时候

[java]  view plain copy
  1. if (typeList.get(position) == Type.Checked) {  
  2.             mHolder.cb.setChecked(true);  
  3.         } else if (typeList.get(position) == Type.UnCheck) {  
  4.             mHolder.cb.setChecked(false);  
  5.         } else {  
  6.             mHolder.cb.setChecked(false);  
  7.         }  

就得把这个checkbox的状态从选中改成未选中的状态,这样就会触发监听事件

[java]  view plain copy
  1. public void onCheckedChanged(CompoundButton buttonView,  
  2.                 boolean isChecked) {  
  3.             if (isChecked) {  
  4.                 typeList.set(position, Type.Checked);  
  5.             } else {  
  6.                 typeList.set(position, Type.UnCheck);  
  7.             }  
  8.         }  
根据这段代码就会把position为0的位置的checkbox的状态设为uncheck。这样一来,当我们滑回去,第一个item重新进入屏幕的时候,我们会发现item的状态丢了,又变成未选中了。

所以为了避免这个问题,每次都必须先注册监听,再根据typelist中存储的状态来设置item是否选中。


checkbox的问题解决了,接下来就是editText的了,这个有点麻烦,大家看我文章开头的图片,我不仅要保存edittext里面的值,还要保证在操作加、减button的时候,操作的edittext对象是正确的,最后还有焦点问题。


2.先说说焦点问题,就是在listview中我们点击editText会发现,软键盘是弹出来了,光标也移到EditText上面了但是,不管输入什么editText里面都不显示!对于这个,我们可以在Activity里面这么配置

android:windowSoftInputMode="adjustPan",这个就是设置输入法的弹出模式,不挤压布局,输入法会覆盖在布局之上,同时editText也会获得焦点。其实之前editText不能输入也是因为没有焦点的缘故,这样就可以解决问题了。也不需要自己动态的获取editText的焦点了。

这个时候edittext能够输入内容了,接下来就是保存内容和button能操作对位的edittext。

我的做法是把edittext这个对象保存在list中,这样的做法不是很好,因为edittext不能序列化存储,我们没法把它持久化存储,这就造成这个对象容易丢。我们都知道系统在横竖屏切换的时候,Activity会重启或者在应用长时间处于后台的时候,系统会把Activity给清掉,等用户回到应用的时候会再重启Activity,如果我们无法存储状态的话,从用户体验上来说就不是很好。或许可以把这个list放在application中静态存储,就和项目中管理所有Activity的list一样。

还没有说完,下篇文章会有demo。


Android中Adapter中edittext,checkbox记住状态解决方案(二) 



Android中Adapter中edittext,checkbox记住状态解决方案(一)

在上篇文章解决了adapter中checkbox记住状态和edittext可编辑的问题,下面谈谈怎么解决记住edittext中的内容和保证在操作加、减按钮的时候,操作的edittext对象是没有错位的问题。

一、记住edittext中的内容

解决的思路和checkbox差不多,不过还是有些差别,checkbox只有两种状态,而edittext的值是不固定的。checkbox我们是用一个enum类型的list来保存状态的,所以edittext就不能了,可以用map和实体类,我为了方便就用了hashMap。

[java]  view plain copy
  1. // 用来存储editext中数据的list  
  2. private List> mData = new ArrayList>();  
在初始化的时候先模拟数据

[java]  view plain copy
  1. for (CartBean cartBean : list) {  
  2.         mData.add(new HashMap());  
  3.     }  

edittext的监听,并且有个log

[java]  view plain copy
  1.          mHolder.num.addTextChangedListener(new TextWatcher() {  
  2.   
  3. @Override  
  4. public void onTextChanged(CharSequence s, int start, int before,  
  5.         int count) {  
  6.   
  7. }  
  8.   
  9. @Override  
  10. public void beforeTextChanged(CharSequence s, int start, int count,  
  11.         int after) {  
  12.   
  13. }  
  14.   
  15. @Override  
  16. public void afterTextChanged(Editable s) {  
  17.     if (!TextUtils.isEmpty(s.toString())) {  
  18.                                     
  19.         if(!TextUtils.isEmpty(s.toString())){  
  20.             mData.get(position).put(etValue,  
  21.                     s.toString());  
  22.              Log.i("afterTextChanged""position"+position);  
  23.         }  
  24.     }  
  25. }  
  26. );  
 根据list对应位置的position取出edittext的值

[java]  view plain copy
  1.     String value = mData.get(position).get(etValue);  
  2. f (!TextUtils.isEmpty(value)) {  
  3. mHolder.num.setText(value);  
  4.  else {  
  5. mHolder.num.setText("1");  
代码出来了,然后在测试的时候可以发现仍然会出现错乱的问题,后来当多次来回滑动列表之后,再滑动列表,让第一个刚好完全出来,下一个刚好进来,这时候第一个item会被刚进来的item重用,执行上面的赋值代码的时候log应该是这样

[java]  view plain copy
  1. 01-27 15:55:46.612: I/afterTextChanged(4784): position0  
  2. 01-27 15:55:46.622: I/afterTextChanged(4784): position1  
  3. 01-27 15:55:46.632: I/afterTextChanged(4784): position2  
  4. 01-27 15:55:46.642: I/afterTextChanged(4784): position3  
  5. 01-27 15:55:46.642: D/AbsListView(4784): unregisterIRListener() is called   
  6. 01-27 15:55:46.642: D/AbsListView(4784): unregisterIRListener() is called   
  7. 01-27 15:55:46.662: D/AbsListView(4784): unregisterIRListener() is called   
  8. 01-27 15:55:46.682: D/AbsListView(4784): unregisterIRListener() is called   
  9. 01-27 15:55:56.882: I/afterTextChanged(4784): position4  
一个操作只触发一个监听

结果发现是这样

[java]  view plain copy
  1. 01-27 15:53:43.772: I/afterTextChanged(4784): position0  
  2. 01-27 15:53:43.772: I/afterTextChanged(4784): position5  
  3. 01-27 15:53:43.772: I/afterTextChanged(4784): position5  
  4. 01-27 15:53:43.772: I/afterTextChanged(4784): position9  
  5. 01-27 15:53:43.772: I/afterTextChanged(4784): position5  
  6. 01-27 15:53:43.772: I/afterTextChanged(4784): position0  
  7. 01-27 15:53:43.772: I/afterTextChanged(4784): position6  
  8. 01-27 15:53:43.772: I/afterTextChanged(4784): position15  
  9. 01-27 15:53:43.772: I/afterTextChanged(4784): position15  
  10. 01-27 15:53:43.772: I/afterTextChanged(4784): position11  
  11. 01-27 15:53:43.772: I/afterTextChanged(4784): position6  
  12. 01-27 15:53:43.772: I/afterTextChanged(4784): position2  
  13. 01-27 15:53:43.772: I/afterTextChanged(4784): position8  
  14. 01-27 15:53:43.772: I/afterTextChanged(4784): position12  
  15. 01-27 15:53:43.772: I/afterTextChanged(4784): position18  
  16. 01-27 15:53:43.772: I/afterTextChanged(4784): position13  
  17. 01-27 15:53:43.772: I/afterTextChanged(4784): position9  
  18. 01-27 15:53:43.772: I/afterTextChanged(4784): position4  
  19. 01-27 15:53:43.772: I/afterTextChanged(4784): position4  
一个操作触发了很多个监听,触发了多个监听,就必然会导致多个地方的edittext的值被改变,所以才出现错乱的问题。后来我想到可能是在adapter反复执行

[java]  view plain copy
  1. mHolder.num.addTextChangedListener(new TextWatcher()  
的缘故,为了证实自己的想法,我点进去看了下监听的源码,发现

[java]  view plain copy
  1. public void addTextChangedListener(TextWatcher watcher) {  
  2.        if (mListeners == null) {  
  3.            mListeners = new ArrayList();  
  4.        }  
  5.   
  6.        mListeners.add(watcher);  
  7.    }  
原来android是用了arraylist把所有加进来的监听都存起来了,所以才会有一个操作触发多个监听的问题。那么checkbox没出现这样的原因应该是每次都重新设置监听对象了吧

[java]  view plain copy
  1. public void setOnClickListener(OnClickListener l) {  
  2.        if (!isClickable()) {  
  3.            setClickable(true);  
  4.        }  
  5.        getListenerInfo().mOnClickListener = l;  
  6.    }  
果然每次都重新设置一个新的。其实根据方法名能够看出来方法的用途的,一个是add,一个是set。
问题找到那就好办了,其实adapter中的viewholder对象数目是固定的,在多也只会重用了,所以我们也可以设置和viewholder一样数量的监听就行了,可以这样做

[html]  view plain copy
  1. if (convertView == null) {  
  2.       ....  
  3.       class MyTextWatcher implements TextWatcher {  
  4.         public MyTextWatcher() {}  
  5.   
  6.   
  7.                @Override  
  8.             public void onTextChanged(CharSequence s, int start,  
  9.                 int before, int count) {  
  10.             }  
  11.   
  12.                @Override  
  13.             public void beforeTextChanged(CharSequence s, int start,  
  14.                 int count, int after) {  
  15.             }  
  16.   
  17.             @Override  
  18.             public void afterTextChanged(Editable s) {  
  19.                 if (!TextUtils.isEmpty(s.toString())) {  
  20.                     mData.get(position).put(etValue, s.toString()); //  
  21.                     Log.i("afterTextChanged", "position" + position);  
  22.                 }  
  23.                }  
  24.     }  
  25.   
  26.             mHolder.num.addTextChangedListener(new MyTextWatcher(mHolder));  
  27. }  
这样只在convertview为null的时候才添加监听,这样保证了一个edittext只会有一个监听。再试试,发现还是不对,改变过的值都无法保存。出了问题还是看log

[java]  view plain copy
  1. 01-27 16:59:40.512: I/afterTextChanged(12344): position0  
  2. 01-27 16:59:40.532: I/afterTextChanged(12344): position1  
  3. 01-27 16:59:40.542: I/afterTextChanged(12344): position2  
  4. 01-27 16:59:40.552: I/afterTextChanged(12344): position3  
  5. 01-27 16:59:42.772: I/afterTextChanged(12344): position4  
  6. 01-27 16:59:43.712: I/afterTextChanged(12344): position0  
  7. 01-27 16:59:44.502: I/afterTextChanged(12344): position1  
  8. 01-27 16:59:45.392: I/afterTextChanged(12344): position2  
  9. 01-27 16:59:46.632: I/afterTextChanged(12344): position3  
  10. 01-27 16:59:47.492: I/afterTextChanged(12344): position4  
  11. 01-27 16:59:47.842: I/afterTextChanged(12344): position0  
  12. 01-27 16:59:49.142: I/afterTextChanged(12344): position1  
  13. 01-27 16:59:51.662: I/afterTextChanged(12344): position2  
  14. 01-27 16:59:52.822: I/afterTextChanged(12344): position3  
  15. 01-27 16:59:53.192: I/afterTextChanged(12344): position4  
  16. 01-27 17:00:06.662: I/afterTextChanged(12344): position0  
看到监听里面的position的值是0-4,也就是说无论列表怎么滑动,position的值都只是convertview为空的时候初始化好的。这样当第一项滚出屏幕,底下一项进入屏幕的时候,执行到这段代码

[java]  view plain copy
  1. //这时代码position肯定是大于4的,假设是5,但mData中位置为5的地方是空值,这就导致第一项和第6项就都被设为1了  
  2. String value = mData.get(position).get(etValue);  
  3.         if (!TextUtils.isEmpty(value)) {  
  4.             mHolder.num.setText(value);  
  5.         } else {  
  6.             mHolder.num.setText("1");  
  7.         }  
想解决这个问题,就得在监听回调的方法里能够动态获取到position的值。这里我们可以这样做

[html]  view plain copy
  1.      if (convertView == null) {  
  2. ......  
  3.   
  4. class MyTextWatcher implements TextWatcher {  
  5.     public MyTextWatcher(ViewHolder holder) {  
  6.         mHolder = holder;  
  7.     }  
  8.   
  9.     /**  
  10.      * 这里其实是缓存了一屏数目的viewholder, 也就是说一屏能显示10条数据,那么内存中就会有10个viewholder  
  11.      * 在这的作用是通过edittext的tag拿到对应的position,用于储存edittext的值  
  12.      */  
  13.     private ViewHolder mHolder;  
  14.   
  15.     @Override  
  16.     public void onTextChanged(CharSequence s, int start,  
  17.             int before, int count) {  
  18.     }  
  19.   
  20.     @Override  
  21.     public void beforeTextChanged(CharSequence s, int start,  
  22.             int count, int after) {  
  23.     }  
  24.   
  25.     @Override  
  26.     public void afterTextChanged(Editable s) {  
  27.         if (!TextUtils.isEmpty(s.toString())) {  
  28.             //通过tag来取position<strong>  
  29.          int position = (Integer) mHolder.num.getTag();strong>  
  30.             mData.get(position).put(etValue, s.toString()); //  
  31.             Log.i("afterTextChanged", "position" + position);  
  32.         }  
  33.     }  
  34. }  
  35.   
  36. mHolder.num.addTextChangedListener(new MyTextWatcher(mHolder));  
  37.   
  38. convertView.setTag(mHolder);  
  39.  else {  
  40. mHolder = (ViewHolder) convertView.getTag();  
  41.   
  42.     //每次用position动态更新edittext的tag  
  43.    <strong> mHolder.num.setTag(position);strong>  
虽然viewholder就固定的那几个,但是我们可以通过edittext的tag从而达到动态更新position值的效果。

然后再测试就会发现edittext能够记住内容了。

二、保证在操作加、减按钮的时候,操作的edittext对象是没有错位的

正常监听是这样写

[java]  view plain copy
  1. mHolder.add.setOnClickListener(new OnClickListener() {  
  2.   
  3.             @Override  
  4.             public void onClick(View v) {  
  5.                 strNum = mData.get(position).get(etValue);  
  6.                 int intnum = Integer.parseInt(strNum);  
  7.                 intnum++;  
  8.                 mHolder.num.setText("" + intnum);  
  9.             }  
  10.         });  
但这样写在adapter中是行不通的, 因为当触发回调的时候,edittext已经不是add按钮注册监听时候的那个了,而是最后一个加载的item中的edittext。edittext的对象已经变了就是导致操作edittext错位的根本原因。 这个问题的解决方法我在上篇文章里面已经说过了,就是把edittext存起来。代码我就不贴了,下面我会贴出demo链接,感兴趣的可以下载看看。有什么好的建议可以留言分享出来,共同学习。

点击前去下载Demo



一、问题原因

我们都知道Android中的adapter的view是用了重用机制的,简单地说就是当屏幕中的item占满屏幕以后,我们滑动listview的时候,第一个item退出屏幕,最后一个item进入屏幕

[java]  view plain copy
  1. View getView(final int position, View convertView, ViewGroup parent)  
这个时候,getview中convertview就不为空了,它的值就是第一个item的view,我们一般都会通过ViewHolder来缓存item,就不用重复findid了,直接就可以使用缓存的控件了。

这个缓存机制就是导致问题出现的根本原因。可能有人就说了,只要不用convertview来缓存不就能解决问题了么。对于这样的解决方案,我只能说,能使用这种方案的都是用屁股在思考问题!

二、问题解决

1.上面有两个问题,分别是editText和checkBox,我们先从简单的来,checkbox只需要考虑两种状态就行了,所以就先解决checkBox

首先用enum来标记下选中和未选中这两种状态

[java]  view plain copy
  1. public enum Type {  
  2.     Checked, UnCheck;  
  3. }  
接着在构造方法里面模拟数据,我这里用typeList来保存checkbox的状态,默认都不选中。 [java]  view plain copy
  1. private List list;  
  2. private List typeList = new ArrayList();  
  3. public CartAdapter(Context context, List list) {  
  4.         this.list = list;  
  5.         this.context = context;  
  6.         initData();  
  7.     }  
  8.   
  9.     private void initData() {  
  10.         for (CartBean cartBean : list) {  
  11.             Type type = Type.UnCheck;  
  12.             typeList.add(type);  
  13.         }  
接下来,
[java]  view plain copy
  1.       mHolder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {  
  2.   
  3.     @Override  
  4.     public void onCheckedChanged(CompoundButton buttonView,  
  5.             boolean isChecked) {  
  6.         if (isChecked) {  
  7.             typeList.set(position, Type.Checked);  
  8.         } else {  
  9.             typeList.set(position, Type.UnCheck);  
  10.         }  
  11.     }  
  12.   
  13. });  
  14.   
  15. f (typeList.get(position) == Type.Checked) {  
  16.     mHolder.cb.setChecked(true);  
  17. else if (typeList.get(position) == Type.UnCheck) {  
  18.     mHolder.cb.setChecked(false);  
  19. else {  
  20.     mHolder.cb.setChecked(false);  
  21. }  
我是先给checkbox注册了监听,当checkbox状态变化的时候,我也相应的改变typeList中对应位置的状态,随后是根据我typeList中存储的状态来设置checkbox是否选中,这样问题就可以解决了。 不过有一点需要强调一下,如果把监听的代码放到设置checkbox状态的代码之后的话,仍然会 出现checkbox状态丢失的问题。 这是为什么?文章开头有说过viewholder会缓存item,所以如果监听写在后面的话,当初始化checkBox属性时,由于可能改变其状态,导致调用了onCheckedChange()方法,而这个监听器是在上一次初始化的时候添加的,那么position就是上一次的,不是本次的position,从而导致typeList中的状态错了。

我就举个例子吧,譬如说我选中了第一个item的checkbox,屏幕能显示10个item,当滑动item的时候,第十一个出来,它重用的第一个item的view,checkbox也是第一个的,监听也是第一个的。那么系统在执行这段代码的时候

[java]  view plain copy
  1. if (typeList.get(position) == Type.Checked) {  
  2.             mHolder.cb.setChecked(true);  
  3.         } else if (typeList.get(position) == Type.UnCheck) {  
  4.             mHolder.cb.setChecked(false);  
  5.         } else {  
  6.             mHolder.cb.setChecked(false);  
  7.         }  

就得把这个checkbox的状态从选中改成未选中的状态,这样就会触发监听事件

[java]  view plain copy
  1. public void onCheckedChanged(CompoundButton buttonView,  
  2.                 boolean isChecked) {  
  3.             if (isChecked) {  
  4.                 typeList.set(position, Type.Checked);  
  5.             } else {  
  6.                 typeList.set(position, Type.UnCheck);  
  7.             }  
  8.         }  
根据这段代码就会把position为0的位置的checkbox的状态设为uncheck。这样一来,当我们滑回去,第一个item重新进入屏幕的时候,我们会发现item的状态丢了,又变成未选中了。

所以为了避免这个问题,每次都必须先注册监听,再根据typelist中存储的状态来设置item是否选中。


checkbox的问题解决了,接下来就是editText的了,这个有点麻烦,大家看我文章开头的图片,我不仅要保存edittext里面的值,还要保证在操作加、减button的时候,操作的edittext对象是正确的,最后还有焦点问题。


2.先说说焦点问题,就是在listview中我们点击editText会发现,软键盘是弹出来了,光标也移到EditText上面了但是,不管输入什么editText里面都不显示!对于这个,我们可以在Activity里面这么配置

android:windowSoftInputMode="adjustPan",这个就是设置输入法的弹出模式,不挤压布局,输入法会覆盖在布局之上,同时editText也会获得焦点。其实之前editText不能输入也是因为没有焦点的缘故,这样就可以解决问题了。也不需要自己动态的获取editText的焦点了。

这个时候edittext能够输入内容了,接下来就是保存内容和button能操作对位的edittext。

我的做法是把edittext这个对象保存在list中,这样的做法不是很好,因为edittext不能序列化存储,我们没法把它持久化存储,这就造成这个对象容易丢。我们都知道系统在横竖屏切换的时候,Activity会重启或者在应用长时间处于后台的时候,系统会把Activity给清掉,等用户回到应用的时候会再重启Activity,如果我们无法存储状态的话,从用户体验上来说就不是很好。或许可以把这个list放在application中静态存储,就和项目中管理所有Activity的list一样。

还没有说完,下篇文章会有demo。


Android中Adapter中edittext,checkbox记住状态解决方案(二) 


更多相关文章

  1. Android(安卓)ListView内部组件事件响应
  2. Android(安卓)之 Notification
  3. Android入门扫盲之一
  4. android selector
  5. 浅谈android的selector背景选择器
  6. Android(安卓)打开本地pdf文件,android 加载pdf文件
  7. Android的selector,背景选择器
  8. 当忘记Android发布签名和别名的时候
  9. android GrantPermissionsActivity 详解

随机推荐

  1. android kernel启动学习笔记
  2. 风投称Android将像Windows一样主宰移动市
  3. Android签名漏洞分析
  4. Android自定义权限的使用
  5. Android Schema的妙用
  6. ANDROID – TOOLBAR STEP BY STEP
  7. Android聊天室(服务器)
  8. android:viewpager实现图片循环滑动+索引
  9. Android Studio App设置线性布局LinerLay
  10. 一些关于 Activity 的技巧