在上篇博客中,详细记录了ListView的各种用法,本篇博客主要用于探究ListView的两种多选模式。
在上篇博客的结尾,说到在多选模式中,ListView选中的列表项都保存在一个SparseBooleanArray中,利用ListView的列表是否被选中的状态,可以突出显示被选中的项,比如给选中的列表项设置背景色
布局文件依然是只有一个ListView
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends ActionBarActivity {
ListView listView;
List<String> datas = new ArrayList<String>();
ListViewAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.list);
initData();
// adapter = new ArrayAdapter<String>(this, R.layout.item, datas);
adapter = new ListViewAdapter();
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
//点击item后,通知adapter重新加载view
adapter.notifyDataSetChanged();
}
});
}
class ListViewAdapter extends BaseAdapter {
@Override
public int getCount() {
// TODO Auto-generated method stub
return datas.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return datas.get(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 stub
ViewHolder viewHolder;
if(convertView == null) {
viewHolder = new ViewHolder();
convertView = View.inflate(MainActivity.this, R.layout.item, null);
viewHolder.text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder)convertView.getTag();
}
viewHolder.text.setText(datas.get(position));
//判断position位置是否被选中,改变颜色
if(listView.isItemChecked(position)) {
convertView.setBackgroundColor(Color.RED);
} else {
convertView.setBackgroundColor(Color.TRANSPARENT);
}
return convertView;
}
class ViewHolder {
TextView text;
}
}
public void initData() {
for(int i = 1; i <= 15; i++) {
datas.add(i + "");
}
}
}
主要代码一共没有几行,点击列表项后,在onItemClick中通知列表刷新数据,在getView时,判断点击的列表项被选中的状态,如果被选中,设置背景色为红色,否则设置为透明色
运行程序,点击列表项
点击某个列表后,背景色变为红色,代表被选中,再次点击同一个列表项后,背景色又变为透明色,代表取消选中
ListView的CHOICE_MODE_MULTIPLE_MODAL模式
这是ListView的另一个多选模式,通过长按ListView的列表项或主动调用setItemChecked方法选中某个列表项进入该模式,这种模式下不会触发ListView的onClick事件。设置为这种模式后,需要给ListView添加MultiChoiceModeListener事件监听,实现其中的几个方法。
可以通过点击菜单项和长按列表项进入多选模式,进入多选模式后,ActionBar的布局和菜单项会发生改变,可以在ActionBar上显示选中的条目数,所以首先要定义ActionBar的布局文件和菜单文件
布局文件actionbar_view.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="已选中条目数:"/>
<TextView
android:id="@+id/selected_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
菜单文件
进入多选模式前的菜单main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.testlistview.MainActivity" >
<item
android:id="@+id/enter_mode"
android:orderInCategory="100"
android:title="进入多选模式"
app:showAsAction="never"/>
</menu>
进入多选模式后的菜单multiple_mode_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.testlistview.MainActivity" >
<item
android:id="@+id/select_all"
android:orderInCategory="100"
android:title="全选"
android:showAsAction="never"/>
<item
android:id="@+id/unselect_all"
android:orderInCategory="200"
android:title="取消全选"
android:showAsAction="never"/>
</menu>
在正常模式下,有一个菜单项,点击菜单项或者长按列表项进入多选模式,进入多选模式后,ActionBar上的View布局显示选中条目的数目,有2个菜单项:全选、取消全选
下面是MainActivity的代码
public class MainActivity extends ActionBarActivity {
ListView listView;
List<String> datas = new ArrayList<String>();
ListViewAdapter adapter;
ModeCallback callback;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.list);
initData();
adapter = new ListViewAdapter();
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
callback = new ModeCallback();
listView.setMultiChoiceModeListener(callback);
}
class ModeCallback implements MultiChoiceModeListener {
View actionBarView;
TextView selectedNum;
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return true;
}
//退出多选模式时调用
@Override
public void onDestroyActionMode(ActionMode mode) {
// TODO Auto-generated method stub
listView.clearChoices();
}
//进入多选模式调用,初始化ActionBar的菜单和布局
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
getMenuInflater().inflate(R.menu.multiple_mode_menu, menu);
if(actionBarView == null) {
actionBarView = LayoutInflater.from(MainActivity.this).inflate(R.layout.actionbar_view, null);
selectedNum = (TextView)actionBarView.findViewById(R.id.selected_num);
}
mode.setCustomView(actionBarView);
return true;
}
//ActionBar上的菜单项被点击时调用
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
switch(item.getItemId()) {
case R.id.select_all:
for(int i = 0; i < adapter.getCount(); i++) {
listView.setItemChecked(i, true);
}
updateSelectedCount();
adapter.notifyDataSetChanged();
break;
case R.id.unselect_all:
listView.clearChoices();
updateSelectedCount();
adapter.notifyDataSetChanged();
break;
}
return true;
}
//列表项的选中状态被改变时调用
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position,
long id, boolean checked) {
// TODO Auto-generated method stub
updateSelectedCount();
mode.invalidate();
adapter.notifyDataSetChanged();
}
public void updateSelectedCount() {
int selectedCount = listView.getCheckedItemCount();
selectedNum.setText(selectedCount + "");
}
}
class ListViewAdapter extends BaseAdapter {
@Override
public int getCount() {
// TODO Auto-generated method stub
return datas.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return datas.get(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 stub
ViewHolder viewHolder;
if(convertView == null) {
viewHolder = new ViewHolder();
convertView = View.inflate(MainActivity.this, R.layout.item, null);
viewHolder.text = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder)convertView.getTag();
}
viewHolder.text.setText(datas.get(position));
if(listView.isItemChecked(position)) {
convertView.setBackgroundColor(Color.RED);
} else {
convertView.setBackgroundColor(Color.TRANSPARENT);
}
return convertView;
}
class ViewHolder {
TextView text;
}
}
public void initData() {
for(int i = 1; i <= 15; i++) {
datas.add(i + "");
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.enter_mode) {
listView.setItemChecked(0, true);
listView.clearChoices();
callback.updateSelectedCount();
}
return super.onOptionsItemSelected(item);
}
}
长按某个列表项进入多选模式后,会自动选中长按的item,这个是正常的,但是如果点击菜单项进入多选模式,只能通过调用listView的setItemChecked方法选中一个item进入,但是这种情况在用户看来是不可理解的,所以在onOptionsItemSelected(MenuItem item)中点击菜单进入多选模式后,要调用listView.clearChoices()清空选中的列表项,这样就不会选中任何列表项
接下来定义了一个实现MultiChoiceModeListener接口的类,实现了接口中的几个方法
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
...
}
进入多选模式时,会调用这个方法来初始化ActionBar上的布局和菜单项。在方法中,加载了菜单文件和布局文件,并调用mode.setCustomView(actionBarView)设置ActionBar的布局
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
...
}
进入多选模式后,点击ActionBar上的菜单项会触发此方法,多选模式下的菜单有2个:全选和取消全选,点击全选后执行的代码是
for(int i = 0; i < adapter.getCount(); i++) {
listView.setItemChecked(i, true);
}
updateSelectedCount();
adapter.notifyDataSetChanged();
break;
遍历并设置adapter中的所有item项为选中状态,然后更新Action View中的选中数目,最后调用notifyDataSetChanged方法通知ListView刷新列表
点击取消全选执行的代码是
listView.clearChoices();
updateSelectedCount();
adapter.notifyDataSetChanged();
break;
调用clearChoices()清空LilistView的选中项,并更新View的选中数目,最后同样要通知ListView刷新列表
然后是
@Override
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
...
}
进入多选模式后点击每个列表项都会回调此方法,方法中的逻辑很简单,更新选中数目,并通知ListView刷新列表
最后是onDestroyActionMode(ActionMode mode)方法,方法中只有一行代码,在退出多选模式后清空ListView中的选中项即可
运行效果,正常模式下
进入多选模式
点击选中列表项
全选
取消全选