1. 程式人生 > >時間日期選擇控制元件DateTimePicker

時間日期選擇控制元件DateTimePicker

      專案中需要使用一個日期時間選擇彈窗。因為系統的日期選擇和時間選擇是分開的,而且顯示效果與要求不一致,因此自定義時間日期彈窗。


思路:這種時間日期選擇控制元件,與系統的有些差別,因此,需要重新設計。不論是年月日,還是時分秒

的單個選擇的控制元件,都是一個數字滾輪的模式。因此基於系統的NumberPicker元件的基礎上進行開發。

1、NumberPicker。系統的數字選擇器沒有開放設定分割線顏色,選擇文字的顏色,文字大小等屬性的

定製,因此採用一個繼承類開放這些屬性設定。

    public JDNumberPicker(Context context) {
        super(context, null);
    }

    public JDNumberPicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        //檢視初始化
        initView(context, attrs);
    }

    public JDNumberPicker(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        //檢視初始化
        initView(context, attrs);
    }

    /**
     * 檢視初始化
     */
    private void initView(Context context, AttributeSet attrs) {
        if(null != attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.JDNumberPicker, 0, 0);
            try{
                if (a.hasValue(R.styleable.JDNumberPicker_jdSelectionDividerHeight)) {
                    mSelectionDividerHeight = a.getDimensionPixelSize(R.styleable.JDNumberPicker_jdSelectionDividerHeight, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT);
                    setDividerHeight(mSelectionDividerHeight);
                }
                if (a.hasValue(R.styleable.JDNumberPicker_jdSelectionDividersDistance)) {
                    mSelectionDividersDistance = a.getDimensionPixelSize(R.styleable.JDNumberPicker_jdSelectionDividersDistance, UNSCALED_DEFAULT_SELECTION_DIVIDERS_DISTANCE);
                    setDividersDistance(mSelectionDividersDistance);
                }
                if (a.hasValue(R.styleable.JDNumberPicker_jdSelectDividersColor)) {
                    mSelectionDividersColor = a.getColor(R.styleable.JDNumberPicker_jdSelectDividersColor, Color.GRAY);
                    setDividerColor(mSelectionDividersColor);
                }
                if (a.hasValue(R.styleable.JDNumberPicker_jdSelectTextColor)) {
                    mSelectionTextColor = a.getColor(R.styleable.JDNumberPicker_jdSelectTextColor, Color.WHITE);
                    setSelectionTextColor(mSelectionTextColor);
                }
                if (a.hasValue(R.styleable.JDNumberPicker_jdSelectTextSize)) {
                    mSelectionTextSize = a.getDimensionPixelSize(R.styleable.JDNumberPicker_jdSelectTextSize, UNSCALED_DEFAULT_SELECTION_TEXTSIZE);
                    setSelectionTextSize(mSelectionTextSize);
                }
            } finally {
                a.recycle();
            }
        }
    }

 屬性的設定方法,通過反射來實現。

    /**
     * 設定分割線顏色
     *
     * @param color : 顏色值
     */
    public void setDividerColor(int color) {
        try {
            Field pf = NumberPicker.class.getDeclaredField("mSelectionDivider");
            if(null != pf) {
                pf.setAccessible(true);
                try {
                    //設定分割線的顏色值
pf.set(this, new ColorDrawable(color)); } catch (Exception e) { e.printStackTrace(); } } } catch (NoSuchFieldException e) { e.printStackTrace(); } }

同理,可以設定分割線的高度,分割線之間的間距等。

2)日期選擇元件。

日期選擇元件可以通過三個數字滾輪選擇元件實現,即三個JDNumberPicker。

/**
 * 日期選擇器
 */
public class JDDatePicker extends LinearLayout {
    private static final String LOG_TAG = JDDatePicker.class.getSimpleName();
    /**
     * The callback used to indicate the user changes\d the date.
     */
    public interface OnDateChangedListener {

        /**
         * Called upon a date change.
         *
         * @param view The view associated with this listener.
         * @param year The year that was set.
         * @param monthOfYear The month that was set (0-11) for compatibility
         *            with {@link java.util.Calendar}.
         * @param dayOfMonth The day of the month that was set.
         */
        void onDateChanged(JDDatePicker view, int year, int monthOfYear, int dayOfMonth);
    }

