Android开发中有时会用到日期和时间的选择,系统自带的有DatePicker和TimePicker两种,但是当我们需要同时选择日期和时间时,自带的选择器就存在比较大的问题。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp" >
<DatePicker
android:id="@+id/date_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:calendarViewShown="false"
android:datePickerMode="spinner"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TimePicker
android:id="@+id/time_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:timePickerMode="spinner"
app:layout_constraintStart_toEndOf="@id/date_picker"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
当我们将两个选择器放在同一行,方便页面进行适配时发现,自带的选择器空间无法调整大小,两个选择器都会采用默认宽高,使得部分内容无法显示。
因此最好的办法还自定义一个时间日期选择器,同时也能方便以后进行样式修改美化。
系统自带的选择器还有一个NumberPicker,我们可以使用这个控件,结合其他控件做出想要的效果。
部分实现代码如下:
dialog_fragment_date_time_picker.xml
<LinearLayout
android:id="@+id/item_linear_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:gravity="fill_horizontal">
<NumberPicker
android:id="@+id/number_picker_year"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.2"
android:descendantFocusability="blocksDescendants" />
<TextView
android:id="@+id/text_view_year"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.05"
android:gravity="center"
android:text="@string/text_year" />
<NumberPicker
android:id="@+id/number_picker_month"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.15"
android:descendantFocusability="blocksDescendants" />
<TextView
android:id="@+id/text_view_month"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.05"
android:gravity="center"
android:text="@string/text_month" />
<NumberPicker
android:id="@+id/number_picker_date"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.15"
android:descendantFocusability="blocksDescendants" />
<TextView
android:id="@+id/text_view_date"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.05"
android:gravity="center"
android:text="@string/text_date" />
<NumberPicker
android:id="@+id/number_picker_hour"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.15"
android:descendantFocusability="blocksDescendants" />
<TextView
android:id="@+id/text_view_hour"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.05"
android:gravity="center"
android:text="@string/text_time_symbol" />
<NumberPicker
android:id="@+id/number_picker_minute"
android:layout_width="0dp"
android:layout_height="200dp"
android:layout_weight="0.15"
android:descendantFocusability="blocksDescendants" />
</LinearLayout>
先在xml文件中自定义控件样式,为每个控件排好位置。再通过代码对每个NumberPicker进行限制。
DateTimePickerDialogFragment.java
Calendar calendar = Calendar.getInstance();
NumberPicker yearPicker = findViewById(R.id.number_picker_year);
NumberPicker monthPicker = findViewById(R.id.number_picker_month);
NumberPicker datePicker = findViewById(R.id.number_picker_date);
NumberPicker hourPicker = findViewById(R.id.number_picker_hour);
NumberPicker minutePicker = findViewById(R.id.number_picker_minute);
//限制年份范围为前后五年
int yearNow = calendar.get(Calendar.YEAR);
yearPicker.setMinValue(yearNow - 5);
yearPicker.setMaxValue(yearNow + 5);
yearPicker.setValue(yearNow);
yearPicker.setWrapSelectorWheel(false); //关闭选择器循环
//设置月份范围为1~12
monthPicker.setMinValue(1);
monthPicker.setMaxValue(12);
monthPicker.setValue(calendar.get(Calendar.MONTH) + 1);
monthPicker.setWrapSelectorWheel(false);
//日期限制存在变化,需要根据当月最大天数来调整
datePicker.setMinValue(1);
datePicker.setMaxValue(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
datePicker.setValue(calendar.get(Calendar.DATE));
datePicker.setWrapSelectorWheel(false);
//24小时制,限制小时数为0~23
hourPicker.setMinValue(0);
hourPicker.setMaxValue(23);
hourPicker.setValue(calendar.get(Calendar.HOUR_OF_DAY));
hourPicker.setWrapSelectorWheel(false);
//限制分钟数为0~59
minutePicker.setMinValue(0);
minutePicker.setMaxValue(59);
minutePicker.setValue(calendar.get(Calendar.MINUTE));
minutePicker.setWrapSelectorWheel(false);
//为年份和月份设置监听
yearPicker.setOnValueChangedListener(this);
monthPicker.setOnValueChangedListener(this);
当选择器日期发生变化时,我们需要判断所显示的日期月份最多有多少天,防止出现不存在日期的情况,这时就需要为选择器值变动监听,因为年份和月份的变动都会影响当月最大天数,因此可以通过实现NumberPicker.OnValueChangeListener,重写onValueChange()方法来实现:
@Override
public void onValueChange(NumberPicker numberPicker, int i, int i1) {
String dateStr = String.format(Locale.CHINA, "%d-%d", yearPicker.getValue(), monthPicker.getValue());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM", Locale.CHINA);
Calendar calendar = Calendar.getInstance();
calendar.setTime(simpleDateFormat.parse(dateStr));
int dateValue = datePicker.getValue();
int maxValue = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
datePicker.setMaxValue(maxValue);
//重设日期值,防止月份变动时超过最大值
datePicker.setValue(Math.min(dateValue, maxValue));
}
这样一个自定义的可以同时选择日期和时间的选择器就基本完工了,以后还能自定义各个控件的样式来实现想要的效果。当然,获取选择器的结果可以通过定义一个Button来监听点击事件来获取
findViewById(R.id.button_confirm).setOnClickListener(view -> {
//获取的日期时间结果
String result = String.format(Locale.CHINA, "%d-%d-%d %d:%d",
yearPicker.getValue(), monthPicker.getValue(), datePicker.getValue(),
hourPicker.getValue(), minutePicker.getValue());
...
}
最后来看一下显示效果吧(PS:不知道为啥转gif会偏红)