自定义个性的DatePicker

来源:互联网 发布:东欧剧变时的中国知乎 编辑:程序博客网 时间:2024/04/29 05:19

ps: 前段时间遇到一个需求,需可以选月份和号数切换且年份不循环的时间选择器。

解决方式:采用NumberPicker自定义个性的DatePicker

先从官网看下 NumberPicker的属性:https://developer.android.com/reference/android/widget/NumberPicker.html

NumberPicker的api:   setFormatter(NumberPicker.Formatter formatter):设置内容的格式    setMaxValue(int maxValue):设置最大值    setMinValue(int minValue):设置最小值    setValue(int value):      根据索引来显示内容    setWrapSelectorWheel(boolean wrapSelectorWheel):设置是否在循环    setOnScrollListener(NumberPicker.OnScrollListener onScrollListener):设置滚动的监听器     setOnValueChangedListener(NumberPicker.OnValueChangeListener    onValueChangedListener):    设置内容改变的监控器注意点:    1.maxValues=内容长度-1    2.若是maxValues发生改变,setWrapSelectorWheel()需要重新设置     

为什么maxValues=内容长度-1,源码给了答案:
这里写图片描述

开始动手啦!

(一)自定义NumberPicker:改变字体颜色
CustormNumberPicker.java:

public class CustormNumberPicker extends NumberPicker {    public CustormNumberPicker(Context context, AttributeSet attrs) {        super(context, attrs);    }    public CustormNumberPicker(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(Color.parseColor("#e68f17"));        }    }}

