自定義日期時間選擇器

最近因項目需要,要做一個日期時間選擇器,平時做日期時間選擇器都是用的系統提供的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下載

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