    private static final int DEFAULT_START_YEAR = 1900;

    private static final int DEFAULT_END_YEAR = 2100;

    /**
     * 日期樣式
     */
    private static final String DATE_FORMAT = "MM/dd/yyyy";
    /**
     * 上下文
     */
    private Context mContext;
    private View root;
    private String[] mShortMonths;

    private java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);

    private int mNumberOfMonths;

    private Calendar mTempDate;

    private Calendar mMinDate;

    private Calendar mMaxDate;

    private Calendar mCurrentDate;
    /**
     * 日期變化監聽
     */
    private OnDateChangedListener mOnDateChangedListener;

    /**
     * 年份選擇器
     */
    private JDNumberPicker mYearSpinner;
    /**
     * 月份選擇器
     */
    private JDNumberPicker mMonthSpinner;
    /**
     * 日選擇器
     */
    private JDNumberPicker mDaySpinner;
    /**
     * 本地時區
     */
    private Locale mCurrentLocale;


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

    public JDDatePicker(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        //檢視初始化
        initView(context, attrs);
    }

    /**
     * 控制元件初始化
     *
     * @param context
     * @param attrs
     */
    private void initView(Context context, AttributeSet attrs) {
        // initialization based on locale
        setCurrentLocale(Locale.getDefault());

        //讀取屬性
        int startYear = DEFAULT_START_YEAR;
        int endYear = DEFAULT_END_YEAR;
        String minDate = "";
        String maxDate = "";
        if(null != attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.JDDatePicker, 0, 0);
            try{
                if(a.hasValue(R.styleable.JDDatePicker_jdStartYear)) {
                    startYear = a.getInt(R.styleable.JDDatePicker_jdStartYear,
                            DEFAULT_START_YEAR);
                }
                if(a.hasValue(R.styleable.JDDatePicker_jdEndYear)) {
                    endYear = a.getInt(R.styleable.JDDatePicker_jdEndYear,
                            DEFAULT_END_YEAR);
                }
                if(a.hasValue(R.styleable.JDDatePicker_jdMinDate)) {
                    minDate = a.getString(R.styleable.JDDatePicker_jdMinDate);
                }
                if(a.hasValue(R.styleable.JDDatePicker_jdMaxDate)) {
                    maxDate = a.getString(R.styleable.JDDatePicker_jdMaxDate);
                }
            } finally {
                a.recycle();
            }
        }

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.layout_date_picker, this,true);
        mYearSpinner = (JDNumberPicker) findViewById(R.id.yearPicker);
        mMonthSpinner = (JDNumberPicker) findViewById(R.id.monthPicker);
        mDaySpinner = (JDNumberPicker) findViewById(R.id.dayPicker);
        mYearSpinner.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        mMonthSpinner.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        mDaySpinner.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
        JDNumberPicker.OnValueChangeListener onChangeListener = new NumberPicker.OnValueChangeListener() {
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
                mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
                // take care of wrapping of days and months to update greater fields
                if (picker == mDaySpinner) {
                    int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
                    if (oldVal == maxDayOfMonth && newVal == 1) {
                        mTempDate.add(Calendar.DAY_OF_MONTH, 1);
                    } else if (oldVal == 1 && newVal == maxDayOfMonth) {
                        mTempDate.add(Calendar.DAY_OF_MONTH, -1);
                    } else {
                        mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
                    }
                } else if (picker == mMonthSpinner) {
                    if (oldVal == 11 && newVal == 0) {
                        mTempDate.add(Calendar.MONTH, 1);
                    } else if (oldVal == 0 && newVal == 11) {
                        mTempDate.add(Calendar.MONTH, -1);
                    } else {
                        mTempDate.add(Calendar.MONTH, newVal - oldVal);
                    }
                } else if (picker == mYearSpinner) {
                    mTempDate.set(Calendar.YEAR, newVal);
                } else {
                    throw new IllegalArgumentException();
                }
                // now set the date to the adjusted one
                setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
                        mTempDate.get(Calendar.DAY_OF_MONTH));
                updateSpinners();
                notifyDateChanged();
            }
        };
        mDaySpinner.setFormatter(JDNumberPicker.getTwoDigitFormatter(mDaySpinner));
        mDaySpinner.setOnLongPressUpdateInterval(100);
        mDaySpinner.setOnValueChangedListener(onChangeListener);
        // month
        mMonthSpinner.setMinValue(0);
        mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
        mMonthSpinner.setDisplayedValues(mShortMonths);
        mMonthSpinner.setOnLongPressUpdateInterval(200);
        mMonthSpinner.setOnValueChangedListener(onChangeListener);
        // year
        mYearSpinner.setMinValue(startYear);
        mYearSpinner.setMaxValue(endYear);
        mYearSpinner.setOnLongPressUpdateInterval(100);
        mYearSpinner.setOnValueChangedListener(onChangeListener);

        // set the min date giving priority of the minDate over startYear
        mTempDate.clear();
        if (!TextUtils.isEmpty(minDate)) {
            if (!parseDate(minDate, mTempDate)) {
                mTempDate.set(startYear, 0, 1);
            }
        } else {
            mTempDate.set(startYear, 0, 1);
        }
        setMinDate(mTempDate.getTimeInMillis());

        // set the max date giving priority of the maxDate over endYear
        mTempDate.clear();
        if (!TextUtils.isEmpty(maxDate)) {
            if (!parseDate(maxDate, mTempDate)) {
                mTempDate.set(endYear, 11, 31);
            }
        } else {
            mTempDate.set(endYear, 11, 31);
        }
        setMaxDate(mTempDate.getTimeInMillis());

        // initialize to current date
        mCurrentDate.setTimeInMillis(System.currentTimeMillis());
        init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
                .get(Calendar.DAY_OF_MONTH), null);

        // If not explicitly specified this view is important for accessibility.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
                setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
            }
        }
    }

    @Override
    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
        onPopulateAccessibilityEvent(event);
        return true;
    }

    @Override
    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
        super.onPopulateAccessibilityEvent(event);

        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
        String selectedDateUtterance = DateUtils.formatDateTime(mContext,
                mCurrentDate.getTimeInMillis(), flags);
        event.getText().add(selectedDateUtterance);
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(DatePicker.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            info.setClassName(JDDatePicker.class.getName());
        }
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setCurrentLocale(newConfig.locale);
    }

    // Override so we are in complete control of save / restore for this widget.
    @Override
    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        dispatchThawSelfOnly(container);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        return new SavedState(superState, getYear(), getMonth(), getDayOfMonth());
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        setDate(ss.mYear, ss.mMonth, ss.mDay);
        updateSpinners();
    }

    /**
     * Class for managing state storing/restoring.
     */
    private static class SavedState extends BaseSavedState {

        private final int mYear;

        private final int mMonth;

        private final int mDay;

        /**
         * Constructor called from {@link DatePicker#onSaveInstanceState()}
         */
        private SavedState(Parcelable superState, int year, int month, int day) {
            super(superState);
            mYear = year;
            mMonth = month;
            mDay = day;
        }

        /**
         * Constructor called from {@link #CREATOR}
         */
        private SavedState(Parcel in) {
            super(in);
            mYear = in.readInt();
            mMonth = in.readInt();
            mDay = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(mYear);
            dest.writeInt(mMonth);
            dest.writeInt(mDay);
        }

        @SuppressWarnings("all")
        // suppress unused and hiding
        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {

            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    /**
     * 設定選中項Text顏色
     *
     * @param color : 顏色值
     */
    public void setSelectionTextColor(int color) {
        mYearSpinner.setSelectionTextColor(color);
        mMonthSpinner.setSelectionTextColor(color);
        mDaySpinner.setSelectionTextColor(color);
    }

    /**
     * 設定選中項分割線高度
     *
     * @param height : 選中項分割線高度
     */
    public void setDividerHeight(int height) {
        mYearSpinner.setDividerHeight(height);
        mMonthSpinner.setDividerHeight(height);
        mDaySpinner.setDividerHeight(height);
    }

    /**
     * 設定分割線顏色
     *
     * @param color : 顏色值
     */
    public void setDividerColor(int color) {
        mYearSpinner.setDividerColor(color);
        mMonthSpinner.setDividerColor(color);
        mDaySpinner.setDividerColor(color);
    }

    /**
     * 設定選項文字大小
     *
     * @param textSize : 文字大小
     */
    public void setSelectionTextSize(int textSize) {
        mYearSpinner.setSelectionTextSize(textSize);
        mMonthSpinner.setSelectionTextSize(textSize);
        mDaySpinner.setSelectionTextSize(textSize);
    }

    /**
     * 設定選中項分割線之間距離
     *
     * @param distance : 選中項分割線之間距離
     */
    public void setDividersDistance(int distance) {
        mYearSpinner.setDividersDistance(distance);
        mMonthSpinner.setDividersDistance(distance);
        mDaySpinner.setDividersDistance(distance);
    }

    /**
     * Updates the current date.
     *
     * @param year The year.
     * @param month The month which is <strong>starting from zero</strong>.
     * @param dayOfMonth The day of the month.
     */
    public void updateDate(int year, int month, int dayOfMonth) {
        if (!isNewDate(year, month, dayOfMonth)) {
            return;
        }
        setDate(year, month, dayOfMonth);
        updateSpinners();
        notifyDateChanged();
    }

    /**
     * Initialize the state. If the provided values designate an inconsistent
     * date the values are normalized before updating the spinners.
     *
     * @param year The initial year.
     * @param monthOfYear The initial month <strong>starting from zero</strong>.
     * @param dayOfMonth The initial day of the month.
     * @param onDateChangedListener How user is notified date is changed by
     *            user, can be null.
     */
    public void init(int year, int monthOfYear, int dayOfMonth,
                     OnDateChangedListener onDateChangedListener) {
        setDate(year, monthOfYear, dayOfMonth);
        updateSpinners();
        mOnDateChangedListener = onDateChangedListener;
    }

    /**
     * 獲取當前的日期
     *
     * @return
     */
    public Calendar getSelectionDate() {
        return mCurrentDate;
    }

    /**
     * 設定日期選擇監聽
     *
     * @param onDateChangedListener How user is notified date is changed by
     *            user, can be null.
     */
    public void setOnDateChangedListener(OnDateChangedListener onDateChangedListener) {
        this.mOnDateChangedListener = onDateChangedListener;
    }

    private void updateSpinners() {
        // set the spinner ranges respecting the min and max dates
        if (mCurrentDate.equals(mMinDate)) {
            mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            mDaySpinner.setWrapSelectorWheel(false);
            mMonthSpinner.setDisplayedValues(null);
            mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
            mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
            mMonthSpinner.setWrapSelectorWheel(false);
        } else if (mCurrentDate.equals(mMaxDate)) {
            mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
            mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
            mDaySpinner.setWrapSelectorWheel(false);
            mMonthSpinner.setDisplayedValues(null);
            mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
            mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
            mMonthSpinner.setWrapSelectorWheel(false);
        } else {
            mDaySpinner.setMinValue(1);
            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
            mDaySpinner.setWrapSelectorWheel(true);
            mMonthSpinner.setDisplayedValues(null);
            mMonthSpinner.setMinValue(0);
            mMonthSpinner.setMaxValue(11);
            mMonthSpinner.setWrapSelectorWheel(true);
        }

        // make sure the month names are a zero based array
        // with the months in the month spinner
        String[] displayedValues = Arrays.copyOfRange(mShortMonths,
                mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
        mMonthSpinner.setDisplayedValues(displayedValues);

        // year spinner range does not change based on the current date
        mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
        mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
        mYearSpinner.setWrapSelectorWheel(false);

        // set the spinner values
        mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
        mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
        mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
    }

    /**
     * @return The selected year.
     */
    public int getYear() {
        return mCurrentDate.get(Calendar.YEAR);
    }

    /**
     * @return The selected month.
     */
    public int getMonth() {
        return mCurrentDate.get(Calendar.MONTH);
    }

    /**
     * @return The selected day of month.
     */
    public int getDayOfMonth() {
        return mCurrentDate.get(Calendar.DAY_OF_MONTH);
    }

    /**
     * Notifies the listener, if such, for a change in the selected date.
     */
    private void notifyDateChanged() {
        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
        if (mOnDateChangedListener != null) {
            mOnDateChangedListener.onDateChanged(this, getYear(), getMonth(), getDayOfMonth());
        }
    }

    /**
     * Sets the maximal date supported by this {@link DatePicker} in
     * milliseconds since January 1, 1970 00:00:00 in
     * {@link TimeZone#getDefault()} time zone.
     *
     * @param maxDate The maximal supported date.
     */
    public void setMaxDate(long maxDate) {
        mTempDate.setTimeInMillis(maxDate);
        if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
            return;
        }
        mMaxDate.setTimeInMillis(maxDate);
        if (mCurrentDate.after(mMaxDate)) {
            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
        }
        updateSpinners();
    }

    /**
     * Sets the minimal date supported by this {@link NumberPicker} in
     * milliseconds since January 1, 1970 00:00:00 in
     * {@link TimeZone#getDefault()} time zone.
     *
     * @param minDate The minimal supported date.
     */
    public void setMinDate(long minDate) {
        mTempDate.setTimeInMillis(minDate);
        if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
            return;
        }
        mMinDate.setTimeInMillis(minDate);
        if (mCurrentDate.before(mMinDate)) {
            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
        }
        updateSpinners();
    }

    /**
     * 設定日期(年月日)
     *
     * @param year : 年
     * @param month : 月
     * @param dayOfMonth : 月天數
     */
    private void setDate(int year, int month, int dayOfMonth) {
        mCurrentDate.set(year, month, dayOfMonth);
        if (mCurrentDate.before(mMinDate)) {
            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
        } else if (mCurrentDate.after(mMaxDate)) {
            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
        }
    }

    /**
     * Parses the given <code>date</code> and in case of success sets the result
     * to the <code>outDate</code>.
     *
     * @return True if the date was parsed.
     */
    private boolean parseDate(String date, Calendar outDate) {
        try {
            outDate.setTime(mDateFormat.parse(date));
            return true;
        } catch (ParseException e) {
            Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
            return false;
        }
    }

    /**
     * 是否是一個新的日期
     *
     * @param year : 年
     * @param month : 月
     * @param dayOfMonth : 月天數
     * @return
     */
    private boolean isNewDate(int year, int month, int dayOfMonth) {
        return (mCurrentDate.get(Calendar.YEAR) != year
                || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
                || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month);
    }

    /**
     * Sets the current locale.
     *
     * @param locale The current locale.
     */
    private void setCurrentLocale(Locale locale) {
        if (locale.equals(mCurrentLocale)) {
            return;
        }

        mCurrentLocale = locale;

        mTempDate = getCalendarForLocale(mTempDate, locale);
        mMinDate = getCalendarForLocale(mMinDate, locale);
        mMaxDate = getCalendarForLocale(mMaxDate, locale);
        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);

        mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
        mShortMonths = new DateFormatSymbols().getShortMonths();

        if (usingNumericMonths()) {
            // We're in a locale where a date should either be all-numeric, or all-text.
            // All-text would require custom NumberPicker formatters for day and year.
            mShortMonths = new String[mNumberOfMonths];
            for (int i = 0; i < mNumberOfMonths; ++i) {
                mShortMonths[i] = String.format("%d", i + 1);
            }
        }
    }

    /**
     * Tests whether the current locale is one where there are no real month names,
     * such as Chinese, Japanese, or Korean locales.
     */
    private boolean usingNumericMonths() {
        return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0));
    }

    /**
     * Gets a calendar for locale bootstrapped with the value of a given calendar.
     *
     * @param oldCalendar The old calendar.
     * @param locale The locale.
     */
    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
        if (oldCalendar == null) {
            return Calendar.getInstance(locale);
        } else {
            final long currentTimeMillis = oldCalendar.getTimeInMillis();
            Calendar newCalendar = Calendar.getInstance(locale);
            newCalendar.setTimeInMillis(currentTimeMillis);
            return newCalendar;
        }
    }


}

其中,最重要的是根據年份和月份的變化,日期選擇器要想贏得調整最大值,例如閏年2月份有29天,大月份(例如1月,3月等)有31天。這個聯動變化在控制元件的初始化JDNumberPicker.OnValueChangeListener onChangeListener = new NumberPicker.OnValueChangeListener()進行實現的。

init:初始化方法設定初始化的日期和選擇監聽。

updateDate: 更新日期

3)時間選擇器,同樣是三個JDNumberPicker組成。採用24小時的模式計時。

4)時間日期選擇器。通過AlertDialog,顯示時間日期選擇。參考JDDateTimePickerDialogUtil。

原專案程式碼: