支持多選的ListView實現方法(不需要CheckBox)

今天在項目中需要實現一個支持多選的ListView,以前的做法是通過給每個列表項添加CheckBox來解決,但是總覺得這樣破壞了列表整體的美觀。

還是自己摸索下吧。先看看ListView的API,木有發現能用的,在往上看看父類AbsListView,找到一個貌似能用的

public void setItemChecked (int position, boolean value)

Added in API level 1

Sets the checked state of the specified position. The is only valid if the choice mode has been set to CHOICE_MODE_SINGLE or CHOICE_MODE_MULTIPLE.

Parameters
position The item whose checked state is to be checked
value The new checked state for the item

就是說,只要把ListView的choiceMode設置爲單選或者多選而不是不能選擇的話,就可以通過這個來設置某個列表項的選中狀態?
還有一個對應的方法是獲取列表項選中狀態

public boolean isItemChecked (int position)

Added in API level 1

Returns the checked state of the specified position. The result is only valid if the choice mode has been set to CHOICE_MODE_SINGLE or CHOICE_MODE_MULTIPLE.

Parameters
position The item whose checked state to return
Returns
  • The item's checked state or false if choice mode is invalid
好了,看到這裏,我激動的以爲問題解決了。於是開始寫測試代碼。
private ListView lv_contacts;
lv_contacts.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
lv_contacts = (ListView) findViewById(R.id.lv_contacts);
/**
* 中間省略設置Adapter等初始化代碼
*/
lv_contacts.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                boolean isChecked = isItemChecked(position);
                Log.i(TAG, "checked state= "+isChecked);
		lv_contacts.setItemChecked(position, !isChecked);
            }
});
然後開始測試,看Log輸出。
我點,我再點,我再再點。。。
爲毛一直輸出
checked state= true
一番測試無果,在API doc上也沒找到原因,這不靠譜的文檔啊。
還是自己想辦法吧,咱自己定義一個數組專門來保存選中狀態不行麼!列表項選中就讓它變色,取消選中就變回去,一目瞭然。
如果直接這麼做,我們一定會遇到一個問題,就是當列表項一個屏幕顯示不下的時候,你上下一拖動,發現問題了,表示列表項選中狀態的顏色顯示亂掉了。比如你在頭上選中兩個,然後一拖動,下面怎麼出來兩個變了顏色的列表項目?
其實呢,這是因爲列表在顯示的時候,爲了提高運行效率,是對列表項進行復用的,我們查看源代碼就可以知道,如果getView的時候發現convertview不爲null,那麼就直接複用這個convertview了。好了,問題就出來這裏,如果我們直接使用現成的Adapter例如ArrayAdapter,SimpleAdapter的話,它們是隻處理顯示的數據的,也就是在bindView裏面僅僅對列表的數據進行了更新,我們自己改變顏色這種事情,它當然是不去修改的,於是就出現了被複用的view顏色也被不恰當的複用了的情況。
分析完畢,下面開始修正,以SimpleAdapter爲例吧,其實很簡單,繼承它之後複寫父類的getView方法就行了。不多說,上代碼:
public class ContactsChooserActivity extends Activity {

    private static final String TAG = "ContactsChooserActivity";

    private ListView lv_contacts;
    private List<Map<String, String>> list_data;
    private boolean[] bCheckStatesArray;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contacts_chooser);
        lv_contacts = (ListView) findViewById(R.id.lv_contacts);
        // 初始化列表數據
        list_data = initListData();
        bCheckStatesArray = new boolean[list_data.size()];
        ContactsAdapter adapter = new ContactsAdapter(this, list_data, R.layout.item_contacts_list,
                new String[]{"NAME", "NUMBER"}, new int[]{R.id.tv_item_name, R.id.tv_item_number});
        lv_contacts.setAdapter(adapter);
        lv_contacts.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                boolean isChecked = bCheckStatesArray[position];
                if (isChecked) {
                    view.setBackgroundColor(Color.TRANSPARENT);
                    bCheckStatesArray[position] = false;
                } else {
                    view.setBackgroundColor(Color.BLUE);
                    bCheckStatesArray[position] = true;
                }
            }
        });
        // 把選中狀態記錄數組初始化一下
        for (int i = 0; i < bCheckStatesArray.length; i++) {
                bCheckStatesArray[i] = false;
        }
    }


    /**
     * 在getView方法中,如果不對view進行處理,就會導致上下拉動列表之後,列表項被選中的顏色狀態混亂。
     */
    private class ContactsAdapter extends SimpleAdapter {

        public ContactsAdapter(Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to) {
            super(context, data, resource, from, to);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            //return super.getView(position, convertView, parent);
            View v = super.getView(position, convertView, parent);
            //根據選中狀態設置好顏色,防止拖動後出現混亂。
            if (bCheckStatesArray[position]) {
                v.setBackgroundColor(Color.BLUE);
            } else {
                v.setBackgroundColor(Color.TRANSPARENT);
            }
            return v;
        }
    }
}

列表項的佈局xml
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:paddingTop="@dimen/item_vertical_margin"
    android:paddingBottom="@dimen/item_vertical_margin"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="NAME"
        android:layout_weight="1"
        android:id="@+id/tv_item_name" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="NUMBER"
        android:id="@+id/tv_item_number" />
</LinearLayout>

主要的代碼就是這些,關於實現列表多選的其它更好方法,還希望大家多多指教和交流。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章