一步一步自定義spinner

spinner就是下拉選擇組件,系統自帶的spinner使用起來非常方便,首先定義一個array(strings.xml),如下:
<array name="grade">
    <item>一年級</item>
    <item>二年級</item>
    <item>三年級</item>
    <item>四年級</item>
    <item>五年級</item>
    <item>六年級</item>
</array>
代碼如下:
Spinner spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.grade, android.R.layout.simple_spinner_item);
spinner.setAdapter(adapter);
這樣就實現了一個簡單的spinner,顯示如下:

但這並不是我想要的樣式和效果,下面我們就一點點的來改造它。

(1)改變初始佈局
即彈窗前的樣式,先自定義一個佈局,如下:
spinner_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:textColor="#6d6d6d"
    android:textSize="15sp"
    android:drawableRight="@drawable/arrow"
    android:drawablePadding="5dp"
    tools:text="一年級">

</TextView>
然後替換createFromResource中的即可,如下:
ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.grade, R.layout.spinner_layout);
這樣還不夠,因爲還有一個帶箭頭的背景,將背景設置爲透明即可,如下:
spinner.setBackgroundColor(0x0);
這樣初始佈局的展示就與spinner_layout一樣了。

(2)改變列表item佈局
經過上面的修改後,發現彈窗中列表item的佈局也變成了spinner_layout,查看ArrayAdapter的構造函數可知有mResource和mDropDownResource兩個變量,其中mResource就是初始佈局,而mDropDownResource則是列表item的佈局。
而createFromResource函數中,mResource和mDropDownResource賦值相同。但是ArrayAdapter還有一個setDropDownViewResource函數。
首先定義一個佈局,如下:
spinner_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:tools="http://schemas.android.com/tools"
    android:textColor="#6d6d6d"
    android:textSize="15sp"
    android:padding="8dp"
    android:gravity="center_horizontal"
    tools:text="一年級">

</TextView>
然後使用setDropDownViewResource函數即可,如下:
adapter.setDropDownViewResource(R.layout.spinner_item);

(3)改變彈窗背景及位置
在開始的動畫中可以看到彈窗會遮擋住,我們想讓彈窗處於下方,同時彈窗是圓角帶箭頭的。
這就需要使用spinner的兩個函數setPopupBackgroundResource和setDropDownVerticalOffset。
但是注意這兩個函數都需要在android4.1版本及以上,鑑於目前4.1以下版本已經很少了,所以我們只考慮4.1以上即可,代碼如下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    spinner.setPopupBackgroundResource(R.drawable.bg_spinner);
    spinner.setDropDownVerticalOffset(dip2px(20));
}

(4)添加選中效果
經過上面的處理,我們已經得到想要的樣式。但是還差一點,彈窗列表中缺少選中的樣式。比如說我當前選擇“二年級”,在彈窗中,對應的item字體應該加深加粗。在spinner源碼中搜尋了一遍,發現並沒有對應的函數和解決方法,那麼我們自己動手吧。
其實spinner是使用adapter來加載列表的,而我們使用createFromResource函數會自動創建了adapter,我們可以自定義一個adapter,如下:
public class SpinnerAdapter<T> extends ArrayAdapter<T> {

    private int selectedPostion;

    public void setSelectedPostion(int selectedPostion) {
        this.selectedPostion = selectedPostion;
    }

    public SpinnerAdapter(@NonNull Context context, int resource, @NonNull T[] objects) {
        super(context, resource, objects);
    }

    @Override
    public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View view = super.getDropDownView(position, convertView, parent);
        TextView textView = (TextView)view;
        if(selectedPostion == position){
            textView.setTextColor(0xff373741);
            textView.getPaint().setFakeBoldText(true);
        }
        else{
            textView.setTextColor(0xff6d6d6d);
            textView.getPaint().setFakeBoldText(false);
        }
        return view;
    }

    public static @NonNull SpinnerAdapter<CharSequence> createFromResource(@NonNull Context context,
                                                                      @ArrayRes int textArrayResId, @LayoutRes int textViewResId) {
        final CharSequence[] strings = context.getResources().getTextArray(textArrayResId);
        return new SpinnerAdapter<>(context, textViewResId, strings);
    }
}
注意ArrayAdapter中的getDropDownView函數是獲取彈窗item使用的view的,而不是getView函數。
同時我們要重新寫一個createFromResource函數。
將之前使用的adapter替換成自定義這個,同時爲spinner設置監聽即可,更改後的完整代碼如下:
Spinner spinner = (Spinner) findViewById(R.id.spinner);
adapter = SpinnerAdapter.createFromResource(this, R.array.grade, R.layout.spinner_layout);
adapter.setDropDownViewResource(R.layout.spinner_item);
spinner.setBackgroundColor(0x0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
    spinner.setPopupBackgroundResource(R.drawable.bg_spinner);
    spinner.setDropDownVerticalOffset(dip2px(20));
}
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        adapter.setSelectedPostion(position);
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {

    }
});
效果如下:

通過我們自己定義adapter可以實現更多複雜的效果和樣式,有時間我們再來探討。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章