这个项目是Android studio的,eclipse的朋友可以复制一下资源就能用了。
知识点如下:
1、解决listView滚动时checkBox的选择状态会发生变化(絮乱);
2、ListView的Item长按,点击时间并存,实现同一UI控件多需求响应各类事件;
3、点击ListViewItem选中响应Item里的CheckBox;
4、计算选中的项数以及相应UI的隐显增强用户体验;
5、自定义AlertDialog布局。全部自己研究编写,测试运行Ok,为真实项目的所用的简化Demo。
希望和大家分享,若有觉得要改进的地方也请提出。
如上图,初始为隐藏ChcekBox,这个情况下点击ListView的Item只会响应点击时间,弹出自定义AlerDialog。当长按Item后会显示出CheckBox进入选择批量删除的模式,点击Item后选中相应CheckBox,这时滚动list不会改变Chenckbox的选择状态,不会发生絮乱且上方导航标题会显示选择项数,而上方导航栏的UI也相应改变,“返回”字样—>“取消”,"清空"按钮会变成一个删除的垃圾桶图标,点击其可以删除选择的Item,操作完毕后恢复CheckBox隐藏的普通状态。
也许这类的Demo网上有很多,但我找过的几乎都不怎么满意, 既想要滚动listview时不会絮乱,又想checkbox在删除操作时才显示并计算选中的删除项数,而且也应该要把相关UI显示的Activity跟Adapter分开,数据与list分开(网上很多Demo都全部写在一起了)。所以经过自己研究后写出了这个Dmeo。
下面贴些关键的代码,首先看xml的:
ListView的Item布局:
这里有个值得注意的地方,就是每一个Item里面的子控件里都有 android:focusable="false" 这语句,这是关系到Android的触摸事件分派机制,这样做是因为同一个UI布局里面如果有CheckBox或Button之类的空间则会优先响应获得点击事件的焦点,子View的onTouchEven会最优先响应。因为该项目还要完成“点击Item勾选CheckBox”,所以必然不能让CheckBox相获得焦点否则则会无效。
下面来看一张图来了解一下Android事件的派发:转自该博客
正如该博客里的所说,若果最里一层的子View(CheckBox)返回的 focusable为 false,则判断再上一层的子View(LinearLayout),如此类推一直到最外层,若果都为false则整个Item的触摸事件消失。
到这里我们再看“点击Item勾选CheckBox”的关键代码:
//提供外部点击Itme时勾选CheckBox
public void itemSelected(View view) {
ViewHolder viewHolder = (ViewHolder) view.getTag();//通过tag取得ViewHolder对象,这样就省去了通过层层的findViewById去实例化我们需要的cb实例的步骤
viewHolder.checkBox.setChecked(!viewHolder.checkBox.isChecked());
}
Adpater代码:
public class DataAdapter extends BaseAdapter {
//上下文
private Context context;
//传入的Activity对象,若是Fragment则传入相应的对象
private MainActivity mainActivity;
private LayoutInflater layoutInflater;
//数据源
private List data = new ArrayList();
//checkBox是否可以点击
private Boolean IsCheckAble = false;
//checkBox是否可见
private boolean isVisible = false;
//是否处于选中状态(isChecked)--Map
private Map isCheckMap = new HashMap();
//保存已选中的删除项数的索引(CheckBoxId)-- Map
private Map isDeleteCheckedBoxId = new HashMap();
private int deleteKey;//isDeleteCheckBoxId的索引值
//保存以选择的删除项数对象(data) -- Map
private Map isDeleteData = new HashMap();
public DataAdapter(Context context) {
this.context = context;
}
public DataAdapter(Context context, MainActivity mainActivity) {
this.context = context;
this.mainActivity = mainActivity;
this.layoutInflater = LayoutInflater.from(context);
dataInit();
setDataIndex();
}
/**
* 初始化虚拟数据,实际项目中应替换成数据库的真实数据
*/
private void dataInit() {
for (int i = 0; i < 40; i++) {
data.add("消息" + i);
}
}
private List> setDataIndex() {
List> list = new ArrayList>();
for (int i = 0; i < data.size(); i++) {
HashMap map = new HashMap();
map.put("dataIndex", i);
list.add(map);
}
return list;
}
/**
* 给数据源添加新的索引,用于删除
*/
private List> getDataIndex() {
List> list = new ArrayList>();
for (int i = 0; i < data.size(); i++) {
HashMap map = new HashMap();
map.put("dataIndex", i);
list.add(map);
}
return list;
}
//ViewHolder静态类
public static class ViewHolder {
public TextView textView;
public CheckBox checkBox;
public ViewGroup cellViewGroup;
}
//在此适配器中代表的数据集中的条目数
@Override
public int getCount() {
return data.size();
}
//获取数据集中与指定索引对应的数据项
@Override
public Object getItem(int position) {
return data.get(position);
}
//获取在列表中指定所以对应的行id
@Override
public long getItemId(int i) {
return 0;
}
//获取一个在数据集中指定索引的视图来显示数据
@Override
public View getView(int position, View view, ViewGroup viewGroup) {
ViewHolder viewHolder = null;
//如果缓存view为空,则需要创建view
if (view == null) {
viewHolder = new ViewHolder();
//根据自定义的Item加载布局
view = layoutInflater.inflate(R.layout.view_listviewcell_for_date, null);
viewHolder.cellViewGroup = (ViewGroup) view.findViewById(R.id.view_listviewcell_for_date_layout);
viewHolder.textView = (TextView) view.findViewById(R.id.view_listviewcell_for_date_text_title);
viewHolder.checkBox = (CheckBox) view.findViewById(R.id.view_listviewcell_for_date_checkbox);
//将设置好的布局保存到缓存中,并将其设置在Tag里,以方便取出Tag
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
if (getDataIndex().get(position).get("dataIndex").toString() != null) {
Log.v("getDataIndex()--->", getDataIndex().get(position).get("dataIndex").toString() + "");
viewHolder.textView.setText(data.get(Integer.valueOf(getDataIndex().get(position).get("dataIndex").toString())));
}
/**
* 批量删除
*/
viewHolder.checkBox.setTag(getDataIndex().get(position).get("dataIndex").toString());
viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
int CheckBoxId = Integer.parseInt(compoundButton.getTag().toString());
int i = -1;
if (isChecked) {
if (!isCheckMap.containsKey(CheckBoxId)) {
isCheckMap.put(CheckBoxId, isChecked);//把已经选择中的checkbox加入,防止listview滚动时发生絮乱
Log.v("isCheckMap.put--->", CheckBoxId + "" + isCheckMap + "");
isDeleteCheckedBoxId.put(deleteKey, CheckBoxId);
Log.v("isDeleteCheckedBoxId--->", "" + isDeleteCheckedBoxId + "" + isDeleteCheckedBoxId.size());
isDeleteData.put(CheckBoxId, data.get(CheckBoxId));
deleteKey++;
}
} else {
isCheckMap.remove(CheckBoxId);
Log.v("isCheckMap.remove--->", CheckBoxId + "" + isCheckMap + "");
isDeleteData.remove(CheckBoxId);
for (Integer key : isDeleteCheckedBoxId.keySet()) {
if (CheckBoxId == isDeleteCheckedBoxId.get(key)) {
i = key;//把checkboxid赋值给i
}
}
if (i != -1) {
isDeleteCheckedBoxId.remove(i);//根据checkboxid找到对应的key并移除
Log.v("i--->", i + "");
}
}
mainActivity.setSelectedItemnum(isCheckMap.size());//提示已选中的项数
}
});
//初始化CheckBox的选中状态
if (isVisible) {//当处于可见状态时
Log.v("isCheckMap--->", isCheckMap + "");
viewHolder.checkBox.setVisibility(View.VISIBLE);
if (isCheckMap != null && isCheckMap.containsKey(position)) {//有曾经选中的记录
viewHolder.checkBox.setChecked(isCheckMap.get(position));//显示为原本的选中状态
} else {
viewHolder.checkBox.setChecked(false);//显示为没选中状态
}
} else {
viewHolder.checkBox.setVisibility(View.GONE);
}
return view;
}
//删除单条数据
public void removeItem(int position) {
data.remove(position);
}
//批量删除数据
public void removeItems() {
for (Integer key : isDeleteCheckedBoxId.keySet()) {
removeItem(isDeleteCheckedBoxId.get(key));
Log.v("removeItem--->", isDeleteCheckedBoxId.get(key) + "");
}
isCheckMap.clear();//清空选中索引
isDeleteCheckedBoxId.clear();//清空删除索引
isDeleteData.clear();
deleteKey = 0;
}
//提供外部点击Itme时勾选CheckBox
public void itemSelected(View view) {
ViewHolder viewHolder = (ViewHolder) view.getTag();//通过tag取得ViewHolder对象,这样就省去了通过层层的findViewById去实例化我们需要的cb实例的步骤
viewHolder.checkBox.setChecked(!viewHolder.checkBox.isChecked());
}
//提供外部操作CheckBox隐现
public Boolean isVisible(Boolean isVisible) {
ViewHolder viewHolder = new ViewHolder();
View view = layoutInflater.inflate(R.layout.view_listviewcell_for_date, null);
viewHolder.checkBox = (CheckBox) view.findViewById(R.id.view_listviewcell_for_date_checkbox);
if (!isVisible) {
isCheckMap.clear();
isDeleteCheckedBoxId.clear();
deleteKey = 0;
mainActivity.setSelectedItemnum(0);
}
if (isVisible) {
viewHolder.checkBox.setVisibility(View.VISIBLE);
} else {
viewHolder.checkBox.setVisibility(View.GONE);
}
this.isVisible = isVisible;
notifyDataSetChanged();
return isVisible;
}
}
在上面的Adapter中你会发现很多输出日志,诸如:
Log.v("isCheckMap.put--->", CheckBoxId + "" + isCheckMap + "");
Log.v("isDeleteCheckedBoxId--->", "" + isDeleteCheckedBoxId + "" + isDeleteCheckedBoxId.size());
11-13 15:44:54.642 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 0{0=true}
11-13 15:44:54.642 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0}1
11-13 15:44:55.333 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 1{0=true, 1=true}
11-13 15:44:55.333 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 1=1}2
11-13 15:44:55.963 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 2{0=true, 1=true, 2=true}
11-13 15:44:55.963 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 1=1, 2=2}3
11-13 15:44:56.434 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 3{0=true, 3=true, 1=true, 2=true}
11-13 15:44:56.434 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 3=3, 1=1, 2=2}4
11-13 15:44:56.944 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 4{0=true, 3=true, 4=true, 1=true, 2=true}
11-13 15:44:56.944 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 3=3, 4=4, 1=1, 2=2}5
11-13 15:44:57.775 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 5{0=true, 5=true, 3=true, 4=true, 1=true, 2=true}
11-13 15:44:57.775 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 5=5, 3=3, 4=4, 1=1, 2=2}6
11-13 15:44:58.536 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 6{0=true, 5=true, 3=true, 4=true, 1=true, 6=true, 2=true}
11-13 15:44:58.536 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 5=5, 3=3, 4=4, 1=1, 6=6, 2=2}7
11-13 15:44:59.036 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 7{0=true, 5=true, 3=true, 4=true, 1=true, 7=true, 6=true, 2=true}
11-13 15:44:59.036 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {0=0, 5=5, 3=3, 4=4, 1=1, 7=7, 6=6, 2=2}8
11-13 15:44:59.617 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 8{8=true, 0=true, 5=true, 3=true, 4=true, 1=true, 7=true, 6=true, 2=true}
11-13 15:44:59.617 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {8=8, 0=0, 5=5, 3=3, 4=4, 1=1, 7=7, 6=6, 2=2}9
11-13 15:45:00.167 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 9{8=true, 0=true, 9=true, 5=true, 3=true, 4=true, 1=true, 7=true, 6=true, 2=true}
11-13 15:45:00.167 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {8=8, 0=0, 9=9, 5=5, 3=3, 4=4, 1=1, 7=7, 6=6, 2=2}10
11-13 15:45:00.648 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 10{8=true, 0=true, 9=true, 5=true, 3=true, 4=true, 10=true, 1=true, 7=true, 6=true, 2=true}
11-13 15:45:00.648 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {8=8, 0=0, 9=9, 5=5, 3=3, 4=4, 10=10, 1=1, 7=7, 6=6, 2=2}11
11-13 15:45:01.268 19308-19308/com.listviewcheckboxdemo V/isCheckMap.put--->: 11{8=true, 11=true, 0=true, 9=true, 5=true, 3=true, 4=true, 10=true, 1=true, 7=true, 6=true, 2=true}
11-13 15:45:01.268 19308-19308/com.listviewcheckboxdemo V/isDeleteCheckedBoxId--->: {8=8, 11=11, 0=0, 9=9, 5=5, 3=3, 4=4, 10=10, 1=1, 7=7, 6=6, 2=2}12
11-13 15:55:22.054 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 8
11-13 15:55:22.064 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 11
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 0
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 9
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 5
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 3
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 4
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 10
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 1
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 7
11-13 15:55:22.074 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 6
11-13 15:55:22.084 28913-28913/com.listviewcheckboxdemo V/removeItem--->: 2
到这里我们就能完成主要的删除并且不絮乱的错误了,因为每当滚动的时候,listview的getview方法都会查看一下上述的list中有没有相应item的标识,若果有则显示为选择状态,反之则否。而项目又用 "isVisible"来标志CheckBox的隐显。
MainActivity:
public class MainActivity extends BackActivity implements View.OnClickListener {
//传递标志是否允许checkBox显示的参数
private boolean isVisible = false;
private TextView titleTextView;
private Button clearButton;
private ImageView deleteImageView;
private ViewGroup cancelViewGroup, returnViewGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
listViewinit();
}
private void initView() {
clearButton = (Button) findViewById(R.id.view_navigation_bar_button_clear);
clearButton.setOnClickListener(this);
deleteImageView = (ImageView) findViewById(R.id.view_navigation_bar_imageview_deletebtn);
deleteImageView.setOnClickListener(this);
titleTextView = (TextView) findViewById(R.id.view_navigation_bar_textview_title);
cancelViewGroup = (ViewGroup) findViewById(R.id.view_navigation_bar_layout_cancelbtn);
cancelViewGroup.setVisibility(View.GONE);
cancelViewGroup.setOnClickListener(this);
returnViewGroup = (ViewGroup) findViewById(R.id.view_navigation_bar_include_returnbtn);
returnViewGroup.setOnClickListener(this);
}
private ListView listView;
private DataAdapter dataAdapter;
private void listViewinit() {
dataAdapter = new DataAdapter(this, MainActivity.this);
listView = (ListView) findViewById(R.id.activity_main_listview);
listView.setAdapter(dataAdapter);
//listView的Item长按事件监听
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> adapterView, View view, int i, long l) {
isVisible = true;
dataAdapter.isVisible(isVisible);
deleteModel(isVisible);
return true;
}
});
//listView的Item点击事件监听
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
// dataAdapter = new DataAdapter(getApplicationContext(), MainActivity.this);
if (isVisible) {//如果checkbox可见,则进入checkbox勾选逻辑
dataAdapter.itemSelected(view);
} else {
ShowpopChose(position, dataAdapter);
}
}
});
}
/**
* 显示/隐藏布局
*/
private void showDeleteView() {
cancelViewGroup.setVisibility(View.VISIBLE);
returnViewGroup.setVisibility(View.GONE);
deleteImageView.setVisibility(View.VISIBLE);
clearButton.setVisibility(View.GONE);
titleTextView.setText("已选中0项");
}
private void showNomalView() {
cancelViewGroup.setVisibility(View.GONE);
returnViewGroup.setVisibility(View.VISIBLE);
clearButton.setVisibility(View.VISIBLE);
deleteImageView.setVisibility(View.GONE);
titleTextView.setText("导航栏标题");
}
private void deleteModel(Boolean isVisible) {
this.isVisible = isVisible;
if (isVisible) {
cancelViewGroup.setVisibility(View.VISIBLE);
returnViewGroup.setVisibility(View.GONE);
deleteImageView.setVisibility(View.VISIBLE);
clearButton.setVisibility(View.GONE);
titleTextView.setText("已选中0项");
} else {
cancelViewGroup.setVisibility(View.GONE);
returnViewGroup.setVisibility(View.VISIBLE);
clearButton.setVisibility(View.VISIBLE);
deleteImageView.setVisibility(View.GONE);
titleTextView.setText("导航栏标题");
}
dataAdapter.notifyDataSetChanged();
listView.setAdapter(dataAdapter);
}
//显示已选中项数
public void setSelectedItemnum(int number) {
titleTextView.setText("已选中" + number + "项");
}
/**
* 点击弹出自定义dialog
*
* @param position
* @param dataAdapter
*/
public void ShowpopChose(final int position, final DataAdapter dataAdapter) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(new MyTextViewCell(this));
final DataAdapter adapter = dataAdapter;
final int index = position - 1;
final AlertDialog dialog = builder.show();//创建一个dialog对象并显示
//设置dialog的布局
//重发
dialog.getWindow().setContentView(R.layout.view_dialog_messagebox_send);
MyTextViewCell sendButton = (MyTextViewCell) dialog.findViewById(R.id.dialog_resend);
sendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "开始发送", Toast.LENGTH_SHORT).show();
}
});
//删除
MyTextViewCell delButton = (MyTextViewCell) dialog.findViewById(R.id.dialog_delete);
delButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "删除消息", Toast.LENGTH_SHORT).show();
}
});
//定位
MyTextViewCell localButton = (MyTextViewCell) dialog.findViewById(R.id.dialog_location);
localButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "重发位置", Toast.LENGTH_SHORT).show();
}
});
Button cancelButton = (Button) dialog.findViewById(R.id.dialog_cancle);
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialog.cancel();
}
});
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.view_navigation_bar_button_clear:
new AlertDialog.Builder(this).setMessage("是否确定全部删除?").setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
}).setNegativeButton("取消", null).show();
break;
case R.id.view_navigation_bar_imageview_deletebtn:
isVisible = !isVisible;
dataAdapter.removeItems();
dataAdapter.isVisible(isVisible);
deleteModel(isVisible);
break;
case R.id.view_navigation_bar_layout_cancelbtn:
isVisible = !isVisible;
dataAdapter.isVisible(isVisible);
dataAdapter.notifyDataSetChanged();
//每次改变完数据后除了,要更新适配器里面的数据外,还要重新绑定一次listview的数据,这样listview会先清空前一次数据再重新读取改变后的数据
listView.setAdapter(dataAdapter);
showNomalView();
break;
}
}
}
可能项目中还有些许不完善,尤其数据写得不是很好,不过删除、显示等逻辑已经完成希望和大家分享,有问题的可以向LZ提出,我会尽力尽快解答,谢谢~
Demo下载地址