1. 程式人生 > >使用第三方WheelView製作日期選擇器

使用第三方WheelView製作日期選擇器

需求:實現一年月日選擇器,預設為當前日期,三者聯動,並且在切換年月時,根據是否閏年或者月份天數動態改變日。多說無益,請看下圖:

這裡寫圖片描述

activity_main.xml放置一個封裝好WheelView的自定義控制元件,沒有什麼自定義屬性

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
tools:context=".MainActivity"> <test.oubowu.com.datepicker.DatePicker android:id="@+id/birthday_picker" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_margin="5dp" android:gravity="center_horizontal"
/> </RelativeLayout>

date_picker.xml線性佈局放置三個WheelView,分別對應年月日:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height
="match_parent" android:background="@drawable/rectangle_bg" android:padding="2dp" android:gravity="center_vertical" android:orientation="horizontal" >
<test.oubowu.com.datepicker.WheelView android:id="@+id/year" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:itemNumber="5" app:lineColor="@android:color/darker_gray" app:lineHeight="2dp" app:maskHight="32dp" app:noEmpty="true" app:normalTextColor="@color/material_green_300" app:normalTextSize="14sp" app:selectedTextColor="@color/material_light_blue_a700" app:selectedTextSize="22sp" app:unitHight="50dp" /> <test.oubowu.com.datepicker.WheelView android:id="@+id/month" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:itemNumber="5" app:lineColor="@android:color/darker_gray" app:lineHeight="2dp" app:maskHight="32dp" app:noEmpty="true" app:normalTextColor="@color/material_light_blue_a100" app:normalTextSize="14sp" app:selectedTextColor="@color/material_red_300" app:selectedTextSize="22sp" app:unitHight="50dp" /> <test.oubowu.com.datepicker.WheelView android:id="@+id/day" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" app:itemNumber="5" app:lineColor="@android:color/darker_gray" app:lineHeight="2dp" app:maskHight="32dp" app:noEmpty="true" app:normalTextColor="@color/material_orange_500" app:normalTextSize="14sp" app:selectedTextColor="@color/material_deep_purple_300" app:selectedTextSize="22sp" app:unitHight="50dp" /> </LinearLayout>

自定義屬性如下,定製性相當強:

Attributes
There are several attributes you can set:

attr 屬性 description 描述
lineColor   divider line color 分割線顏色
lineHeight  divider line height 分割線高度
itemNumber  wheelview show item count 此wheelView顯示item的個數
maskHight   mask height 蒙版高度(normalText的位置)
noEmpty if set true select area can't be null(empty),or could be empty 設定true則選中不能為空,否則可以是空
normalTextColor unSelected Text color 未選中文字顏色
normalTextSize  unSelected Text size 未選中文字字型大小
selectedTextColor   selected Text color 選中文字顏色
selectedTextSize    selected Text size 選中文字字型大小
unitHight   item unit height 每個item單元的高度

主要看下DatePicker 程式碼是如何實現聯動的:

public class DatePicker extends LinearLayout implements WheelView.OnSelectListener {

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

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

    /**
     * 獲取選擇的年
     *
     * @return
     */
    public String getYear() {
        return mWheelYear.getSelectedText();
    }

    /**
     * 獲取選擇的月
     *
     * @return
     */
    public String getMonth() {
        return mWheelMonth.getSelectedText();
    }

    /**
     * 獲取選擇的日
     *
     * @return
     */
    public String getDay() {
        return mWheelDay.getSelectedText();
    }

    private WheelView mWheelYear;
    private WheelView mWheelMonth;
    private WheelView mWheelDay;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        LayoutInflater.from(getContext()).inflate(R.layout.date_picker, this);

        mWheelYear = (WheelView) findViewById(R.id.year);
        mWheelMonth = (WheelView) findViewById(R.id.month);
        mWheelDay = (WheelView) findViewById(R.id.day);

        // 格式化當前時間,並轉換為年月日整型資料
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
        String[] split = sdf.format(new Date()).split("-");
        int currentYear = Integer.parseInt(split[0]);
        int currentMonth = Integer.parseInt(split[1]);
        int currentDay = Integer.parseInt(split[2]);

        // 設定預設年月日為當前日期
        mWheelYear.setData(getYearData(currentYear));
        mWheelYear.setDefault(0);
        mWheelMonth.setData(getMonthData());
        mWheelMonth.setDefault(currentMonth - 1);
        mWheelDay.setData(getDayData(getLastDay(currentYear, currentMonth)));
        mWheelDay.setDefault(currentDay - 1);

