自定义个性的DatePicker

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值