自定义日期时间选择器

最近因项目需要,要做一个日期时间选择器,平时做日期时间选择器都是用的系统提供的DatePicker,TimePicker.他们两个的使用方法简单我就不做过多的介绍。但是你如果想在DatePicker上面加上自定义的样式却比较复杂,在网上搜索了一下之后,感觉比较复杂,所以打算自定一个日期时间选择器。

先贴一张日期时间选择器的效果图,如下:

这里写图片描述

  • 自定义Dialog
  • 使用NumberPicker绘制选择器
  • 控制时间选择的范围

自定义Dialog

自定义Dialog的好处是界面布局可以自由的绘制,它的自由度高。只需设置好dialog的宽高,以及显示的位置自定义定义Dialog的代码比较简单,这里主要是构造方法部分的代码。相信你看一下就会明白,如下所示:

 public MyCalendarDialog(Context context, int width, int height, int layout,
                            int style) {
        super(context, style);
        // 设置内容
        view = LayoutInflater.from(context).inflate(layout,null);
        setContentView(view);
        this.context = context;
        // 设置窗口属性
        Window window = getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        // 设置宽度、高度、密度、对齐方式
        float density = getDensity(context);
        params.width = (int) (width * density);
        params.height = (int) (height * density);
        params.gravity = Gravity.CENTER;
        window.setAttributes(params);
        initData();
        initView();
    }
     /**
     * 获取显示密度
     *
     * @param context
     * @return
     */
    public float getDensity(Context context) {
        Resources res = context.getResources();
        DisplayMetrics dm = res.getDisplayMetrics();
        return dm.density;
    }

使用NumberPicker绘制选择器

NumberPicker的效果和DatePicker类似,只是他只包含显示没有里面逻辑控制等的代码。下面xml文件部


 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:background="@drawable/shape_white"
    >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp">

        <NumberPicker
            android:id="@+id/np_year"
            android:layout_width="0dp"
            android:layout_weight="1.3"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_height="wrap_content">

        </NumberPicker>
        <NumberPicker
            android:id="@+id/np_month"
            android:layout_width="0dp"
            android:layout_weight="0.8"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_height="wrap_content">

        </NumberPicker>
        <NumberPicker
            android:id="@+id/np_day"
            android:layout_width="0dp"
            android:layout_weight="0.8"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_height="wrap_content">

        </NumberPicker>
        <NumberPicker
            android:id="@+id/np_hour"
            android:layout_width="0dp"
            android:layout_weight="0.8"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_height="wrap_content">

        </NumberPicker>
        <NumberPicker
            android:id="@+id/np_minute"
            android:layout_width="0dp"
            android:layout_weight="0.8"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_height="wrap_content">

        </NumberPicker>

    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:layout_height="2px"
        android:layout_marginRight="10dp"
        android:layout_marginLeft="10dp"
        android:background="@android:color/darker_gray"></View>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/tv_cancel"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="取消"/>

        <View
            android:layout_width="2px"
            android:layout_height="50dp"
            android:background="@android:color/darker_gray"></View>
        <TextView
            android:id="@+id/tv_sure"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="确定"/>

    </LinearLayout>

</LinearLayout>

最主要的是对NumberPicker进行时间和日期的判断,例如通过设置NumberPicker的最大值和最小值
numberPicker.setMinValue(minute);
numberPicker.setMaxValue(60);

通过设置Formatter,来达到自己想要的NumberPicker显示样式,主要代码如下:

 public class MyFormatter implements NumberPicker.Formatter {
        @Override
        public String format(int value) {
            String s = value + "年";
            return s;
        }
    }

