Android原生时间控件DatePicker——月份由英文转数字

网上有很多优秀的开源时间控件,可以满足我们大部分的需求。

但有时候还是会碰到系统自带的DatePicker,这个控件默认的月份显示为英文,如JANUARY、FEBRUARY等。

如何将月份由英文改成数字,这是个好玩的问题。

庖丁解牛

DatePicker并没有提供相关api可以实现我们的需求,所以需要我们自己来分析。

查看DatePicke的源码,它的布局文件是date_picker.xml,如下所示:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_gravity="center_horizontal"
    android:orientation="horizontal"
    android:gravity="center">

    <LinearLayout android:id="@+id/pickers"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:orientation="horizontal"
        android:gravity="center">

        <!-- Month -->
        <NumberPicker
            android:id="@+id/month"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="1dip"
            android:layout_marginEnd="1dip"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

        <!-- Day -->
        <NumberPicker
            android:id="@+id/day"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="1dip"
            android:layout_marginEnd="1dip"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

        <!-- Year -->
        <NumberPicker
            android:id="@+id/year"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="1dip"
            android:layout_marginEnd="1dip"
            android:focusable="true"
            android:focusableInTouchMode="true"
            />

    </LinearLayout>

    <!-- calendar view -->
    <CalendarView
        android:id="@+id/calendar_view"
        android:layout_width="245dip"
        android:layout_height="280dip"
        android:layout_marginStart="44dip"
        android:layout_weight="1"
        android:focusable="true"
        android:focusableInTouchMode="true"
        android:visibility="gone"
        />

</LinearLayout>

可以很轻松地发现,月份就是一个id为month的NumberPicker控件。

DatePicker中以month控件为线索,很快发现NumberPicker下的这个方法:

    /**
     * Sets the values to be displayed.
     *
     * @param displayedValues The displayed values.
     */
    public void setDisplayedValues(String[] displayedValues) {
        ...
        ...
        ...
    }

该方法参数为一个String数组,该数组每一项就是展示的月份数据。

所以如果我们自己调用该方法,传入以数字表示的月份,就达到目的啦!

寻找调用时机

DatePicker源码中,直接调用setDisplayedValues方法的地方有两处,

一处是在构造函数中:

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

        ......

        OnValueChangeListener onChangeListener = new OnValueChangeListener() {
            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
               ......
                updateSpinners();
                updateCalendarView();
                notifyDateChanged();
            }
        };

       ......

        // month
        mMonthSpinner = (NumberPicker) findViewById(R.id.month);
        mMonthSpinner.setMinValue(0);
        mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
        mMonthSpinner.setDisplayedValues(mShortMonths);
        mMonthSpinner.setOnLongPressUpdateInterval(200);
        mMonthSpinner.setOnValueChangedListener(onChangeListener);
        mMonthSpinnerInput =(EditText)mMonthSpinner.findViewById(R.id.numberpicker_input);

        ......
}

mShortMonths就是默认的英文月份,就不展开介绍了。

构造函数中在第X行调用了setDisplayedValues方法,完成了展示月份数据的设置。

同时mMonthSpinner的值监听器为onChangeListener,当月份变化时会回调onValueChange方法,该方法中又会调用updateSpinners方法,这是另一处直接调用setDisplayedValues方法的地方:

private void updateSpinners() {
      ......

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

      ......
    }

updateSpinners方法除了会在上述的监听器中被调用,还要其他方法中也会调用,例如:

    /**
     * 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();
        updateCalendarView();
        mOnDateChangedListener = onDateChangedListener;
    }

        /**
     * 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);
        mCalendarView.setMaxDate(maxDate);
        if (mCurrentDate.after(mMaxDate)) {
            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
            updateCalendarView();
        }
        updateSpinners();
    }

以上所有直接或间接调用setDisplayedValues方法的地方都会将月份重置为英文显示,所以我们自己调用setDisplayedValues方法的时机就是在这些方法被调用之后。

也就是说,我们的调用时机在DatePicker构造函数之后、init()setMaxDate()等方法之后和onDateChanged()方法之中。

轻松实现

知道了在何时调用何方法后,我们来具体实现这一需求。

首先准备好展示的月份数组:

String months[] = {"1月", "2月", "3月", "4月", "5月", "6月",
            "7月", "8月", "9月", "10月", "11月", "12月"};

假设mDatePicker就是目标DatePicker控件,因为DatePicker没有提供可以获取到月份控件的api,所以需要自己想办法获取到月份控件,再调用它的setDisplayedValues方法。

分析之前的date_picker.xml文件,可以写出下面这行代码:

((NumberPicker) ((ViewGroup) ((ViewGroup) mDatePicker.getChildAt(0)).getChildAt(0)).getChildAt(0)).setDisplayedValues(months);

通过调用这行代码,就实现了替换掉英文月份的目标!

猜你喜欢

转载自blog.csdn.net/recordGrowth/article/details/77650790