ListView 中有一个属性:android:choiceMode, 对应三个可选值: * singleChoice 单选 * multipleChoice 多选 * none 默认情况,没有选中效果 在 ListView 的布局中设置了 android:choiceMode 属性后,item 布局需要实现 checkable,才有选中效果。 那么我们先来看一下这个 checkable 接口: ``` /** * Defines an extension for views that make them checkable. * */ public interface Checkable { /** * Change the checked state of the view * * @param checked The new checked state */ void setChecked(boolean checked); /** * @return The current checked state of the view */ boolean isChecked(); /** * Change the checked state of the view to the inverse of its current state * */ void toggle(); } ``` 接口很简单,就三个方法: * setChecked(boolean checked) 设置是否选中。当我们点击 item 的时候,会调用这个方法。 * boolean isChecked() 判断是否选中。 * toggle() 开关,如果当前是选中的状态,调用该方法后取消选中,反之,选中。 #### 实现单选效果: 1、 ListView 布局中 android:choiceMode 设置为 singleChoice。 2、选取实现了 checkable 接口的 View 或者 ViewGroup 作为 item 布局控件。 * 当 item 展示的数据比较简单,例如就是一段文本,item 布局可以直接使用系统自带的 CheckedTextView 控件,该控件有一个属性:android:checkMark="?android:listChoiceIndicatorSingle" 为单选样式;“?android:listChoiceIndicatorMultiple” 为多选样式。若要修改显示的样式,可以自己写一个 selector, 然后 checkMark 指定为这个 selector。例如: 在 drawable 文件夹下面创建一个 ic_hideable_item.xml 文件。 ``` ``` checkMark 指定为上面的那个 xml 文件: ``` ``` #### 实现多选效果: 1、 ListView 布局中 android:choiceMode 设置为 multipleChoice。 2、选取实现了 checkable 接口的 View 或者 ViewGroup 作为 item 布局控件。 这里笔者自定义一个控件实现 Checkable 接口。代码如下: ``` public class CheckableLayout extends RelativeLayout implements Checkable { private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked}; private boolean mChecked; public CheckableLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void setChecked(boolean b) { if (b != mChecked){ mChecked = b; refreshDrawableState(); } } @Override public boolean isChecked() { return mChecked; } @Override public void toggle() { setChecked(!mChecked); } @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) mergeDrawableStates(drawableState, CHECKED_STATE_SET); return drawableState; } } ``` 应用到 item 布局: ``` ``` 注意到上面 TextView、ImageView 控件中的 android:duplicateParentState 属性, 该属性表示当前控件是否跟随父控件的状态(点击、焦点等)。若将 TextView 的该属性置为 false,则文字无变色效果;若将 ImageView 的该属性置为 false,则无选中效果。 最后怎样获取选中 item 对应的位置呢? * 单选 ---> 通过 ListView 的 getCheckedItemPosition() 获取选中的位置。 * 多选 ---> 通过 ListView 的 getCheckedItemPositions() 得到一个 SparseBooleanArray,key 为 position,value 为是否选中。 ``` mSingleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { int checkedItemPosition = mSingleListView.getCheckedItemPosition(); Toast.makeText(MainActivity.this, "you chose item" + checkedItemPosition, Toast.LENGTH_SHORT).show(); } }); mMultipleListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { SparseBooleanArray checkedItemPositions = mMultipleListView.getCheckedItemPositions(); boolean isChecked = checkedItemPositions.get(position); Toast.makeText(MainActivity.this, "item" + position + "isChecked=" + isChecked, Toast.LENGTH_SHORT).show(); } }); ``` [源码传送门](https://github.com/jamin918/CustomChoiceList)