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