这样显示的就如上图所示,分别写年月日的Formatter.
对于NumberPicker默认中间的小横杆的颜色,NumberPicker默认的字体颜色等如果需要自定义,则需要实现以下的方法:

 /**
     * 修改分割线的颜色
     * @param numberPicker
     */
    private void setNumberPickerDividerColor(NumberPicker numberPicker) {
        NumberPicker picker = numberPicker;
        Field[] pickerFields = NumberPicker.class.getDeclaredFields();
        for (Field pf : pickerFields) {
            if (pf.getName().equals("mSelectionDivider")) {
                pf.setAccessible(true);
                try {
                    //设置分割线的颜色值
                    pf.set(picker, new ColorDrawable(ContextCompat.getColor(context, Color.BLUE)));
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (Resources.NotFoundException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                break;
            }
        }
    }
/**
 * Created by yuanYe on 2016/9/14.
 * QQ 962851730
 */

class MyNumberPicker extends NumberPicker {


    public MyNumberPicker(Context context) {
        super(context);
    }

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

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

    @Override
    public void addView(View child) {
        super.addView(child);
        updateView(child);
    }

    @Override
    public void addView(View child, int index,
                        android.view.ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        updateView(child);
    }

    @Override
    public void addView(View child, android.view.ViewGroup.LayoutParams params) {
        super.addView(child, params);
        updateView(child);
    }

    public void updateView(View view) {
        if (view instanceof EditText) {
            //这里修改字体的属性
            ((EditText) view).setTextColor(ContextCompat.getColor(getContext(), R.color.colorAccent));
//            ((EditText) view).setTextSize();

        }
    }

}

控制时间选择的范围

对于时间的选择控制,需求需要的是只能选择今天以后的时间,为了更好的提醒用户,这里就需要把今天之前的时间隐藏,对于NumberPikcer而言,我是在它的滑动监听事件里面写的相应控制,具体如下:

 /**
     * 连月日,时分秒的联动
     */
    public class MyScrollListener implements NumberPicker.OnValueChangeListener {

        @Override
        public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
            switch (picker.getId()) {
                case R.id.np_year:
                    np_day.setMaxValue(DateUtils.getMonthDays(np_year.getValue(), np_month.getValue() - 1));
                    np_day.setWrapSelectorWheel(true);
                    Log.i("yuanyg", "12314123151555555555555555");
                    if (newVal >= year) {
                        np_month.setWrapSelectorWheel(true);
                        np_month.setMinValue(1);
                        np_day.setWrapSelectorWheel(true);
                        np_day.setMinValue(1);
                        np_hour.setWrapSelectorWheel(true);
                        np_hour.setMinValue(1);
                        np_minute.setWrapSelectorWheel(true);
                        np_minute.setMinValue(1);
                    }
                    break;
                case R.id.np_month:
                    np_day.setMaxValue(DateUtils.getMonthDays(np_year.getValue(), np_month.getValue() - 1));
                    np_day.setWrapSelectorWheel(true);
                    if(np_year.getValue()==year){
                        np_month.setMinValue(month);
                        np_month.setWrapSelectorWheel(false);
                    }

                    if (newVal > month && np_year.getValue() >= year) {
                        np_day.setWrapSelectorWheel(true);
                        np_day.setMinValue(1);
                        np_hour.setWrapSelectorWheel(true);
                        np_hour.setMinValue(1);
                        np_minute.setWrapSelectorWheel(true);
                        np_minute.setMinValue(1);
                    }
                    break;
                case R.id.np_day:
                    Log.i("yuanye","year="+np_year.getValue()+"---------------->month="+np_month.getMaxValue());
                    if(year==np_year.getValue()&&month==np_month.getValue()){
                        Log.i("yuanye","22222222222222222222");
                        np_day.setMinValue(day);
                        np_day.setWrapSelectorWheel(false);
                    }
                    if (np_month.getValue() >= month && np_year.getValue() >= year&&newVal>day) {
                        np_hour.setWrapSelectorWheel(true);
                        np_hour.setMinValue(1);
                        np_minute.setWrapSelectorWheel(true);
                        np_minute.setMinValue(1);
                    }

                    break;
                case R.id.np_hour:
                    if(year==np_year.getValue()&&month==np_month.getValue()&&day==np_day.getValue()){
                        np_hour.setMinValue(hour);
                        np_hour.setWrapSelectorWheel(false);
                    }
                    if (np_month.getValue() >= month && np_year.getValue() >= year&&np_day.getValue()>=day&&newVal>hour) {
                        np_minute.setWrapSelectorWheel(true);
                        np_minute.setMinValue(1);
                    }
                    break;
                case R.id.np_minute:
                    if(year==np_year.getValue()&&month==np_month.getValue()&&day==np_day.getValue()&&hour==np_hour.getValue()){
                        np_minute.setMinValue(mimute);
                        np_minute.setWrapSelectorWheel(false);
                    }
                    break;

            }

        }
    }

到这里我还遇到了一个Bug,就是在初次进入的时候,NumberPicker并不能正常显示我要的样式,必须要我滑动一下后,样式才能正确,在几经百度后,发现,需要用反射加上一下代码:

 /**
     * using reflection to change the value because
     * changeValueByOne is a private function and setValue
     * doesn't call the onValueChange listener.
     *
     * @param higherPicker the higher picker
     * @param increment    the increment
     */
    private void changeValueByOne(final NumberPicker higherPicker, final boolean increment) {

        Method method;
        try {
            // refelction call for
            // higherPicker.changeValueByOne(true);
            method = higherPicker.getClass().getDeclaredMethod("changeValueByOne", boolean.class);
            method.setAccessible(true);
            method.invoke(higherPicker, increment);

        } catch (final NoSuchMethodException e) {
            e.printStackTrace();
        } catch (final IllegalArgumentException e) {
            e.printStackTrace();
        } catch (final IllegalAccessException e) {
            e.printStackTrace();
        } catch (final InvocationTargetException e) {
            e.printStackTrace();
        }
    }

到这里基本就完成了,我在代码中加入上图的一个默认布局,并加入了事件的确认回调函数,具体的使用方法如下:

 final MyCalendarDialog dialog2 = new MyCalendarDialog(ReleaseNoticeActivity.this, R.layout.activity_calendar, R.style.MyCalendar);
                    dialog2.setSureClick(new MyCalendarDialog.MyCalendar() {
                        @Override
                        public void setCalendar(int year, int month, int day, int hour, int minute) {
                        //你点击确认后需要进行的后续操作
                        }
                    });
                    dialog2.show();

本博客为记录我在android开发中遇到的点滴
Demo下载

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章