有不少人在使用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>
效果圖就不上傳了,就是單選模式。