1. 程式人生 > >更改DatePicker中年月日的間隔和分隔線顏色

更改DatePicker中年月日的間隔和分隔線顏色

  Android提供了一個選擇日期的控制元件——DatePicker,但是這個控制元件本身存在一些缺陷:寬度不可控,年月日中間的間距過寬,分隔線的顏色不可以定義等等,於是網上就有了很多開源的日期選擇控制元件,它們之中有很多寫得非常棒。本文將提供一種方法,更改原生控制元件DatePicker中年月日的間距,以及分隔線的顏色。
  首先我們還是先看一下原生控制元件,如果在xml佈局檔案中擺放一個DatePicker,它的寬度最好設為wrap_content而非固定值。如果我將它的寬度寫死,並且小於它本來的寬度時,控制元件將會被截斷,而不是縮短年月日之間的間距,效果如下圖。
截斷

  如果要修改控制元件的屬性,我們最好先看看原始碼,瞭解一下這個控制元件是如何實現的。進入DatePicker這個類,可以看到下面一段程式碼

            mSpinners = (LinearLayout) mDelegator.findViewById(R.id.pickers);

            // calendar view day-picker
            mCalendarView = (CalendarView) mDelegator.findViewById(R.id.calendar_view);
            mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
                public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
                    setDate(year, month, monthDay);
updateSpinners(); notifyDateChanged(); } }); // day mDaySpinner = (NumberPicker) mDelegator.findViewById(R.id.day); mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter()); mDaySpinner.setOnLongPressUpdateInterval
(100); mDaySpinner.setOnValueChangedListener(onChangeListener); mDaySpinnerInput = (EditText) mDaySpinner.findViewById(R.id.numberpicker_input); // month mMonthSpinner = (NumberPicker) mDelegator.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); // year mYearSpinner = (NumberPicker) mDelegator.findViewById(R.id.year); mYearSpinner.setOnLongPressUpdateInterval(100); mYearSpinner.setOnValueChangedListener(onChangeListener); mYearSpinnerInput = (EditText) mYearSpinner.findViewById(R.id.numberpicker_input);

  從上面這段程式碼可以分析出,DatePicker中選擇年月日的部分其實是三個NumberPicker,名字分別為mYearSpinner,mMonthSpinner和mDaySpinner,它們三個平鋪在一個LinearLayout中,這個線性佈局的名字叫做mSpinners。從這段程式碼向上翻,還能找到一個佈局檔案R.layout.date_picker_legacy,追蹤進去,可以看到date_picker_legacy.xml,這個就是DatePicker的佈局。可以看到,基本符合剛才的分析,這裡把佈局檔案的程式碼附上。

<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>

  在佈局檔案裡我們可以看到,NumberPicker中的間距時通過marginStart和marginEnd定義的,因此我們修改這兩個值就可以更改間距,不過要注意的是,marginStart和marginEnd是在API17中登場的,對於之前的版本,需要加以判斷,否則會出現NoSuchMethodError。

  接下來再看分隔線顏色,由於分隔線屬於NumberPicker中的一部分,因此我們應當到NumberPicker的原始碼中去尋找。在NumberPicker.java中有一個Drawable型別的私有成員變數叫做mSelectionDivider,這個變數對應的就是分隔線,並且在NumberPicker的onDraw()方法中,將這個Drawable畫到了canvas上面。將原始碼中的這一部分搬運過來。

        // draw the selection dividers
        if (showSelectorWheel && mSelectionDivider != null) {
            // draw the top divider
            int topOfTopDivider = mTopSelectionDividerTop;
            int bottomOfTopDivider = topOfTopDivider + mSelectionDividerHeight;
            mSelectionDivider.setBounds(0, topOfTopDivider, mRight, bottomOfTopDivider);
            mSelectionDivider.draw(canvas);

            // draw the bottom divider
            int bottomOfBottomDivider = mBottomSelectionDividerBottom;
            int topOfBottomDivider = bottomOfBottomDivider - mSelectionDividerHeight;
            mSelectionDivider.setBounds(0, topOfBottomDivider, mRight, bottomOfBottomDivider);
            mSelectionDivider.draw(canvas);
        }

  因此要修改分隔線顏色,我們可以採取這樣的思路:用反射拿到私有變數mSelectionDivider,並且在NumberPicker繪製之前,用一個colorDrawable替換它。

  為了方便以後使用,我們這裡建立一個DatePicker的子類,並在它的構造中找到用於選擇年月日的三個NumberPicker,將它們儲存到一個ArrayList中,提供外部方法以設定間隔和顏色。我這裡為了設定日期方便還提供了日期的setter和getter方法,程式碼如下。


