在之前『Android 日期选择器』中介绍了 DatePicker
和 DatePickerDialog
的使用方法,可以满足我们平时开发中对日期选择的需要,日历卡的样式也十分讨喜。
但是,需求是万变的。我们假设这么一种场景,比如说我在做一个软件,用户可以查看以往的月度账单,『支付宝』就有类似的功能:
那么就需要有一个可以选择年份和月份但却没有具体日的功能,怎么实现?
Android 原生并没有类似的控件,所以必须要自己实现。其实对于月份,你完全可以手动把 12 个月都写进去,但是对于年份,你就无能为力了,因为年份理论上来说是一个无穷数,假设你这个软件可以使用 60 年,那么就至少要录入 60 个年份,重复而无意义的工作。
我们能否定制一个类似 DatePicker
的月份选择器呢?
看上去,『支付宝』的月份选择器和 DatePicker
很相像,区别在于去掉了日子列表。二者之间如此相似,是否意味着两者可能并非偶然撞衫,而是本来系出一源呢,我们只需要在 DatePicker
中隐藏日子的滚轮,即可实现移花接木的效果。
public class MonthPicker extends DatePicker {
public MonthPicker(Context context, AttributeSet attrs) {
super(context, attrs);
ViewGroup viewGroup = (ViewGroup) ((ViewGroup) getChildAt(0)).getChildAt(0);
int groupChildCount = viewGroup.getChildCount();
if (groupChildCount == 3) {
viewGroup.getChildAt(1).setVisibility(GONE);
} else if (groupChildCount == 5) {
viewGroup.getChildAt(2).setVisibility(GONE);
viewGroup.getChildAt(3).setVisibility(GONE);
}
}
}
定义 MonthPicker
继承自 DatePicker
,在构造方法中把显示日子的 View
去掉即可。这里做了一个判断分支,因为不同手机上的可能有不同的显示效果:
有些手机显示“月 日 年”三列,那我们就把“日”的列去掉,有些手机显示“月 | 日 | 年”五列,也就是每项中间会有一个分割线分开,那么我们就把“日”的列和分割线一起去掉。
在布局文件中,因为 MonthPicker
继承自 DatePicker
,所以基本按照 DatePicker
的属性设置即可:
<com.picker.MonthPicker
android:id="@+id/month_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:calendarViewShown="false"
android:datePickerMode="spinner" />
这里记得要设置为滚轮样式并隐藏日历卡样式,否则是无法呈现正常效果的。
最终效果如下:
可以看到,获取到的日期也是和 DatePicker
同样的格式,参照『Android 日期选择器』一文,做好相应的截取操作即可。
但是,“月 日 年”听起来是不是有点别扭,没错,这一般是西方国家的使用方法,中国一般会使用“年 月 日”这种说法,所以,当你把语言切换到中文后,就会出现这种情况:
你会尴尬地发现,我们在上面的方法中截掉了“月份”这一项,这就十分离谱了,所以还需要为 MonthPicker
做一些国际化的适配:
public final class MonthPicker extends DatePicker {
public MonthPicker(Context context, AttributeSet attrs) {
super(context, attrs);
ViewGroup viewGroup = (ViewGroup) ((ViewGroup) getChildAt(0)).getChildAt(0);
int groupChildCount = viewGroup.getChildCount();
switch (Locale.getDefault().getLanguage()) {
case "zh":
if (groupChildCount == 3) {
viewGroup.getChildAt(2).setVisibility(GONE);
} else if (groupChildCount == 5) {
viewGroup.getChildAt(3).setVisibility(GONE);
viewGroup.getChildAt(4).setVisibility(GONE);
}
break;
case "en":
if (groupChildCount == 3) {
viewGroup.getChildAt(1).setVisibility(GONE);
} else if (groupChildCount == 5) {
viewGroup.getChildAt(2).setVisibility(GONE);
viewGroup.getChildAt(3).setVisibility(GONE);
}
break;
...
}
}
}
我的做法是通过 Locale
获取当前语言名称,如果当前语言为中文,则截掉处在末端的 View
,如果当前语言为英文,则截掉处在中间的 View
。修改后中文语言下的效果如下:
如果想适配更多语言,只需继续添加 CASE
即可,如果有国家是用“日 月 年”的写法,则再添加截掉处在首位的 View
的逻辑。