(二)改变NumberPicker的间隔线颜色,关闭editext的光标:
官方没有提供改变NumberPicker的分割线颜色的api,这里就采用反射来突破Private私有化。(ps:从网上找到的解决方式)

  public void setNumberPickerCong(NumberPicker picker, int maxValues,        String[] values, int currentPosition) {        picker.setMinValue(0);        picker.setMaxValue(maxValues);        if(picker.getId()==R.id.selectdate_dialog_year){//年份不循环            picker.setWrapSelectorWheel(false);        }        picker.setDisplayedValues(values);        picker.setValue(currentPosition);        picker.setOnValueChangedListener(this);          //反射设置分割线颜色:        Field[] pickerFields = NumberPicker.class.getDeclaredFields();        for (Field f : pickerFields) {            if (f.getName().equals("mSelectionDivider")) {                f.setAccessible(true);                try {                    //设置需求的颜色                    f.set(picker,                        new ColorDrawable(context.getResources().getColor(                                R.color.gasstation_selectdate_gray)));                } catch (Exception e) {                    e.printStackTrace();                }                break;            }        }         //反射设置分割线高度:        for (Field f : pickerFields) {            if (f.getName().equals("mSelectionDividerHeight")) {                f.setAccessible(true);                try {                    f.set(picker, 1);                } catch (Exception e) {                    e.printStackTrace();                }                break;            }        }        //关掉编辑模式        picker.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);     }

(三)计算每年每个月的最大天数:

Calendar可以获取当前年,月,分。还可以获取某年某月中的最大天数代码如下:
    private   Calendar calendar;    /**     * 获取当前的年,月,号,当前月最大号数    */    public void getCurrentDate() {        calendar= Calendar.getInstance(Locale.getDefault());        currentYear = calendar.get(Calendar.YEAR);        currentMonth = calendar.get(Calendar.MONTH);        currentDay = calendar.get(Calendar.DAY_OF_MONTH);        currentMaxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);    }    /**     *  计算某月最大天数     * @param year     * @param month     * @return     */    public int getMaxDay(int year, int month) {        calendar.clear();//清空默认时间        calendar.set(Calendar.YEAR, year);        calendar.set(Calendar.MONTH, month - 1);// Calendar中,月份从0开始算起        int  day = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);        return day;    }

(四)初始化年,月,天的数据:
这里采用四个数组来装天数(28,29,30,31),一个数组来装月的数据(1,2….,12),一个数组装年

 private String[]  years, months, days; private List<String> dayList; private String[] day_28, day_29, day_30, day_31; private void initData() {        dayList = new ArrayList<>();        years = new String[5];        months = new String[12];        //初始化,月份的数据,四种天数的数据(28,29,30,31)        for (int i = 1; i < 32; i++) {            int position = i - 1;            String content = String.valueOf(i);            setYearCong(position);            setMonthCong(position,content);            dayList.add(content);            setDayCong(i,dayList);        }        recycleList();    }    /**     * 设置当前年份的前后两年,比如2014,2015,2016,2017,2018     * @param position     */    public void setYearCong(int position){        if(position<5){            years[position]=String.valueOf((currentYear+2)-4+position);        }    }    /**     * 设置月份,1~12     * @param position     * @param month     */    public void setMonthCong(int position,String month){        if (position < 12) {            months[position] = month;        }    }    /**     * 设置天数四个数组,(28,29,30,31)     * @param index     * @param dayList     */    public void setDayCong(int index,List<String>  dayList){        switch (index){            case 28:                day_28= new String[28];                listCopyArray(dayList, day_28);                break;            case  29:                day_29= new String[29];                listCopyArray(dayList, day_29);                break;            case 30:                day_30= new String[30];                listCopyArray(dayList, day_30);                break;            case 31:                day_31= new String[31];                listCopyArray(dayList, day_31);                break;        }    }    /**     * 回收list     */    private void recycleList() {        if(dayList!=null){            dayList.clear();            dayList=null;        }    }

(五)监控滑动年或者月的选中状态改变时,这个月中的最大天数也随着改变:

在NumberPicker的setOnValueChangedListener()中监控滑动导致值改变:

    /**     * 监控NumberPicker 的滑动值改变:     * 当年份或者月份改变时,每个月的最大天数必须随着改变     * @param picker     * @param oldVal     * @param newVal     */    @Override    public void onValueChange(NumberPicker picker, int oldVal, int newVal) {        switch (picker.getId()) {            case R.id.selectdate_dialog_year:                 currentMaxDay=getMaxDay(Integer.valueOf(years[newVal]),                          Integer.valueOf( months[month_NumberPicker.getValue()] ) );                setDay_NnmberPicekerCong(currentMaxDay);                break;            case R.id.selectdate_dialog_month:                currentMaxDay=getMaxDay(Integer.valueOf( years[year_NumberPicker.getValue() ]),                         Integer.valueOf( months[ newVal ] ));                setDay_NnmberPicekerCong(currentMaxDay);                break;        }    } /**     * 根据每个月最大天数,来设置显示day的NumberPicker     * @param index */    public void setDay_NnmberPicekerCong(int index) {        switch (index) {            case 28:                days = day_28;                break;            case 29:                days = day_29;                break;            case 30:                days = day_30;                break;            case 31:                days = day_31;                break;        }         if(isFirt){  //设置当前的号数,即今天是几号             setNumberPickerCong(day_NnmberPiceker, days.length - 1, days, currentDay - 1);            isFirt=false;        }         else{  //滑动年,月导致最大号数的改变(即28或者29或者30或者31)             int before_Size= day_NnmberPiceker.getMaxValue()+1;             if(before_Size<=(days.length-1)){//原本的length< 即将设置max(特殊情况:b_length=m_max)                day_NnmberPiceker.setDisplayedValues(days);                day_NnmberPiceker.setMaxValue(days.length-1);                day_NnmberPiceker.setMinValue(0);            }else{  //(原本的max+1)>=即将设置的length                day_NnmberPiceker.setMinValue(0);                day_NnmberPiceker.setMaxValue(days.length-1);                 day_NnmberPiceker.setDisplayedValues(days);            }        }    }

在更新NumberPicker的时候遇到一个很奇特的问题,拿出来分享下:

   操作          length               maxValues   curent         30                    29   update         31                     30  先设置setMaxValues(),再设置setDisplayedValues() 运行结果却报异常: error: java.lang.ArrayIndexOutOfBoundsException: length=30; index=30

截图如下:
这里写图片描述

经过多次尝试,总结规律:原本的maxValues>=即将设置的maxValues,那先设置setMax,再设置content.反之,先设置content,在来设置max

ps:将三个numberPicker(年,月,天)封装在Dialog中,对外提供选择时间的方法,在下载的项目中都有,偷懒不写了

经历一番波折,填坑,最终完成需求,展示成果:

这里写图片描述

这里写图片描述

项目下载:http://download.csdn.net/detail/hexingen/9590302

0 0
原创粉丝点击