ListView單選和多選模式徹底解析

有不少人在使用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>

效果圖就不上傳了,就是單選模式。

相關文章
相關標籤/搜索