import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.NumberPicker;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * Créé par liusiqian 15/11/27.
 */
public class CustomDatePicker extends DatePicker
{
    private List<NumberPicker> mPickers;

    public CustomDatePicker(Context context)
    {
        super(context);
        findNumberPicker();
    }

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

    public CustomDatePicker(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        findNumberPicker();
    }

    /**
     * 得到控制元件裡面的numberpicker元件
     */
    private void findNumberPicker()
    {
        mPickers = new ArrayList<NumberPicker>();
        LinearLayout llFirst = (LinearLayout) getChildAt(0);
        LinearLayout mSpinners = (LinearLayout) llFirst.getChildAt(0);

        for (int i = 0; i < mSpinners.getChildCount(); i++)
        {
            NumberPicker picker = (NumberPicker) mSpinners.getChildAt(i);
            mPickers.add(i, picker);
        }
    }

    /**
     * 設定時間
     * @param strDate  yyyy-mm-dd
     */
    public void setDate(String strDate)
    {
        int day, month, year;
        if (!TextUtils.isEmpty(strDate))
        {
            String[] dateValues = strDate.split("-");
            if (dateValues.length == 3)
            {
                year = Integer.parseInt(dateValues[0]);
                month = Integer.parseInt(dateValues[1]) - 1;
                day = Integer.parseInt(dateValues[2]);
                updateDate(year, month, day);
                return;
            }
        }

        //error
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(System.currentTimeMillis());
        day = calendar.get(Calendar.DAY_OF_MONTH);
        month = calendar.get(Calendar.MONTH);
        year = calendar.get(Calendar.YEAR);
        updateDate(year, month, day);
    }

    /**
     * 獲得時間
     * @return  yyyy-mm-dd
     */
    public String getDate()
    {
        StringBuilder sbDate = new StringBuilder();
        sbDate.append(format2Digits(getYear())).append("-")
                .append(format2Digits(getMonth()+1)).append("-")
                .append(format2Digits(getDayOfMonth()));
        return sbDate.toString();
    }

    private String format2Digits(int value)
    {
        return String.format("%02d",value);
    }


    /**
     * 設定picker間隔
     *
     * @param margin
     */
    public void setPickerMargin(int margin)
    {
        for (NumberPicker picker : mPickers)
        {
            LinearLayout.LayoutParams lps = (LinearLayout.LayoutParams) picker.getLayoutParams();
            lps.setMargins(margin, 0, margin, 0);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
            {
                lps.setMarginStart(margin);
                lps.setMarginEnd(margin);
            }
            picker.setLayoutParams(lps);
        }
    }

    /**
     * 設定時間選擇器的分割線顏色
     */
    public void setDividerColor(int color)
    {
        for (int i = 0; i < mPickers.size(); i++)
        {
            NumberPicker picker = mPickers.get(i);

            try
            {
                Field pf = NumberPicker.class.getDeclaredField("mSelectionDivider");
                pf.setAccessible(true);
                pf.set(picker, new ColorDrawable(color));
            }
            catch (NoSuchFieldException e)
            {
                e.printStackTrace();
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }

        }
    }
}

  使用時,直接呼叫提供給外部的setDividerColor和setPickerMargin兩個方法即可。

    CustomDatePicker picker = (CustomDatePicker) findViewById(R.id.date_picker);
    picker.setDividerColor(0xffcccccc);
    picker.setPickerMargin(1);

  設定之後效果如下圖所示。
設定之後