Android ListView CHOICE_MODE_MULTIPLE_MODAL多选模式解析

最近的课程需要做一个通讯录App,于是就想借鉴Android自带的Contacts应用,实现里面的一些效果。在删除联系人时,Contacts的界面是这样的。

删除联系人

一开始觉得这个功能很复杂,后来在网上找了一下,发现这个功能是Android自带的,只要在xml布局文件中将ListViewmode属性设置为CHOICE_MODE_MULTIPLE_MODAL,然后在列表项上长按,即可进入多选模式,同时工具栏也会发生变化。

要想处理多选模式下的各种行为(更换菜单栏、响应列表项的check状态变化以及菜单项的点击事件),需要一个实现ListView.MultiChoiceModeListener,然后将这个类的对象传给ListViewsetMultiChoiceModeListener()方法。

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。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页