有不少人在使用listView设置单选和多选的时候不知如何下手,或者从网上找了一些例子,费很大劲才看懂,但又以为人家写的太过复杂。其中网上不少的demo的中心思想是找到要选择的cell,而后使用adapter.notifyDataSetChanged(),来达到单选或者多选的效果,固然这是一种解决办法;还有一种思想是,让adapter中的View实现Checkable接口,而后达到效果,这是一种很是简便的方式,但网上的那些例子又都没有说出之因此然,因此不少人疑问为何个人View要实现Checkable。下面我将一一说明。java
首先,要明确两点,要实现单选或者多选:android
1.listview设置选择模式,即:ide
public void setChoiceMode(int choiceMode)
模式分为:ui
/** * Normal list that does not indicate choices */ public static final int CHOICE_MODE_NONE = 0; /** * The list allows up to one choice */ public static final int CHOICE_MODE_SINGLE = 1; /** * The list allows multiple choices */ public static final int CHOICE_MODE_MULTIPLE = 2; /** * The list allows multiple choices in a modal selection mode */ public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
这四种模式。当你设置完模式后,其实你的ListView已是单选模式或者多选模式了,但当咱们点击时为何没有反应呢,看第二条。this
2.ViewHolder中的控件实现Checkable接口。spa
这里解释一下为何要实现Checkable接口,首先看看Listview类中的一个方法:.net
/** * Add a view as a child and make sure it is measured (if necessary) and * positioned properly. * * @param child The view to add * @param position The position of this child * @param y The y position relative to which this view will be positioned * @param flowDown If true, align top edge to y. If false, align bottom * edge to y. * @param childrenLeft Left edge where children should be positioned * @param selected Is this position selected? * @param recycled Has this view been pulled from the recycle bin? If so it * does not need to be remeasured. */ private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft, boolean selected, boolean recycled) {...}
咱们不用管listview何时调用的setupChild这个方法,咱们只需知道当listview列表展现时,它必定调用了该方法,其中方法中有一段代码:code
if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { if (child instanceof Checkable) { ((Checkable) child).setChecked(mCheckStates.get(position)); } else if (getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { child.setActivated(mCheckStates.get(position)); } }
ok,当咱们设置的模式不是默认模式时,若是咱们的view实现了Checkable,这个child就调用setChecked这个方法,这个方法又是个抽象方法,咱们能够实现它作咱们想作的事情。这就是咱们为何实现Checkable了。orm
好了,知道这些以后,我在说说整个流程:xml
首先listview.setChoiceMode:
public void setChoiceMode(int choiceMode) { mChoiceMode = choiceMode; if (mChoiceActionMode != null) { mChoiceActionMode.finish(); mChoiceActionMode = null; } if (mChoiceMode != CHOICE_MODE_NONE) { if (mCheckStates == null) { mCheckStates = new SparseBooleanArray(0); } if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) { mCheckedIdStates = new LongSparseArray<Integer>(0); } // Modal multi-choice mode only has choices when the mode is active. Clear them. if (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) { clearChoices(); setLongClickable(true); } } }
调用这个方法时,这里面有两个重要的变量:1.mCheckStates 2.mCheckIdStates,若不懂这两个变量的类型,暂且把它们当成两个map,一个是Map<Integer,Boolean>类型,一个是Map<Long,Intger>类型,mCheckStates是用来标示哪些位置是选中的,mCheckIdStates是用来标示那些选中位置的viewId是多少。对于不懂SparseBooleanArray和LongSparseArray的能够参考个人一篇博客:http://my.oschina.net/gef/blog/600698?fromerr=MP2ZlkD6。
而后当listview加载完成,咱们点击某一项时,会执行:
public boolean performItemClick(View view, int position, long id) {...}
方法,其中有一段代码:
if (mChoiceMode == CHOICE_MODE_MULTIPLE || (mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL && mChoiceActionMode != null)) { boolean checked = !mCheckStates.get(position, false); mCheckStates.put(position, checked); if (mCheckedIdStates != null && mAdapter.hasStableIds()) { if (checked) { mCheckedIdStates.put(mAdapter.getItemId(position), position); } else { mCheckedIdStates.delete(mAdapter.getItemId(position)); } } if (checked) { mCheckedItemCount++; } else { mCheckedItemCount--; } if (mChoiceActionMode != null) { mMultiChoiceModeCallback.onItemCheckedStateChanged(mChoiceActionMode, position, id, checked); dispatchItemClick = false; } checkedStateChanged = true; } else if (mChoiceMode == CHOICE_MODE_SINGLE) { boolean checked = !mCheckStates.get(position, false); if (checked) { mCheckStates.clear(); mCheckStates.put(position, true); if (mCheckedIdStates != null && mAdapter.hasStableIds()) { mCheckedIdStates.clear(); mCheckedIdStates.put(mAdapter.getItemId(position), position); } mCheckedItemCount = 1; } else if (mCheckStates.size() == 0 || !mCheckStates.valueAt(0)) { mCheckedItemCount = 0; } checkedStateChanged = true; }
意思就是当你点击某一项的时候,根据你设置的ChoiceMode,那两个重要的变量mCheckStates 和mCheckIdStates会添加相应的值。同时也会回调setUpChild方法,而后根据:
if (child instanceof Checkable) { ((Checkable) child).setChecked(mCheckStates.get(position)); }
展现setCheckable方法里面的内容。
OK,来个例子:
Activity中:
public class ListViewSingleChoiceActivity extends AppCompatActivity{ private ListView listview; private ArrayList<String> arrayList = new ArrayList<>(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_singlechoice); listview = (ListView) findViewById(R.id.listview); listview.setChoiceMode(ListView.CHOICE_MODE_SINGLE); for (int i = 0;i < 10;i++){ arrayList.add("hahahahahahhahaha" + i); } listview.setAdapter(new MyAdapter(this,1,arrayList)); } class MyAdapter extends ArrayAdapter<String>{ public MyAdapter(Context context, int resource, List<String> objects) { super(context, resource, objects); } @Override public View getView(int position, View convertView, ViewGroup parent) { MyView myView = null; if (convertView == null){ myView = new MyView(ListViewSingleChoiceActivity.this); }else{ myView = (MyView) convertView; } myView.setText(getItem(position)); return myView; } } class MyView extends FrameLayout implements Checkable{ private TextView content; private CheckBox checkBox; public MyView(Context context) { super(context); View.inflate(context,R.layout.item_single_choice,this); content = (TextView) findViewById(R.id.content); checkBox = (CheckBox) findViewById(R.id.checkbox); } public void setText(String text) { content.setText(text); } @Override public void setChecked(boolean checked) {//重要方法 checkBox.setChecked(checked); } @Override public boolean isChecked() { return checkBox.isChecked(); } @Override public void toggle() { checkBox.toggle(); } } }
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"> <CheckBox android:id="@+id/checkbox" android:text="hello" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/content" android:text="gefgef" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
效果图就不上传了,就是单选模式。