需求:实现一年月日选择器,默认为当前日期,三者联动,并且在切换年月时,根据是否闰年或者月份天数动态改变日。多说无益,请看下图:
使用的第三方库(https://github.com/helloJp/WheelView)
此库可兼容到低版本,定制性很强,获取选中数据很方便。详情请戳链接,感谢helloJp的分享。
activity_main.xml放置一个封装好WheelView的自定义控件,没有什么自定义属性
"http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
.oubowu.com.datepicker.DatePicker
android:id="@+id/birthday_picker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="5dp"
android:gravity="center_horizontal" />
date_picker.xml线性布局放置三个WheelView,分别对应年月日:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/rectangle_bg"
android:padding="2dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<test.oubowu.com.datepicker.WheelView
android:id="@+id/year"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:itemNumber="5"
app:lineColor="@android:color/darker_gray"
app:lineHeight="2dp"
app:maskHight="32dp"
app:noEmpty="true"
app:normalTextColor="@color/material_green_300"
app:normalTextSize="14sp"
app:selectedTextColor="@color/material_light_blue_a700"
app:selectedTextSize="22sp"
app:unitHight="50dp"
/>
<test.oubowu.com.datepicker.WheelView
android:id="@+id/month"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:itemNumber="5"
app:lineColor="@android:color/darker_gray"
app:lineHeight="2dp"
app:maskHight="32dp"
app:noEmpty="true"
app:normalTextColor="@color/material_light_blue_a100"
app:normalTextSize="14sp"
app:selectedTextColor="@color/material_red_300"
app:selectedTextSize="22sp"
app:unitHight="50dp"
/>
<test.oubowu.com.datepicker.WheelView
android:id="@+id/day"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:itemNumber="5"
app:lineColor="@android:color/darker_gray"
app:lineHeight="2dp"
app:maskHight="32dp"
app:noEmpty="true"
app:normalTextColor="@color/material_orange_500"
app:normalTextSize="14sp"
app:selectedTextColor="@color/material_deep_purple_300"
app:selectedTextSize="22sp"
app:unitHight="50dp"
/>
LinearLayout>
自定义属性如下,定制性相当强:
Attributes
There are several attributes you can set:
attr 属性 description 描述
lineColor divider line color 分割线颜色
lineHeight divider line height 分割线高度
itemNumber wheelview show item count 此wheelView显示item的个数
maskHight mask height 蒙版高度(normalText的位置)
noEmpty if set true select area can't be null(empty),or could be empty 设置true则选中不能为空,否则可以是空
normalTextColor unSelected Text color 未选中文本颜色
normalTextSize unSelected Text size 未选中文本字体大小
selectedTextColor selected Text color 选中文本颜色
selectedTextSize selected Text size 选中文本字体大小
unitHight item unit height 每个item单元的高度
主要看下DatePicker 代码是如何实现联动的:
public class DatePicker extends LinearLayout implements WheelView.OnSelectListener {
public DatePicker(Context context) {
this(context, null);
}
public DatePicker(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 获取选择的年
*
* @return
*/
public String getYear() {
return mWheelYear.getSelectedText();
}
/**
* 获取选择的月
*
* @return
*/
public String getMonth() {
return mWheelMonth.getSelectedText();
}
/**
* 获取选择的日
*
* @return
*/
public String getDay() {
return mWheelDay.getSelectedText();
}
private WheelView mWheelYear;
private WheelView mWheelMonth;
private WheelView mWheelDay;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
LayoutInflater.from(getContext()).inflate(R.layout.date_picker, this);
mWheelYear = (WheelView) findViewById(R.id.year);
mWheelMonth = (WheelView) findViewById(R.id.month);
mWheelDay = (WheelView) findViewById(R.id.day);
// 格式化当前时间,并转换为年月日整型数据
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
String[] split = sdf.format(new Date()).split("-");
int currentYear = Integer.parseInt(split[0]);
int currentMonth = Integer.parseInt(split[1]);
int currentDay = Integer.parseInt(split[2]);
// 设置默认年月日为当前日期
mWheelYear.setData(getYearData(currentYear));
mWheelYear.setDefault(0);
mWheelMonth.setData(getMonthData());
mWheelMonth.setDefault(currentMonth - 1);
mWheelDay.setData(getDayData(getLastDay(currentYear, currentMonth)));
mWheelDay.setDefault(currentDay - 1);
mWheelYear.setOnSelectListener(this);
mWheelMonth.setOnSelectListener(this);
mWheelDay.setOnSelectListener(this);
}
/**
* 年范围在:1900~今年
*
* @param currentYear
* @return
*/
private ArrayList getYearData(int currentYear) {
ArrayList list = new ArrayList<>();
for (int i = currentYear; i >= 1900; i--) {
list.add(String.valueOf(i));
}
return list;
}
private ArrayList getMonthData() {
ArrayList list = new ArrayList<>();
for (int i = 1; i <= 12; i++) {
list.add(String.valueOf(i));
}
return list;
}
/**
* 日范围在1~lastDay
*
* @param lastDay
* @return
*/
private ArrayList getDayData(int lastDay) {
//ignore condition
ArrayList list = new ArrayList<>();
for (int i = 1; i <= lastDay; i++) {
list.add(String.valueOf(i));
}
return list;
}
/**
* 判断是否闰年
*
* @param year
* @return
*/
private boolean isLeapYear(int year) {
return (year % 100 == 0 && year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
}
/**
* 获取特定年月对应的天数
*
* @param year
* @param month
* @return
*/
private int getLastDay(int year, int month) {
if (month == 2) {
// 2月闰年的话返回29,防止28
return isLeapYear(year) ? 29 : 28;
}
// 一三五七八十腊,三十一天永不差
return month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ? 31 : 30;
}
@Override
public void endSelect(View view, int id, String text) {
// 滚轮滑动停止后调用
switch (view.getId()) {
case R.id.year:
case R.id.month:
// 记录当前选择的天数
int selectDay = Integer.parseInt(getDay());
// 根据当前选择的年月获取对应的天数
int lastDay = getLastDay(Integer.parseInt(getYear()), Integer.parseInt(getMonth()));
// 设置天数
mWheelDay.setData(getDayData(lastDay));
// 如果选中的天数大于实际天数,那么将默认天数设为实际天数;否则还是设置默认天数为选中的天数
if (selectDay > lastDay) {
mWheelDay.setDefault(lastDay - 1);
} else {
mWheelDay.setDefault(selectDay - 1);
}
break;
}
}
@Override
public void selecting(View view, int id, String text) {
}
}
DatePicker继承于线性布局,在onFinishInflate填充date_picker.xml布局,然后拿到三个wheelview,格式化时间,取出年月日。
WheelView的方法:
Method
1. setData(ArrayList data)
set WheelView data
设置WheelView的数据
2. resetData(ArrayList data)
reset WheelView data ,if you has setData
重置 WheelView的数据,如果已经设置过的话
3. int getSelected()
get selected item index
获取选中项的index
4. String getSelectedText()
get selected item text
获取选中项的文本信息
5. boolean isScrolling
is WheelView is scrolling
获取WheelView是否在滚动中
6. boolean isEnable()
is WheelView is enable
获取wheelView是否可用
7. void setEnable(boolean isEnable)
set WheelView enable
设置WheelView是否可用
8. void setDefault(int index)
set default selected index
设置默认选中项的index
9. int getListSize()
get WheelView item count
获取WheelView的item项个数
10. String getItemText(int index)
get the text by index
获取index位置上的文本数据
11. void setOnSelectListener(OnSelectListener onSelectListener)
set listener on WheelView that can get info when WheelView is scrolling or stop scroll.
对WheelView设置监听,在 滑动过程 或者 滑动停止 返回数据信息。
年的话是以当前年作为最开始选中的,因为我实际用来做生日选择器的;月就是12个月没什么好说的。
mWheelYear.setData(getYearData(currentYear));
private ArrayList<String> getYearData(int currentYear) {
ArrayList<String> list = new ArrayList<>();
for (int i = currentYear; i >= 1900; i--) {
list.add(String.valueOf(i));
}
return list;
}
日的话稍微复杂点,因为月份不同日数不同,闰年二月天数多一天,所以做多判断。
getLastDay(currentYear, currentMonth)就是根据年月算出日数
mWheelDay.setData(getDayData(getLastDay(currentYear, currentMonth)));
private ArrayList getDayData(int lastDay) {
//ignore condition
ArrayList list = new ArrayList<>();
for (int i = 1; i <= lastDay; i++) {
list.add(String.valueOf(i));
}
return list;
}
private int getLastDay(int year, int month) {
if (month == 2) {
// 2月闰年的话返回29,防止28
return isLeapYear(year) ? 29 : 28;
}
// 一三五七八十腊,三十一天永不差
return month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12 ? 31 : 30;
}
private boolean isLeapYear(int year) {
return (year % 100 == 0 && year % 400 == 0) || (year % 100 != 0 && year % 4 == 0);
}
我们要联动的话就需要监听三个WheelView的滚动停止对应的数据了,WheelView提供setOnSelectListener给我们监听,这里只需在停止的时候拿到数据做联动即可。
为了直观一点,我们选中的天数会根据年月的不同计算出的实际天数做对比,若选中的天数大于实际天数,会回退到实际天数。
@Override
public void endSelect(View view, int id, String text) {
// 滚轮滑动停止后调用
switch (view.getId()) {
case R.id.year:
case R.id.month:
// 记录当前选择的天数
int selectDay = Integer.parseInt(getDay());
// 根据当前选择的年月获取对应的天数
int lastDay = getLastDay(Integer.parseInt(getYear()), Integer.parseInt(getMonth()));
// 设置天数
mWheelDay.setData(getDayData(lastDay));
// 如果选中的天数大于实际天数,那么将默认天数设为实际天数;否则还是设置默认天数为选中的天数
if (selectDay > lastDay) {
mWheelDay.setDefault(lastDay - 1);
} else {
mWheelDay.setDefault(selectDay - 1);
}
break;
}
}
实在是简单实用,比官方的好用何止一丢丢。
Demo下载链接如下:
http://download.csdn.net/detail/oushangfeng123/9034101