1. 程式人生 > >android 自定義ListView的點選樣式。

android 自定義ListView的點選樣式。

google官方教程 自定義ListView列表項的選中狀態。

注:以下程式碼來自google官方Demo。
我們有個資料展示的需求,需要以ListView形式展示,但是我們需要一個狀態去標識出已選擇或者未選擇,如圖:
這裡寫圖片描述

可以看到,如果是已選擇狀態,列表項的文字和右側圖示是高亮的。
接下來看如何實現:
首先,定義一下 color state,定義文字被選中和未選中時的顏色變化 :
檔案:hideable_text_color.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
android:state_checked="false" android:color="#6000" />
<item android:color="#09c" /> </selector>

同樣,對右側圖示定義一個drawable state:
檔案:ic_hideable_item.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="false" android:drawable="@drawable/ic_hideable_item_unchecked"
/>
<item android:drawable="@drawable/ic_hideable_item_checked" /> </selector>

接下來,定義列表項的佈局容器,定義其狀態切換的功能。這裡採用的是一個LinearLayout作為父佈局。我們需要繼承它,然後複寫它的onCreateDrawableState方法,把我們自定義的狀態在合適的時候新增進去。並實現Checkable介面,實現該介面的好處是:當列表被點選時,Checkable實現類的相關方法,如 isChecked(),setChecked()自動會被呼叫,不用我們再顯示實現列表項點選事件,再呼叫這些方法去改變列表狀態,所以是很方便的。

public class CheckableLinearLayout extends LinearLayout implements Checkable {
    private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};

    private boolean mChecked = false;

    public CheckableLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public boolean isChecked() {
        return mChecked;
    }

    public void setChecked(boolean b) {
        if (b != mChecked) {
            mChecked = b;
            refreshDrawableState();
        }
    }

    public void toggle() {
        setChecked(!mChecked);
    }

    @Override
    public int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        }
        return drawableState;
    }
}

程式碼不復雜,聲明瞭一個mChecked,然後在mChecked=true的情況下,通過onCreateDrawableState方法,加入我們自定義的狀態。

接下來使用該佈局容器:註釋裡寫了:使用該佈局作為列表項容器的 ListView 要設定為”選擇模式”。這就是為什麼該佈局容器要實現 Checkable的原因,因為一旦列表項被選擇,就會呼叫Checkable介面的方法,這是Android為我們實現的一套機制,很方便,直接使用吧。
檔案:list_item.xml

<!--
    The ListView from sample_main.xml has a choiceMode set, meaning that when a user
    selects a list item, the ListView will set the state for that item's root view
    (this CheckableLinearLayout) to "checked". Note that this requires that the root view
    implements the Checkable interface. Once the root view is checked, any children that
    have the duplicateParentState attribute set will inherit this "checked" state.
-->
<com.example.android.customchoicelist.CheckableLinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="8dp"
    android:paddingRight="8dp"
    android:minHeight="?android:listPreferredItemHeight"
    android:gravity="center_vertical">

    <!--
        The duplicateParentState attribute on this TextView, along with the color state list
        used in the textColor attribute causes its text color to change when its parent
        is checked or unchecked.
    -->
    <TextView android:id="@android:id/text1"
        android:duplicateParentState="true"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content"
        android:textAppearance="?android:textAppearanceMedium"
        android:textColor="@color/hideable_text_color" />

    <!--
        The duplicateParentState attribute on this ImageView, along with the state list
        drawable in the src attribute causes its image to change when its parent
        is checked or unchecked.

        To use the standard radio or checkmark image, set the src to
        ?android:listChoiceIndicatorMultiple or ?android:listChoiceIndicatorSingle. These
        are system theme attributes that reference a state list drawable.
    -->
    <ImageView android:src="@drawable/ic_hideable_item"
        android:duplicateParentState="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp" />

</com.example.android.customchoicelist.CheckableLinearLayout>

注意控制元件都有個 android:duplicateParentState=”true” 的屬性,如果設定此屬性,將直接從父容器中獲取繪圖狀態(游標,按下等)。 注意僅僅是獲取繪圖狀態,而沒有獲取事件,也就是你點一下LinearLayout時Button有被點選的效果,但是不執行點選事件,這樣就可以更新控制元件的狀態了。

接下來定義一個ListView:
檔案:sample_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:showDividers="middle"
    android:divider="?android:dividerHorizontal">

    <TextView style="@style/Widget.DescriptionBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/intro_message" />

    <!--
        When a ListView has a choiceMode set, it will allow users to "choose"
        one or more items. The framework provides default list item layouts
        that show standard radio buttons or check boxes next to a
        single line of text:

        android.R.layout.simple_list_item_single_choice and
        android.R.layout.simple_list_item_multiple_choice.

        In some cases, you may want to customize this layout. When doing so,
        the root view must implement the Checkable interface.

        Lastly, remember to use padding on your ListViews to adhere to the standard
        metrics described in the Android Design guidelines. When doing so,
        you should set the android:scrollbarStyle such that the scrollbar
        doesn'isn't inset.
    -->
    <ListView android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:paddingLeft="@dimen/page_margin"
        android:paddingRight="@dimen/page_margin"
        android:scrollbarStyle="outsideOverlay"
        android:choiceMode="multipleChoice" />
</LinearLayout>

註釋裡寫到:ListView的”android:choiceMode”有兩種值:multipleChoice 和singleChoice 意思很清楚,將ListView的選擇模式設定為多選或者單選模式,不管哪種模式,列表項被選中時都會改變樣式。

接下來是Activity,很簡單的實現,其中,類Cheese只是資料的提供類,就不寫出來了:

/**
 * This sample demonstrates how to create custom single- or multi-choice
 * {@link android.widget.ListView} UIs. The most interesting bits are in
 * the <code>res/layout/</code> directory of this sample.
 */
public class MainActivity extends ListActivity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.sample_main);
        setListAdapter(new MyAdapter());
    }

    /**
     * A simple array adapter that creates a list of cheeses.
     */
    private class MyAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            return Cheeses.CHEESES.length;
        }

        @Override
        public String getItem(int position) {
            return Cheeses.CHEESES[position];
        }

        @Override
        public long getItemId(int position) {
            return Cheeses.CHEESES[position].hashCode();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup container) {
            if (convertView == null) {
                convertView = getLayoutInflater().inflate(R.layout.list_item, container, false);
            }

            ((TextView) convertView.findViewById(android.R.id.text1))
                    .setText(getItem(position));
            return convertView;
        }
    }
}

到此為止,效果就實現了。