ListView多選和單選模式重新整理


  •    超簡單的單選和多選ListView

 

    在開發過程中,我們經常會使用ListView去呈現列表數據,比如商品列表,通話記錄,聯系人列表等等,在一些情況下,我們還需要去選擇其中的一些列表數據進行編輯。以前,我在項目開發中,都是在自定義的Adapter中去維護一個SparseBooleanArray變量來保存當前ListView中已經被選中的項,然后在自定義Adapter的getView()和ListView的setOnItemClickListener()方法中去實時更新SparseBooleanArray變量,從而當用戶選擇提交數據的時候,直接遍歷SparseBooleanArray中的值就可以了,這種做法,雖然也能實現功能,但是無疑增加了代碼開銷。

    今天,在看文檔的時候,發現了一個更好的解決方案(很多人已經用過了吧):使用ListView的choiceMode,官方文檔見如下:

                         


    根據上面的文檔說明,可以知道,android:choiceMode有以下幾個值:默認(不設置android:choiceMode屬性,即不支持單選或多選),singleChoice(單選),multipleChoice(多選),mutipleChoiceModal(特殊多選模式,可以通過設置MultiChoiceModeListener進行監聽選擇模式,類似ActionBar的ActionMode)。在XML中給ListView設置了android:choiceMode屬性一個值后,我們還需要給ListView一個適配器,這里我們使用默認的ArrayAdapter:

 

    //如果是單選模式,則可以使用 android.R.layout.simple_list_item_single_choice
    ArrayAdapter<String> myAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, mDatas);
    mListView.setAdapter(myAdapter);


    這樣我們就已經完成了ListView的多選或單選實現。

    另外一個問題是:我們怎樣才能獲取當前ListView中被選中的那些項呢?

    我們其實可以通過ListView的isItemChecked(int position)方法判斷一項是否被選中,或直接使用ListView.getCheckedItemPositions()來獲取所有選中的項,這個方法返回一個SparseBooleanArray對象,遍歷它就可以獲取所有選中的項。

  • 自定義ListView的多選和單選項布局


  對於一些簡單的列表,上面的方法可能已經能夠滿足需求。其實,上面的列表項只顯示了一個標題和一個復選框,但在實際開發中,UE或產品經理可能要求我們去實現的列表遠比上面的列表復雜得多,所以往往就需要使用自定義的Adapter來填充ListView。

     但是,如果我們使用自定義的Adapter來填充ListView,那怎么讓我們自定義的Checkbox能夠無縫銜接ListView的選擇狀態呢?

     一種普遍的做法是在重寫自定義Adapter的getView()時,先通過convertView.findViewById()獲取到Checkbox后,通過mList.isItemChecked(int position)判斷當前position的狀態后,再去更新Checkbox的選擇狀態。

     這里,我介紹的是另外一種方法。
     首先,我們先來看下為什么我們使用android.R.layout.simple_list_item_multiple_choice布局來填充ArrayAdapter時,不需要我們自己去維護CheckBox的選擇狀態?
     查看ListView的源碼,在ListView的setupChild方法中,有下面的一段代碼:
    

    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));
       }
    }


   即如果ListView的child(從自定義的Adapter的getView()方法中返回的View)實現了Checkable接口,那么當listView的項選擇狀態改變時,listView也會去同步更新這個child的狀態(android 3.1或3.1以上平台,會觸發setActivated方法),其實simple_list_item_multiple_choice.xml中只有一個CheckedTextView
  

<CheckedTextView          
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"    
    android:layout_height="?android:attr/listPreferredItemHeightSmall"
    android:textAppearance="?android:attr/textAppearanceListItemSmall"
    android:gravity="center_vertical"
    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />


    而CheckedTextView 是實現了Checkable接口的,所以當我們使用simple_list_item_multiple_choic.xml布局作為Adapter的getView()的返回值時,是不需要我們額外去關心Checkbox的狀態問題。
    通過上面的分析,我們自定義一個View時,只需要實現了Checkable接口,那么就不用我們在getView中去額外維護選中狀態了。如果android3.1或android3.1以上的平台,我們還可以重寫setActivated方法來更新我們的選中狀態。相關示例代碼如下:

package com.shaoxiong.li.marvel.myapplication;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Checkable;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * Created by lishaoxiong on 16-2-22.
 */
public class CustomCheckTextView extends LinearLayout implements Checkable {

    private TextView titleView;
    private CheckBox mCheckBox;

    public CustomCheckTextView(Context context) {
        this(context, null);
    }

    public CustomCheckTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomCheckTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);


        LayoutInflater mLayoutInflater = LayoutInflater.from(context);
        //將加載出來的View添加到當前View層級中去。
        //有兩種方案,一種是加載布局時將rootView傳進去,或直接使用addView添加進去
        //View v = mLayoutInflater.inflate(R.layout.layout_custom_ctv, null);
        View v = mLayoutInflater.inflate(R.layout.layout_custom_ctv, this, true);
        titleView = (TextView)v.findViewById(R.id.headListView_item_text);
        mCheckBox = (CheckBox)v.findViewById(R.id.headListView_item_cb);
        //this.addView(v);
    }

    @Override
    public void setChecked(boolean checked) {
        mCheckBox.setChecked(checked);
    }

    @Override
    public boolean isChecked() {
        return mCheckBox.isChecked();
    }

    @Override
    public void toggle() {
        mCheckBox.toggle();
    }

    public void setTitle(String title) {
        titleView.setText(title);
    }

    @Override
    public void setActivated(boolean activated) {
        super.setActivated(activated);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="horizontal"
   android:padding="20dp">
    <CheckBox
        android:id="@+id/headListView_item_cb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:focusable="false"
        android:clickable="false"
        android:focusableInTouchMode="false"/>

    <TextView
       android:id="@+id/headListView_item_text"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginRight="10dp"/>


</LinearLayout>
    public class MyAdapter extends BaseAdapter {

        private Context mContext;
        private ArrayList<String> dataList;

        public MyAdapter(Context context, ArrayList<String> dataList) {
            this.mContext = context;
            this.dataList= dataList;
        }

        @Override
        public int getCount() {
            return dataList.size();
        }

        @Override
        public Object getItem(int position) {
            return dataList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;
            if(convertView == null) {
                convertView = LayoutInflater.from(mContext).inflate(R.layout.item_headlistview, null);
                viewHolder = new ViewHolder();
                //viewHolder.mTextView = (TextView)convertView.findViewById(R.id.headListView_item_text);
//                viewHolder.checkedTv = (CheckedTextView)convertView.findViewById(R.id.item_checked_tv);
                viewHolder.customCheckTextView = (CustomCheckTextView)convertView;
                convertView.setTag(viewHolder);
            }else {
                viewHolder = (ViewHolder)convertView.getTag();
            }
//            viewHolder.checkedTv.setText(dataList.get(position));
//            viewHolder.mTextView.setText(dataList.get(position));
            viewHolder.customCheckTextView.setTitle(dataList.get(position));
            return convertView;
        }
    }

    static class ViewHolder {
//        TextView mTextView;
//        CheckedTextView checkedTv;
        CustomCheckTextView customCheckTextView;
    }

  

    這樣,通過上面的方法,你就可以去實現各種自己自定義好布局的多選或單選列表了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM