更改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);
設定之後效果如下圖所示。