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

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<CartBean> list;  
  2. private List<Type> typeList = new ArrayList<Type>();  
  3. public CartAdapter(Context context, List<CartBean> 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.                 <strong>typeList.set(position, Type.UnCheck);</strong>  
  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<CartBean> list;  
  2. private List<Type> typeList = new ArrayList<Type>();  
  3. public CartAdapter(Context context, List<CartBean> 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.                 <strong>typeList.set(position, Type.UnCheck);</strong>  
  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<CartBean> list;  
  2. private List<Type> typeList = new ArrayList<Type>();  
  3. public CartAdapter(Context context, List<CartBean> 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.                 <strong>typeList.set(position, Type.UnCheck);</strong>  
  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<Map<String, String>> mData = new ArrayList<Map<String, String>>();  
在初始化的时候先模拟数据

[java]  view plain copy
  1. for (CartBean cartBean : list) {  
  2.         mData.add(new HashMap<String, String>());  
  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.              <strong>Log.i("afterTextChanged""position"+position);</strong>  
  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. <strong>01-27 15:55:56.882: I/afterTextChanged(4784): position4</strong>  
一个操作只触发一个监听

结果发现是这样

[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.            <strong>mListeners = new ArrayList<TextWatcher>();</strong>  
  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.        <strong>getListenerInfo().mOnClickListener = l;</strong>  
  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<CartBean> list;  
  2. private List<Type> typeList = new ArrayList<Type>();  
  3. public CartAdapter(Context context, List<CartBean> 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.                 <strong>typeList.set(position, Type.UnCheck);</strong>  
  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记住状态解决方案(二) 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值