        mWheelYear.setOnSelectListener(this);
        mWheelMonth.setOnSelectListener(this);
        mWheelDay.setOnSelectListener(this);

    }

    /**
     * 年範圍在:1900~今年
     *
     * @param currentYear
     * @return
     */
    private ArrayList<String> getYearData(int currentYear) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = currentYear; i >= 1900; i--) {
            list.add(String.valueOf(i));
        }
        return list;
    }

    private ArrayList<String> getMonthData() {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 1; i <= 12; i++) {
            list.add(String.valueOf(i));
        }
        return list;
    }

    /**
     * 日範圍在1~lastDay
     *
     * @param lastDay
     * @return
     */
    private ArrayList<String> getDayData(int lastDay) {
        //ignore condition
        ArrayList<String> list = new ArrayList<>();
        for (int i = 1; i <= lastDay; i++) {
            list.add(String.valueOf(i));
        }
        return list;
    }

    /**
     * 判斷是否閏年
     *
     * @param year
     * @return
     */
    private boolean isLeapYear(int year) {
        return (year % 100 == 0 && year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
    }

    /**
     * 獲取特定年月對應的天數
     *
     * @param year
     * @param month
     * @return
     */
    private int getLastDay(int year, int month) {
        if (month == 2) {
            // 2月閏年的話返回29,防止28
            return isLeapYear(year) ? 29 : 28;
        }
        // 一三五七八十臘,三十一天永不差
        return month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ? 31 : 30;
    }

    @Override
    public void endSelect(View view, int id, String text) {
        // 滾輪滑動停止後呼叫
        switch (view.getId()) {
            case R.id.year:
            case R.id.month:
                // 記錄當前選擇的天數
                int selectDay = Integer.parseInt(getDay());
                // 根據當前選擇的年月獲取對應的天數
                int lastDay = getLastDay(Integer.parseInt(getYear()), Integer.parseInt(getMonth()));
                // 設定天數
                mWheelDay.setData(getDayData(lastDay));
                // 如果選中的天數大於實際天數,那麼將預設天數設為實際天數;否則還是設定預設天數為選中的天數
                if (selectDay > lastDay) {
                    mWheelDay.setDefault(lastDay - 1);
                } else {
                    mWheelDay.setDefault(selectDay - 1);
                }
                break;
        }
    }

    @Override
    public void selecting(View view, int id, String text) {

    }


}

DatePicker繼承於線性佈局,在onFinishInflate填充date_picker.xml佈局,然後拿到三個wheelview,格式化時間,取出年月日。

WheelView的方法:

Method
1. setData(ArrayList data)

set WheelView data
設定WheelView的資料

2. resetData(ArrayList data)

reset WheelView data ,if you has setData
重置 WheelView的資料,如果已經設定過的話

3. int getSelected()

get selected item index
獲取選中項的index

4. String getSelectedText()

get selected item text
獲取選中項的文字資訊

5. boolean isScrolling

is WheelView is scrolling
獲取WheelView是否在滾動中

6. boolean isEnable()

is WheelView is enable
獲取wheelView是否可用

7. void setEnable(boolean isEnable)

set WheelView enable
設定WheelView是否可用

8. void setDefault(int index)

set default selected index
設定預設選中項的index

9. int getListSize()

get WheelView item count
獲取WheelView的item項個數

10. String getItemText(int index)

get the text by index 
獲取index位置上的文字資料

11. void setOnSelectListener(OnSelectListener onSelectListener)

set listener on WheelView that can get info when WheelView is scrolling or stop scroll.
對WheelView設定監聽,在 滑動過程 或者 滑動停止 返回資料資訊。

年的話是以當前年作為最開始選中的,因為我實際用來做生日選擇器的;月就是12個月沒什麼好說的。

mWheelYear.setData(getYearData(currentYear));
private ArrayList<String> getYearData(int currentYear) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = currentYear; i >= 1900; i--) {
            list.add(String.valueOf(i));
        }
        return list;
    }

日的話稍微複雜點,因為月份不同日數不同,閏年二月天數多一天,所以做多判斷。
getLastDay(currentYear, currentMonth)就是根據年月算出日數

mWheelDay.setData(getDayData(getLastDay(currentYear, currentMonth)));

private ArrayList<String> getDayData(int lastDay) {
        //ignore condition
        ArrayList<String> list = new ArrayList<>();
        for (int i = 1; i <= lastDay; i++) {
            list.add(String.valueOf(i));
        }
        return list;
    }

    private int getLastDay(int year, int month) {
        if (month == 2) {
            // 2月閏年的話返回29,防止28
            return isLeapYear(year) ? 29 : 28;
        }
        // 一三五七八十臘,三十一天永不差
        return month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ? 31 : 30;
    }

private boolean isLeapYear(int year) {
        return (year % 100 == 0 && year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
    }

我們要聯動的話就需要監聽三個WheelView的滾動停止對應的資料了,WheelView提供setOnSelectListener給我們監聽,這裡只需在停止的時候拿到資料做聯動即可。
為了直觀一點,我們選中的天數會根據年月的不同計算出的實際天數做對比,若選中的天數大於實際天數,會回退到實際天數。

@Override
    public void endSelect(View view, int id, String text) {
        // 滾輪滑動停止後呼叫
        switch (view.getId()) {
            case R.id.year:
            case R.id.month:
                // 記錄當前選擇的天數
                int selectDay = Integer.parseInt(getDay());
                // 根據當前選擇的年月獲取對應的天數
                int lastDay = getLastDay(Integer.parseInt(getYear()), Integer.parseInt(getMonth()));
                // 設定天數
                mWheelDay.setData(getDayData(lastDay));
                // 如果選中的天數大於實際天數,那麼將預設天數設為實際天數;否則還是設定預設天數為選中的天數
                if (selectDay > lastDay) {
                    mWheelDay.setDefault(lastDay - 1);
                } else {
                    mWheelDay.setDefault(selectDay - 1);
                }
                break;
        }
    }