最近的课程需要做一个通讯录App,于是就想借鉴Android自带的Contacts应用,实现里面的一些效果。在删除联系人时,Contacts的界面是这样的。
一开始觉得这个功能很复杂,后来在网上找了一下,发现这个功能是Android自带的,只要在xml布局文件中将ListView
的mode属性
设置为CHOICE_MODE_MULTIPLE_MODAL,然后在列表项上长按,即可进入多选模式,同时工具栏也会发生变化。
要想处理多选模式下的各种行为(更换菜单栏、响应列表项的check状态变化以及菜单项的点击事件),需要一个实现ListView.MultiChoiceModeListener
,然后将这个类的对象传给ListView
的setMultiChoiceModeListener()
方法。
ListView.MultiChoiceModeListener接口的方法
MultiChoiceModeListener
中只有了onItemCheckedStateChanged(...)
方法,但是它继承了ActionMode.Callback
接口,里面又包含onCreateActionMode(...)
、onPrepareActionMode(...)
、onActionItemClicked(...)
、和onDestroyActionMode(...)
方法,这些方法由ListView
回调。
下面对这几个方法里要做的工作做简要介绍:
onItemCheckedStateChanged(...)
:这是多选模式下特有的方法,监听列表项的选中状态变化,比如可以在里面进行CheckBox的UI更新,也可以是标题栏中选择的项的数量更新。onCreateActionMode(...)
:可以进行多选模式下的菜单栏布局更新,但是只在多选模式创建时调用一次。onPrepareActionMode(...)
:在创建完成后,每当需要进行菜单栏布局的更新,可以在这个方法中进行。onActionItemClicked(...)
:监听多选模式下的菜单栏项的点击事件。onDestroyActionMode(...)
:在多选模式退出时调用,可以在里面完成还原正常模式下的菜单栏布局。
实现多选删除功能还需要知道的一点知识
当我点击了多选菜单栏中的删除按钮时,我如何得知哪些列表项是被选中的呢?
ListView
为我们提供了getCheckedItemPositions()
方法,但其返回结果并不是一个简单的下表的列表,而是一个SparseBooleanArray
对象,里面包括了每个被选中项的index,以及其选中状态(`getCheckedItemPositions()
方法返回的肯定都是true了,但是当返回的是所有列表项的SparseBooleanArray
对象时,里面的选中状态就有用了)。但是直接根据index删除,List中项的index会发生变化,可以先将指定index的对象全部取出放在另一个List里,然后调用List.remove(Object)方法,这样就可以避免错删了。
实现的效果以及源代码
源代码如下:
布局文件fragment_multi_choice_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:choiceMode="multipleChoiceModal">
</ListView>
</LinearLayout>
正常模式下的菜单栏布局文件normal_mode_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
</menu>
多选模式下的菜单栏布局文件multi_choice_mode_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_item_delete"
android:icon="@drawable/ic_delete"
app:showAsAction="ifRoom|withText"
android:title="@string/delete_contacts"/>
</menu>
Java文件MultiChoiceListFragment.java
public class MultiChoiceListFragment extends Fragment {
@BindView(R.id.list_view)
ListView mListView;
private List<String> mList;
private ModeCallback mCallback;
private ArrayAdapter mAdapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_multi_choice_list, container, false);
ButterKnife.bind(this, view);
mList = new ArrayList<>();
mList.add("one");
mList.add("two");
mList.add("three");
mList.add("four");
mList.add("five");
mList.add("six");
mList.add("seven");
mAdapter = new ArrayAdapter<>(Objects.requireNonNull(getActivity()),
android.R.layout.simple_list_item_1, mList);
mListView.setAdapter(mAdapter);
mCallback = new ModeCallback();
mListView.setMultiChoiceModeListener(mCallback);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Toast.makeText(getActivity(), "选择了一个item", Toast.LENGTH_SHORT).show();
}
});
return view;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.normal_mode_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
private class ModeCallback implements ListView.MultiChoiceModeListener {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.multi_choice_mode_menu, menu);
mAdapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_multiple_choice, mList);
mListView.setAdapter(mAdapter);
mode.setTitle(getString(R.string.selected_num,
mListView.getCheckedItemCount()));
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return true;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getItemId() == R.id.menu_item_delete) {
SparseBooleanArray array = mListView.getCheckedItemPositions();
System.out.println(array.size());
List<String> toDelItems = new ArrayList<>();
for (int i = 0; i < array.size(); ++i) {
System.out.println("key: " + array.keyAt(i) + " value: " + array.valueAt(i));
toDelItems.add(mList.get(array.keyAt(i)));
}
for (String s : toDelItems) mList.remove(s);
mAdapter.notifyDataSetChanged();
mode.finish();
}
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mListView.clearChoices();
mAdapter = new ArrayAdapter<>(Objects.requireNonNull(getActivity()),
android.R.layout.simple_list_item_1, mList);
mListView.setAdapter(mAdapter);
}
@Override
public void onItemCheckedStateChanged(ActionMode mode,
int position, long id, boolean checked) {
mode.setTitle(getString(R.string.selected_num, mListView.getCheckedItemCount()));
}
}
}
项目的github地址藏在文中的某个地方,欢迎star、fork。