更改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);

  设置之后效果如下图所示。
设置之后

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值