GridView多选错误范例解析
来源:互联网 发布:阿里云系统 编辑:程序博客网 时间:2024/06/04 19:26
最近在写一个程序,需要使用GridView显示很多图片的缩略图。想要实现的效果是长按
进入多选状态,在多选状态点击各个图片能够勾选,并得到所有选择的图片。
最初参考的是这篇文章
http://blog.csdn.net/zhouyuanjing/article/details/8372686
文章里作者提供了源码,为分析方便,贴在下面。
其中GridItem是列表项的View类,实现了Checkable接口
作者的基本思路就是自己维护一个HashMap,用于记录哪些位置的图片被选中了,然后
根据选中状态在Adapter的getView方法中设置图片的外观。如(1)处所示。
但是经过我的试验,发现这种方法不仅费事,而且极易出错,可以说是吃力不讨好。
因为AbsListView本身就有一个变量来记录各项的选中状态:
SparseBooleanArray mCheckStates;
SparseBooleanArray是android.util包中的一个类,API说它
SparseBooleanArrays map integers to booleans。因此它也相当于是一个Map,将int
类型的key对应到boolean值上。实际上就记录了GridView中的各个position的选择状态
true或者false.
如果我们只是没有用GridView自带的机制来处理选中状态,那还好,顶多算费事点。
但是除了费事之外,自己维护选中状态会随之带来很多问题。
虽然我们在getView中调用GridItem的setChecked方法设置好了列表项的状态(这个状态
依据的是自定义的mSelectMap),但getView返回后,GridView自身还会去调用一次
GridItem的setChecked方法再次设置列表项的状态,而这次调用,依据的是mCheckState
中记录的状态。这个状态,很可能跟mSelectMap不一样。
我们可以做试验来验证。
首先在GridItem的setCheced()方法,getView方法中都添加Log代码,如(2)(3)所示
第一次长按第0项,Logcat输出为:
1、08-05 16:27:13.416: D/GridItem(1812): setChecked: false
2、08-05 16:27:13.416: D/GridItem(1812): setChecked: false
3、08-05 16:27:13.416: D/GridItem(1812): setChecked: false
4、08-05 16:27:14.036: D/HomeActivity(1812): getView: 0,true
5、08-05 16:27:14.036: D/GridItem(1812): setChecked: true
6、08-05 16:27:14.056: D/HomeActivity(1812): getView: 0,true
7、08-05 16:27:14.056: D/GridItem(1812): setChecked: true
8、08-05 16:27:14.076: D/GridItem(1812): setChecked: true
9、08-05 16:27:14.086: D/HomeActivity(1812): getView: 1,false
10、08-05 16:27:14.086: D/GridItem(1812): setChecked: false
11、08-05 16:27:14.086: D/GridItem(1812): setChecked: false
12、08-05 16:27:14.096: D/HomeActivity(1812): getView: 2,false
13、08-05 16:27:14.096: D/GridItem(1812): setChecked: false
14、08-05 16:27:14.096: D/GridItem(1812): setChecked: false
其中1~3行是GridView调用的setChecked,全为false,可以认为是建立初始状态
第5行是我们自己在getView中调用的setChecked;
第6,7行与第4、5行一样,好像每次第一项都会处理两次,不知何故;
第8行是GridView在getView(0)之后调用的setChecked;
同理,第11、14行分别是系统在每次getView之后调用的setChecked。
好,保持第0项为选中状态,我们点击返回退出多选模式,Logcat输出为:
1、08-05 16:34:30.897: D/HomeActivity(1812): getView: 0,true
2、08-05 16:34:30.897: D/GridItem(1812): setChecked: true
3、08-05 16:34:30.907: D/HomeActivity(1812): getView: 0,true
4、08-05 16:34:30.907: D/GridItem(1812): setChecked: true
5、08-05 16:34:30.907: D/GridItem(1812): setChecked: false
6、08-05 16:34:30.917: D/HomeActivity(1812): getView: 1,false
7、08-05 16:34:30.917: D/GridItem(1812): setChecked: false
8、08-05 16:34:30.917: D/GridItem(1812): setChecked: false
9、08-05 16:34:30.917: D/HomeActivity(1812): getView: 2,false
10、08-05 16:34:30.927: D/GridItem(1812): setChecked: false
11、08-05 16:34:30.937: D/GridItem(1812): setChecked: false
对于第一项,第3、4行是重复第1、2行的;
第5行就有意思了,是由GridView调用的setChecked,为false。
长按第2项再次进入多选,Logcat输出如下:
1、08-05 16:39:21.666: D/GridItem(1812): setChecked: false
2、08-05 16:39:21.666: D/GridItem(1812): setChecked: false
3、08-05 16:39:21.688: D/GridItem(1812): setChecked: false
4、08-05 16:39:22.306: D/HomeActivity(1812): getView: 0,true
5、08-05 16:39:22.316: D/GridItem(1812): setChecked: true
6、08-05 16:39:22.348: D/HomeActivity(1812): getView: 0,true
7、08-05 16:39:22.348: D/GridItem(1812): setChecked: true
8、08-05 16:39:22.356: D/GridItem(1812): setChecked: false
9、08-05 16:39:22.356: D/HomeActivity(1812): getView: 1,false
10、08-05 16:39:22.366: D/GridItem(1812): setChecked: false
11、08-05 16:39:22.366: D/GridItem(1812): setChecked: false
12、08-05 16:39:22.366: D/HomeActivity(1812): getView: 2,true
13、08-05 16:39:22.409: D/GridItem(1812): setChecked: true
14、08-05 16:39:22.409: D/GridItem(1812): setChecked: true
第1、2、3行为GridView在设置初始状态
第6、7行重复第4、5行;
第8行和第7行仍旧是不一致的,代表mSelectMap和GridView在第0项是否选择上仍旧是
不一样的。在mSeletMap看来,第0项和第2项处于选择状态;而在GridView看来,只有
第2项处于选择状态。
如果保持第2项选中,退出多选模式,则对于第2项的选择状态,两者也会出现不一致的
情况。我们当然可以在退出多选模式的时候做点什么,使mSelectMap和GridView的
mCheckStates保持一致。但既然我们并不能在getView中借由自己维护的数据模型
所以,直接使用AbsListView的mCheckedState及相关的公共方法来设置和查询选中状态
才是正途。下面是AbsListView提供的一些与选中有关的方法。
//判断一个item是否被选中1
public boolean isItemChecked(int position);
//获得被选中item的总数
public int getCheckedItemCount();
//选中一个item
public void setItemChecked(int position, boolean value);
//清除选中的item
public void clearChoices();
详细可参考
进入多选状态,在多选状态点击各个图片能够勾选,并得到所有选择的图片。
最初参考的是这篇文章
http://blog.csdn.net/zhouyuanjing/article/details/8372686
文章里作者提供了源码,为分析方便,贴在下面。
import java.util.HashMap;import java.util.Map;import java.util.Set;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.view.ActionMode;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView.LayoutParams;import android.widget.AbsListView.MultiChoiceModeListener;import android.widget.BaseAdapter;import android.widget.Checkable;import android.widget.FrameLayout;import android.widget.GridView;import android.widget.ImageView;import android.widget.ListAdapter;import android.widget.TextView;public class HomeActivity extends Activity implements MultiChoiceModeListener { private GridView mGridView; private GridAdapter mGridAdapter; private TextView mActionText; private static final int MENU_SELECT_ALL = 0; private static final int MENU_UNSELECT_ALL = MENU_SELECT_ALL + 1; private Map<Integer, Boolean> mSelectMap = new HashMap<Integer, Boolean>(); private int[] mImgIds = new int[] { R.drawable.img_1, R.drawable.img_2, R.drawable.img_3}; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mGridView = (GridView) findViewById(R.id.gridview); mGridView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL); mGridAdapter = new GridAdapter(this); mGridView.setAdapter(mGridAdapter); mGridView.setMultiChoiceModeListener(this); } /** Override MultiChoiceModeListener start **/ @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub View v = LayoutInflater.from(this).inflate(R.layout.actionbar_layout, null); mActionText = (TextView) v.findViewById(R.id.action_text); mActionText.setText(formatString(mGridView.getCheckedItemCount())); mode.setCustomView(v); getMenuInflater().inflate(R.menu.action_menu, menu); return true; } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub menu.getItem(MENU_SELECT_ALL).setEnabled( mGridView.getCheckedItemCount() != mGridView.getCount()); return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // TODO Auto-generated method stub switch (item.getItemId()) { case R.id.menu_select: for (int i = 0; i < mGridView.getCount(); i++) { mGridView.setItemChecked(i, true); mSelectMap.put(i, true); } break; case R.id.menu_unselect: for (int i = 0; i < mGridView.getCount(); i++) { mGridView.setItemChecked(i, false); mSelectMap.clear(); } break; } return true; } @Override public void onDestroyActionMode(ActionMode mode) { // TODO Auto-generated method stub mGridAdapter.notifyDataSetChanged(); } @Override public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { // TODO Auto-generated method stub mActionText.setText(formatString(mGridView.getCheckedItemCount())); mSelectMap.put(position, checked); mode.invalidate(); } /** Override MultiChoiceModeListener end **/ private String formatString(int count) { return String.format(getString(R.string.selection), count); } private class GridAdapter extends BaseAdapter { private Context mContext; public GridAdapter(Context ctx) { mContext = ctx; } @Override public int getCount() { // TODO Auto-generated method stub return mImgIds.length; } @Override public Integer getItem(int position) { // TODO Auto-generated method stub return Integer.valueOf(mImgIds[position]); } @Override public long getItemId(int position) { // TODO Auto-generated method stub return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stubLog.d(TAG, "getView: "+position+","+(mSelectMap.get(position) == null ? false : mSelectMap.get(position)));//<------(2) GridItem item; if (convertView == null) { item = new GridItem(mContext); item.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); } else { item = (GridItem) convertView; } item.setImgResId(getItem(position)); item.setChecked(mSelectMap.get(position) == null ? false : mSelectMap.get(position));//<-----(1) return item; } }}
其中GridItem是列表项的View类,实现了Checkable接口
import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.widget.Checkable;import android.widget.ImageView;import android.widget.RelativeLayout;public class GridItem extends RelativeLayout implements Checkable { private Context mContext; private boolean mChecked; private ImageView mImgView = null; private ImageView mSecletView = null; public GridItem(Context context) { this(context, null, 0); } public GridItem(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GridItem(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub mContext = context; LayoutInflater.from(mContext).inflate(R.layout.grid_item, this); mImgView = (ImageView) findViewById(R.id.img_view); mSecletView = (ImageView) findViewById(R.id.select); } @Override public void setChecked(boolean checked) { // TODO Auto-generated method stubLog.d(TAG,"setChecked: "+checked);//<--- mChecked = checked; setBackgroundDrawable(checked ? getResources().getDrawable( R.drawable.background) : null); mSecletView.setVisibility(checked ? View.VISIBLE : View.GONE); } @Override public boolean isChecked() { // TODO Auto-generated method stub return mChecked; } @Override public void toggle() { // TODO Auto-generated method stub setChecked(!mChecked); } public void setImgResId(int resId) { if (mImgView != null) { mImgView.setBackgroundResource(resId); } }}
作者的基本思路就是自己维护一个HashMap,用于记录哪些位置的图片被选中了,然后
根据选中状态在Adapter的getView方法中设置图片的外观。如(1)处所示。
但是经过我的试验,发现这种方法不仅费事,而且极易出错,可以说是吃力不讨好。
因为AbsListView本身就有一个变量来记录各项的选中状态:
SparseBooleanArray mCheckStates;
SparseBooleanArray是android.util包中的一个类,API说它
SparseBooleanArrays map integers to booleans。因此它也相当于是一个Map,将int
类型的key对应到boolean值上。实际上就记录了GridView中的各个position的选择状态
true或者false.
如果我们只是没有用GridView自带的机制来处理选中状态,那还好,顶多算费事点。
但是除了费事之外,自己维护选中状态会随之带来很多问题。
虽然我们在getView中调用GridItem的setChecked方法设置好了列表项的状态(这个状态
依据的是自定义的mSelectMap),但getView返回后,GridView自身还会去调用一次
GridItem的setChecked方法再次设置列表项的状态,而这次调用,依据的是mCheckState
中记录的状态。这个状态,很可能跟mSelectMap不一样。
我们可以做试验来验证。
首先在GridItem的setCheced()方法,getView方法中都添加Log代码,如(2)(3)所示
第一次长按第0项,Logcat输出为:
1、08-05 16:27:13.416: D/GridItem(1812): setChecked: false
2、08-05 16:27:13.416: D/GridItem(1812): setChecked: false
3、08-05 16:27:13.416: D/GridItem(1812): setChecked: false
4、08-05 16:27:14.036: D/HomeActivity(1812): getView: 0,true
5、08-05 16:27:14.036: D/GridItem(1812): setChecked: true
6、08-05 16:27:14.056: D/HomeActivity(1812): getView: 0,true
7、08-05 16:27:14.056: D/GridItem(1812): setChecked: true
8、08-05 16:27:14.076: D/GridItem(1812): setChecked: true
9、08-05 16:27:14.086: D/HomeActivity(1812): getView: 1,false
10、08-05 16:27:14.086: D/GridItem(1812): setChecked: false
11、08-05 16:27:14.086: D/GridItem(1812): setChecked: false
12、08-05 16:27:14.096: D/HomeActivity(1812): getView: 2,false
13、08-05 16:27:14.096: D/GridItem(1812): setChecked: false
14、08-05 16:27:14.096: D/GridItem(1812): setChecked: false
其中1~3行是GridView调用的setChecked,全为false,可以认为是建立初始状态
第5行是我们自己在getView中调用的setChecked;
第6,7行与第4、5行一样,好像每次第一项都会处理两次,不知何故;
第8行是GridView在getView(0)之后调用的setChecked;
同理,第11、14行分别是系统在每次getView之后调用的setChecked。
可以看到,迄今为止,GridView调用的setChecked和我们自己调用的setChecked状态
都还是一致的。
好,保持第0项为选中状态,我们点击返回退出多选模式,Logcat输出为:
1、08-05 16:34:30.897: D/HomeActivity(1812): getView: 0,true
2、08-05 16:34:30.897: D/GridItem(1812): setChecked: true
3、08-05 16:34:30.907: D/HomeActivity(1812): getView: 0,true
4、08-05 16:34:30.907: D/GridItem(1812): setChecked: true
5、08-05 16:34:30.907: D/GridItem(1812): setChecked: false
6、08-05 16:34:30.917: D/HomeActivity(1812): getView: 1,false
7、08-05 16:34:30.917: D/GridItem(1812): setChecked: false
8、08-05 16:34:30.917: D/GridItem(1812): setChecked: false
9、08-05 16:34:30.917: D/HomeActivity(1812): getView: 2,false
10、08-05 16:34:30.927: D/GridItem(1812): setChecked: false
11、08-05 16:34:30.937: D/GridItem(1812): setChecked: false
对于第一项,第3、4行是重复第1、2行的;
第5行就有意思了,是由GridView调用的setChecked,为false。
对比第4行和第5行,即GridView认为第0项已经取消选择了,而我们依据mSelectMap
却认为第0项还在选择状态。
至此已经出现了视图和数据模型不一致的情况。长按第2项再次进入多选,Logcat输出如下:
1、08-05 16:39:21.666: D/GridItem(1812): setChecked: false
2、08-05 16:39:21.666: D/GridItem(1812): setChecked: false
3、08-05 16:39:21.688: D/GridItem(1812): setChecked: false
4、08-05 16:39:22.306: D/HomeActivity(1812): getView: 0,true
5、08-05 16:39:22.316: D/GridItem(1812): setChecked: true
6、08-05 16:39:22.348: D/HomeActivity(1812): getView: 0,true
7、08-05 16:39:22.348: D/GridItem(1812): setChecked: true
8、08-05 16:39:22.356: D/GridItem(1812): setChecked: false
9、08-05 16:39:22.356: D/HomeActivity(1812): getView: 1,false
10、08-05 16:39:22.366: D/GridItem(1812): setChecked: false
11、08-05 16:39:22.366: D/GridItem(1812): setChecked: false
12、08-05 16:39:22.366: D/HomeActivity(1812): getView: 2,true
13、08-05 16:39:22.409: D/GridItem(1812): setChecked: true
14、08-05 16:39:22.409: D/GridItem(1812): setChecked: true
第1、2、3行为GridView在设置初始状态
第6、7行重复第4、5行;
第8行和第7行仍旧是不一致的,代表mSelectMap和GridView在第0项是否选择上仍旧是
不一样的。在mSeletMap看来,第0项和第2项处于选择状态;而在GridView看来,只有
第2项处于选择状态。
如果保持第2项选中,退出多选模式,则对于第2项的选择状态,两者也会出现不一致的
情况。我们当然可以在退出多选模式的时候做点什么,使mSelectMap和GridView的
mCheckStates保持一致。但既然我们并不能在getView中借由自己维护的数据模型
mSelectMap来控制外观,选中状态的外观仍然取决于GridView.mCheckStates。那为什
么要多此一举自己定义一个mSelectMap并费劲地使它和GridView.mCheckStates保持一
致,而不直接使用GridView.mCheckStates呢?
所以,直接使用AbsListView的mCheckedState及相关的公共方法来设置和查询选中状态
才是正途。下面是AbsListView提供的一些与选中有关的方法。
//判断一个item是否被选中1
public boolean isItemChecked(int position);
//获得被选中item的总数
public int getCheckedItemCount();
//选中一个item
public void setItemChecked(int position, boolean value);
//清除选中的item
public void clearChoices();
详细可参考
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1105/1906.html
最后附上我修改的代码
http://download.csdn.net/detail/glorydream2015/8975721
(没积分下载别人的东西了,大家给点分意思一下吧)
0 0
- GridView多选错误范例解析
- LazyTableImages范例解析
- SeismicXML范例解析
- QT范例解析
- LazyTableImages范例解析
- Tasky范例解析
- EmployeeDirectory范例解析
- xml解析-dom范例
- xml解析-sax范例
- xml解析-dom4j范例
- Word2Vec 源码解析+范例
- GridView解析
- 图像解析库使用范例
- Python解析json数据结构范例
- Gson解析date类型 范例
- Python解析json数据结构范例
- Json 解析的一个范例
- Android AIDL机制范例解析
- Android 读取sd卡图片并显示
- QEMU代码中os_daemonize()函数的理解
- 定时器(Quartz)快速入门
- 设计模式学习--中介者模式
- 如何加锁
- GridView多选错误范例解析
- [2015-08-05] python016
- 关于头文件重复包含以及函数重定义问题
- Linux&C 线程控制 课后习题
- NYOJ 822 画图
- 黑马程序员——ios学习笔记 C语言 指针
- 网页制作实践步骤三 登录界面
- Supervised Descent Method and its Applications to Face Alignment
- 20150805面试有感