Android — 长按ListView 利用上下文菜单(ActionMode) 进行批量事件处理
好久没写博客拉```````
近期最终略微闲一点了```````
无聊拿手机清理短信。发现批量事件的处理还是挺管用的``````
那么自己也来山寨一记看看效果吧`````
闲话少说,首先,我们来看下手机自带的短信功能里运行批量删除时的效果:
然后 是我们自己简单山寨的效果:
 
  
  
模拟的操作过程非常easy,但也非常有代表性。
我们假定我们所处的场景为。进入一个存放联系人列表的界面。
于是,首先我们定义了一个进度框,模拟提示正在从网络上下载数据。
接着。当网络数据成功下载到移动设备上后,将数据绑定显示到相应的ListView之中。
然后,就是我们这篇博客提到的:长按该联系人列表的ListView触发事件。
弹出使用ActionMode的上下文菜单。并让该ListView中的列表项支持复现,实现批量操作。
最后。就是当用户选择了一定数量的选项后。点击菜单中的Item进行某项批量操作后,运行相应的操作,并刷新ListView。
理清了我们想要实现的大致效果,接着我们要做的
就是整理一下思路,然后逐步的去编写代码,完毕实现工作。let's do it !
首先,我们已经知道了自己 想要以一个联系人列表作为场景。
那么,自然我们会须要一个ListView来绑定和存放这些联系人数据。
于是,我们先将存放ListView以及定义该ListView的Item的细节的布局文件搞出来,分别为:
context_menu_action_mode.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <ListView
android:id="@+id/context_menu_listView"
android:layout_width="match_parent"
android:layout_height="match_parent" /> </LinearLayout>
context_menu_action_mode_item.xml
<? xml version="1.0" encoding="utf-8"? >
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin" > <ImageView
android:id="@+id/user_head"
android:layout_width="55dp"
android:layout_height="55dp"
android:contentDescription="@string/user_head_description"
android:src="@drawable/headimage_default" /> <TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:layout_toEndOf="@id/user_head"
android:layout_toRightOf="@id/user_head"
android:textSize="25sp" /> <TextView
android:id="@+id/phone_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/user_head"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:layout_toEndOf="@id/user_head"
android:layout_toRightOf="@id/user_head" /> <CheckBox
android:id="@+id/contact_selected_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:clickable="false"
android:focusable="false" /> </RelativeLayout>
关于布局的定义。并没有什么难点。
唯一须要注意的是,我们为了更加友好的交互体验,所以在用户长按ListView进入可复选的模式后,
在每一个列表的最右側加入显示了一个CheckBox,以提示用户是否成功选择到了想要操作的列表项。
CheckBox仅仅有在用户进入复选模式后,才显示,所以我们须要在后面注意在代码中动态的控制其显示情况。
而且!更须要注意的是,记得将CheckBox的clickable与focusable两个属性的值设置为false!
这样做的原因是由于CheckBox(定义在作为ListView的Item文件其中)自身的响应焦点及点击事件的优先级高于ListView自身。
所以。假设忘记设置的话,焦点及响应事件将被拦截在CheckBox,无法到达ListView。
第二步,当我们定义好了ListView的相关程序之后,自然忘不了它的好基友:适配器Adapter
MyContactAdapter.java:
package com.example.android_menu_test_demo.adapter; import java.util.ArrayList;
import com.example.android_menu_test_demo.R;
import com.example.android_menu_test_demo.domain.Contact;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView; public class MyContactAdapter extends BaseAdapter { private Context mContext;
private ArrayList<Contact> contacts;
private ViewHolder mViewHolder;
private ArrayList<Contact> selected_contacts = new ArrayList<Contact>(); private boolean itemMultiCheckable; public MyContactAdapter(Context mContext, ArrayList<Contact> contacts) {
this.mContext = mContext;
this.contacts = contacts;
} @Override
public int getCount() {
return contacts.size();
} @Override
public Object getItem(int position) {
return contacts.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.contact_listview_item, null); mViewHolder = new ViewHolder(); mViewHolder.user_head = (ImageView) convertView.findViewById(R.id.user_head);
mViewHolder.user_name_text = (TextView) convertView.findViewById(R.id.user_name);
mViewHolder.phone_number_text = (TextView) convertView.findViewById(R.id.phone_number);
mViewHolder.item_seleted = (CheckBox) convertView.findViewById(R.id.contact_selected_checkbox); convertView.setTag(mViewHolder);
} else {
mViewHolder = (ViewHolder) convertView.getTag();
} // ************对于控件的详细处理**************** // 设置checkbox是否可见
if (itemMultiCheckable) {
mViewHolder.item_seleted.setVisibility(View.VISIBLE);
// 假设checkbox可见。证明当前处于可多选操作情况下,则依据用户选择情况设置checkbox被选中状态
if (selected_contacts.contains(contacts.get(position))) {
mViewHolder.item_seleted.setChecked(true);
} else {
mViewHolder.item_seleted.setChecked(false);
} } else {
mViewHolder.item_seleted.setVisibility(View.GONE);
} // 控件赋值
Contact contact = contacts.get(position);
mViewHolder.user_name_text.setText(contact.getUserName());
mViewHolder.phone_number_text.setText(contact.getPhoneNumber()); return convertView;
} public void setItemMultiCheckable(boolean flag) {
itemMultiCheckable = flag;
} public void addSelectedContact(int position) {
selected_contacts.add(contacts.get(position));
} public void cancelSeletedContact(int position) {
selected_contacts.remove(contacts.get(position));
} public void clearSeletedContacts() {
selected_contacts = new ArrayList<Contact>();
} public void deleteSeletedContacts() {
for (Contact contact : selected_contacts) {
contacts.remove(contact);
}
} static class ViewHolder {
ImageView user_head;
TextView user_name_text, phone_number_text;
CheckBox item_seleted;
}
}
适配器类的定义与我们开发中最常见的定义并没有太多差别。
值得注意的的代码,无非就是前面谈到的,做好动态控制CheckBox显示状态的工作。
另外,我们在适配器的定义中,为了让listview要显示的数据,更便于装载和传递。
一般会定义封装数据的实体类,正如上面的Contact类。只是这个太简单,就没贴代码的必要了。
接下来。就是我们想要实现的功能的重点了,
我们说到希望通过ListView的长点击事件,来触发一个上下文菜单来进行事件处理。
在Android 3.0之后加入的ActionMode相对于之前的普通上下文菜单。
显然更适合对于批量事件的处理。有着更好的交互体验。
所以说。既然将要使用到上下文 菜单,那么。废话少说。
先定义一个我们须要的简单的菜单文件:
multi_acitonmode_menu.xml:
<?xml version="1.0" encoding="utf-8"? >
<menu xmlns:android="http://schemas.android.com/apk/res/android" > <item
android:id="@+id/menu_cancle"
android:showAsAction="always"
android:title="@string/item_cancle"/> <item
android:id="@+id/menu_delete"
android:showAsAction="always"
android:title="@string/item_delete"/> </menu>
紧接着,一切准备 工作我们都已经基本就绪,
那么接下来要做的。自然就是Activity的代码编写工作了。
ContextMenuActionModeActivity.java
package com.example.android_menu_test_demo; import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AbsListView.MultiChoiceModeListener;
import java.util.ArrayList; import com.example.android_menu_test_demo.adapter.MyContactAdapter;
import com.example.android_menu_test_demo.domain.Contact; import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle; @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class ContextMenuActionModeActivity extends Activity { private ListView contact_list_view;
private ProgressDialog mDialog; private MyContactAdapter mAdpater;
private MultiModeCallback mCallback; // 模拟数据
private ArrayList<Contact> contacts;
private String[] userNames = new String[] { "Jack", "Rose", "Matt", "Adam", "Xtina", "Blake", "Tupac", "Biggie",
"T.I", "Eminem" };
private String[] phoneNumbers = new String[] { "138-0000-0001", "138-0000-0002", "138-0000-0003", "138-0000-0004",
"138-0000-0005", "138-0000-0006", "138-0000-0007", "138-0000-0008", "138-0000-0009", "138-0000-0010" }; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.context_menu_action_mode); initView(); new ContactsDownloadTask().execute(); } private void initView() {
contact_list_view = (ListView) this.findViewById(R.id.context_menu_listView); mDialog = new ProgressDialog(this); mDialog.setTitle("提示信息");
mDialog.setMessage("下载联系人列表中...");
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mCallback = new MultiModeCallback();
contact_list_view.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
contact_list_view.setMultiChoiceModeListener(mCallback);
} private void downloadContactsFromServer() {
if (contacts == null) {
contacts = new ArrayList<Contact>();
} for (int i = 0; i < userNames.length; i++) {
contacts.add(new Contact(userNames[i], phoneNumbers[i]));
}
} private class ContactsDownloadTask extends AsyncTask<Void, Integer, Void> { private int currentlyProgressValue; @Override
protected void onPreExecute() {
mDialog.show();
super.onPreExecute();
} @Override
protected Void doInBackground(Void... params) { while (currentlyProgressValue < 100) {
publishProgress(++currentlyProgressValue);
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
} // download data from server
downloadContactsFromServer();
return null;
} @Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mDialog.setProgress(values[0]);
} @Override
protected void onPostExecute(Void result) {
super.onPostExecute(result); mAdpater = new MyContactAdapter(ContextMenuActionModeActivity.this, contacts);
contact_list_view.setAdapter(mAdpater); mDialog.dismiss();
} } private class MultiModeCallback implements MultiChoiceModeListener {
private View mMultiSelectActionBarView;
private TextView mSelectedCount; @Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.multi_acitonmode_menu, menu); mAdpater.setItemMultiCheckable(true);
mAdpater.notifyDataSetChanged(); if (mMultiSelectActionBarView == null) {
mMultiSelectActionBarView = LayoutInflater.from(ContextMenuActionModeActivity.this)
.inflate(R.layout.list_multi_select_actionbar, null);
mSelectedCount = (TextView) mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);
}
mode.setCustomView(mMultiSelectActionBarView);
((TextView) mMultiSelectActionBarView.findViewById(R.id.title)).setText(R.string.select_item);
return true;
} @Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
} @Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_cancle:
mAdpater.setItemMultiCheckable(false);
mAdpater.clearSeletedContacts();
mAdpater.notifyDataSetChanged();
mode.finish();
break;
case R.id.menu_delete:
mAdpater.deleteSeletedContacts();
mAdpater.notifyDataSetChanged();
mode.invalidate();
mode.finish();
break; default:
break;
}
return false;
} @Override
public void onDestroyActionMode(ActionMode mode) {
mAdpater.setItemMultiCheckable(false);
mAdpater.clearSeletedContacts();
mAdpater.notifyDataSetChanged(); } @Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
if (checked) {
mAdpater.addSelectedContact(position);
} else {
mAdpater.cancelSeletedContact(position);
} mAdpater.notifyDataSetChanged(); updateSeletedCount();
mode.invalidate(); } public void updateSeletedCount() {
mSelectedCount.setText(Integer.toString(contact_list_view.getCheckedItemCount()) + "条");
} } }
对于我们这种菜鸟来说,上面activity代码中值得注意的可能是:
1、基本上,我们首先会定义一个异步任务类。模拟从网络下载数据的过程。有助于Adapter的API的使用的掌握。
2、我们在上面代码中定义的实现了MultiChoiceModeListener接口的内部类,MultiModeCallback就是帮助我们实现长按ListView(也试用于GridView)。而且监听处理MultiChoice事件的关键。
— 简单来说,能够看到。我们在该内部类的回调方法onCreateActionMode中,处理长按ListView后,ActionMode菜单相关的创建工作。而且在此控制ListView中的CheckBox显示,告知用户,我们已经进入到了能够进行批量操作的模式下。
— onActionItemClicked方法 用于监听和响应菜单上相应的选项的点击事件,你能够在此依据自己的需求,为相应的菜单选项编写响应代码。
— onDestroyActionMode方法 用于处理菜单销毁时,所要运行的动作。
— 而onItemCheckedStateChanged方法 则就是用于监听处理ListView中每一个列表项的选中状态改变时的回调了。我们会在这里依据需求完毕相应的编码工作。
3、到了这里,我们对于我们想要的功能的实现,能够说已经是基本搞定了。可是,你可能已经在上面的MultiModeCallback类的某些代码中注意点到:
为了更加友善的交互感受,我们 还能够以ActionBar的形式。在菜单条上,加入一段内容。正如 短信功能里所使用的那样,用以提示用户类似于“您当前已经选择了XX条内容”的信息。所以我们还会定义一个类似ActionBar的布局文件。例如以下:
list_multi_select_actionbar.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/custom_title_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"> <TextView
android:id="@+id/title"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textColor="#ffffff" /> <TextView
android:id="@+id/selected_conv_count"
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffff"/> </LinearLayout>
在上面的代码中,你能够看到,我们相同是在onCreateActionMode方法中完毕ActionMode上下文菜单的装载工作的同一时候,也会进行对于该作为ActionBar使用的View的装载与显示控制工作。
在该View装载和显示工作完毕之后,我们要做的就非常easy了,仅仅须要在onItemCheckedStateChanged中进行监听,当用户选中某个列表项时,对用于显示提示信息的TextView的显示内容进行更新,则OK了。
走到这一步,我们能够说是已经山寨完成了,下一步要做的则能够将Demo编译到模拟器或者手机上,看看效果了~
Android — 长按ListView 利用上下文菜单(ActionMode) 进行批量事件处理的更多相关文章
- ListView的使用(二)长按弹出上下文菜单
		
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView ...
 - Android成长日记-ContextMenu实现上下文菜单
		
一. ContextMenu的组成 标题以及标题图标 菜单内容 菜单内容的点击事件 二. ContextMenu与OptionMenu的区别 OptionMenu对应的是activity,一个acti ...
 - Android 长按Listview显示CheckBox,实现批量删除。
		
ListView实现的列表,如果是可编辑,可删除的,一般都要提供批量删除功能,否则的话,一项一项的删除体验很不好,也给用户带来了很大的麻烦. 实现效果图 具体实现代码 select.xml 主布局文件 ...
 - android 为组件添加contextMenu上下文菜单
		
package com.example.fragmentNavigation2.fragment; import android.os.Bundle; import android.support.v ...
 - Android进阶(二十八)上下文菜单ContextMenu使用案例
		
上下文菜单ContextMenu使用案例 前言 回顾之前的应用程序,发现之前创建的选项菜单无法显示了.按照正常逻辑来说,左图中在"商品信息"一栏中应该存在选项菜单,用户可进行分享等 ...
 - Android 上下文菜单 ActionMode
		
public class MainActivity extends Activity { private Button button; private ActionMode actionMode; @ ...
 - Android 的上下文菜单: Context Menu,registerForContextMenu(getListView())
		
概述: Android 的上下文菜单类似于 PC 上的右键菜单.当为一个视图注册了上下文菜单之后,长按(2 秒左右)这个视图对象就会弹出一个浮动菜单,即上下文菜单.任何视图都可以注册上下文菜单,不过, ...
 - Android 上下文菜单实现
		
1.覆盖Activity的onCreateContenxtMenu()方法,调用Menu的add方法添加菜单项(MenuItem). 2.覆盖Activity的onContextItemSelecte ...
 - Android 上下文菜单 ContextMenu
		
public class MainActivity extends Activity { private ListView listView; @Override protected void onC ...
 
随机推荐
- Leetcode 331.验证二叉树的前序序列化
			
验证二叉树的前序序列化 序列化二叉树的一种方法是使用前序遍历.当我们遇到一个非空节点时,我们可以记录下这个节点的值.如果它是一个空节点,我们可以使用一个标记值记录,例如#. 例如,上面的二叉树可以被序 ...
 - .net提高文章
			
文章:.NET程序性能的基本要领 文章:你的字典里有多少元素? 文章:快速自检电脑是否被黑客入侵过(Windows版) 文章:关于DNS,你应该知道这些
 - 九度oj 题目1352:和为S的两个数字
			
题目描述: 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输入: 每个测试案例包括两行: 第一行包含一个整数n和k, ...
 - 九度oj 题目1102:最小面积子矩阵
			
题目描述: 一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积) 输入: 每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K接下来 ...
 - [LOJ#2270][BZOJ4912][SDOI2017]天才黑客
			
[LOJ#2270][BZOJ4912][SDOI2017]天才黑客 试题描述 SD0062 号选手小 Q 同学为了偷到 SDOI7012 的试题,利用高超的黑客技术潜入了 SDOI 出题组的内联网的 ...
 - [LA3620]Manhattan Wiring
			
[LA3620]Manhattan Wiring 试题描述 输入 输出 输入示例 输出示例 数据规模及约定 见“输入” 题解 我们把“连线”的过程改为“铺地砖”的过程,总共有 11 种地砖,每种地砖上 ...
 - 算法复习——带修改莫队(bzoj2453)
			
题目: Description 你小时候玩过弹珠吗? 小朋友A有一些弹珠,A喜欢把它们排成队列,从左到右编号为1到N.为了整个队列鲜艳美观,小朋友想知道某一段连续弹珠中,不同颜色的弹珠有多少.当然,A ...
 - LA 4728 旋转卡壳算法求凸包的最大直径
			
#include<iostream> #include<cstdio> #include<cmath> #include<vector> #includ ...
 - msp430项目编程35
			
msp430中项目---nand接口编程35 1.电路工作原理 2.代码(显示部分) 3.代码(功能实现) 4.项目总结
 - Linux 下tomcat的配置
			
参考文档:http://blog.csdn.net/jeamking/article/details/7881196 http://www.cnblogs.com/panxuejun/p/618641 ...