最近因项目需要,要做一个日期时间选择器,平时做日期时间选择器都是用的系统提供的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下载