
Android-PickerView时间选择器定制实战指南5分钟掌握年月日时分秒格式、隐藏单位与日期范围限制【免费下载链接】Android-PickerViewThis is a picker view for android , support linkage effect, timepicker and optionspicker.时间选择器、省市区三级联动项目地址: https://gitcode.com/gh_mirrors/an/Android-PickerView做Android开发的朋友一定遇到过这样的场景注册页面让用户选出生日期订单页面要定配送时间会员中心要设置有效期……每一个都需要一个能用的时间选择器。但系统自带的控件样式固定、格式单一想显示个2026年08月17日都要费一番功夫。今天要介绍的 Android-PickerView就是为解决这个痛点而生的开源轮子它提供时间选择器与选项选择器两大组件支持年月日时分秒任意组合、自定义标签、日期范围限制、甚至省市区三级联动把时间选择器定制从繁琐的造轮子工作变成几行配置的事。接下来我们从一个真实需求出发一步步把它用起来。五步快速上手让时间选择器在你的页面跑起来先说结论这个库的使用模式非常统一——用一个 Builder构建器把参数配好再调用.build()生成选择器。就像你去餐厅点菜把要什么、多大、什么口味告诉服务员后厨直接给你端上来。先克隆项目源码把pickerview与wheelview两个 module 引入你的工程git clone https://gitcode.com/gh_mirrors/an/Android-PickerView项目结构里有两个核心源码目录后面会反复提到建议先有个印象pickerview/src/main/java/com/bigkoo/pickerview/builder/TimePickerBuilder.java时间选择器的配置入口pickerview/src/main/java/com/bigkoo/pickerview/builder/OptionsPickerBuilder.java选项选择器如省市区的配置入口下面是最小的可运行示例——弹出一个默认的时间选择器点击确定后把结果回填到按钮上// 1. 先声明成员变量方便后续 show() 弹出 private TimePickerView pvTime; private void initTimePicker() { // 2. 构造 TimePickerBuilder同时传入选中回调 pvTime new TimePickerBuilder(this, new OnTimeSelectListener() { Override public void onTimeSelect(Date date, View v) { // 4. 用户点确定后date 就是选中的时间 SimpleDateFormat sdf new SimpleDateFormat(yyyy-MM-dd HH:mm:ss); btnTime.setText(sdf.format(date)); } }).build(); // 3. build() 生成选择器对象 }调用时一句话弹出pvTime.show(); // 从底部弹出选择器到这里一个可用的时间选择器就跑通了。默认配置下显示的是年月日时分秒六个滚轮年份范围 1900–2100。但实际业务很少需要这么齐全下面进入正题如何把时间选择器定制成你想要的样子。场景一注册页只选出生日期——如何隐藏不需要的时间单位假设你在做一个注册页用户只需要填出生日期精确到天就够了。时分秒三个滚轮放在那里既占空间又显得很傻。这就要用到setType方法——它接收一个长度为 6 的布尔数组依次对应年、月、日、时、分、秒true显示、false隐藏。pvTime new TimePickerBuilder(this, new OnTimeSelectListener() { Override public void onTimeSelect(Date date, View v) { btnBirthday.setText(new SimpleDateFormat(yyyy-MM-dd).format(date)); } }) .setType(new boolean[]{true, true, true, false, false, false}) // 只显示年月日 .build();同理如果你做一个秒表记录页面只需要时分秒那就反过来.setType(new boolean[]{false, false, false, true, true, true}) // 只显示时分秒简单来说这个布尔数组就像一个开关面板想留哪个单位就把对应位置拨到true。这里要注意两点数组长度必须是 6多一个少一个都会在运行时抛异常源码WheelTime.java中有type.length ! 6的校验。隐藏单位只是把对应滚轮setVisibility(GONE)并不会影响数据的读取回调里拿到的Date依然完整。配好显示单位后下一个问题自然来了这些滚轮旁边的年月日文字能不能换成我自己的场景二界面文案国际化——自定义年月日标签文字与偏移setLabel方法接收 6 个字符串参数对应年、月、日、时、分、秒的标签文字。中文界面用年/月/日英文界面换成Y/M/D只需要一行.setLabel(年, 月, 日, 时, 分, 秒) // 想换什么写什么除了文字内容标签的位置也能微调。setTextXOffset可以让某个标签在水平方向发生偏移常用于配合自定义布局做视觉对齐// 六个参数分别对应年月日时分秒的 X 轴偏移量像素 .setTextXOffset(0, 0, 0, 40, 0, -40)还有一个容易忽略但很实用的小开关——isCenterLabel。默认情况下每个滚轮条目都带着标签文字滚起来满屏都是年月设置isCenterLabel(false)后只有中间选中项显示标签界面立刻清爽许多。项目里MainActivity.java的自定义时间选择器就用了这个配置效果可以参考预览图场景三配送时间不能选昨天——三步限制可选日期范围很多业务对可选时间有硬性约束外卖配送时间不允许选过去的时刻机票不能选已起飞的班次。这时就用setRangDate(startDate, endDate)来圈定可选范围。// 第一步定义三个时间点 Calendar selectedDate Calendar.getInstance(); // 默认选中当前时间 Calendar startDate Calendar.getInstance(); startDate.set(2014, 1, 23); // 起始时间2014年2月23日 Calendar endDate Calendar.getInstance(); endDate.set(2027, 2, 28); // 结束时间2027年3月28日 // 第二步把时间点配置进 Builder pvTime new TimePickerBuilder(this, new OnTimeSelectListener() { Override public void onTimeSelect(Date date, View v) { btnTime.setText(getTime(date)); } }) .setDate(selectedDate) // 默认选中项 .setRangDate(startDate, endDate) // 可选范围 .build();为什么强调三步因为setDate默认选中和setRangDate可选范围必须配合使用如果只设范围不设默认值选择器会把起始日期当作默认选中项如果默认值超出范围也会被自动拉回起始日期。这一逻辑在TimePickerView.java的initDefaultSelectedDate()里写得明明白白。这里有一个新手必踩的巨坑我必须用最大的字号提醒你Calendar的月份是从 0 开始计数的0 代表 1 月11 代表 12 月。所以startDate.set(2014, 1, 23); // 这是 2014年2月23日不是 1月写错月份不会报错但选择器一打开日期就不对排查起来非常隐蔽。老规矩要表示 1 月就写 0想选 12 月就写 11。场景四一键换肤——用颜色与间距让选择器贴合你的设计稿很多团队的设计稿里强调色是品牌绿或品牌蓝系统默认的灰色分割线显然不够看。别急TimePickerBuilder提供了一整套化妆方法全部支持链式调用pvTime new TimePickerBuilder(this, new OnTimeSelectListener() { Override public void onTimeSelect(Date date, View v) { /* 回调处理 */ } }) .setContentTextSize(18) // 滚轮文字大小sp .setDividerColor(0xFF24AD9D) // 分割线颜色品牌色 .setTextColorCenter(0xFF24AD9D) // 选中项文字颜色 .setTextColorOut(0xFFa8a8a8) // 非选中项文字颜色 .setLineSpacingMultiplier(1.6f) // 条目行距倍数取值范围 1.0~4.0 .setItemVisibleCount(5) // 最大可见条目数建议取奇数 .isAlphaGradient(true) // 上下渐变透明效果更有立体感 .isDialog(true) // 以 Dialog 形式弹出 .build();逐条解释一下行距倍数决定滚轮的胖瘦倍数越大条目间距越大一般 1.2~2.0 比较舒服超出 1.0~4.0 会被钳制到边界值。可见条目数默认是 9设为偶数时内部会自动加 1比如设 6 实际显示 7所以直接用 3、5、7、9 这些奇数最省心。isDialog(true)会把选择器挂到 DecorView 上以对话框形式展示配合addOnCancelClickListener还能监听取消点击适合作为表单页的弹层。这套配置的组合非常多效果差异巨大。项目预览里有一张夜间模式的截图就是通过调整背景色、分割线和文字颜色实现的暗色风格场景五设计稿非要自定义头部——用自定义布局替换整个外壳当换色已经满足不了设计时就需要连布局一起换。setLayoutRes方法允许你传入一个自定义布局文件并配合CustomListener接管布局里的所有控件。先看布局文件核心是一个顶部操作栏加一个 id 为timepicker的滚轮容器参考app/src/main/res/layout/pickerview_custom_time.xml!-- 顶部左边图片取消按钮右边文字确定按钮 -- RelativeLayout ImageView android:idid/iv_cancel ... / TextView android:idid/tv_finish android:text完成 ... / /RelativeLayout !-- 滚轮容器id 必须叫 timepicker且要包含 year/month/day/hour/min/second -- LinearLayout android:idid/timepicker ... com.contrarywind.view.WheelView android:idid/year .../ com.contrarywind.view.WheelView android:idid/month .../ !-- ... 其余滚轮同理 -- /LinearLayout再在代码里注册自定义布局并给完成和取消控件绑上事件pvCustomTime new TimePickerBuilder(this, new OnTimeSelectListener() { Override public void onTimeSelect(Date date, View v) { btnCustomTime.setText(getTime(date)); } }) .setLayoutRes(R.layout.pickerview_custom_time, new CustomListener() { Override public void customLayout(View v) { // v 就是自定义布局的根 View从这里拿控件 TextView tvFinish v.findViewById(R.id.tv_finish); ImageView ivCancel v.findViewById(R.id.iv_cancel); // 完成先取数据再关闭 tvFinish.setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { pvCustomTime.returnData(); // 触发 onTimeSelect 回调 pvCustomTime.dismiss(); } }); // 取消直接关闭 ivCancel.setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { pvCustomTime.dismiss(); } }); } }) .build();这里有几个关键点一个都不能错timepicker这个 id 必须存在。源码TimePickerView.initView()里通过findViewById(R.id.timepicker)找到滚轮容器找不到就会空指针崩溃。六个滚轮子控件的 id 要保留year、month、day、hour、min、secondWheelTime.java就是按这些 id 去找滚轮并填充数据的。确认/取消的点击逻辑需要自己写——returnData()会触发你传入的OnTimeSelectListenerdismiss()负责关闭。如果你同时需要给选项选择器做自定义布局套路完全一样只是容器 id 换成了optionspicker。项目里的CustomLayout.gif展示了自定义布局的实际效果场景六长辈要看的农历日期——一行代码开启农历模式对生活服务类 App 来说农历是刚需。setLunarCalendar(true)可以直接把时间选择器切成农历显示pvLunar new TimePickerBuilder(this, new OnTimeSelectListener() { Override public void onTimeSelect(Date date, View v) { // 注意这里回调返回的是农历对应的公历时间 Toast.makeText(MainActivity.this, getTime(date), Toast.LENGTH_SHORT).show(); } }) .setLunarCalendar(true) .build();更妙的是农历模式支持运行时动态切换——这在做公农历切换功能时非常有用。示例代码MainActivity.java里用了一个 CheckBox 来切换cbLunar.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { // 切换农历开关滚轮数据会自动重建 pvLunar.setLunarCalendar(!pvLunar.isLunarCalendar()); } });农历的数据计算由pickerview/src/main/java/com/bigkoo/pickerview/utils/ChinaDate.java与LunarCalendar.java两个工具类完成支持范围是 1900–2100 年还考虑了闰月的处理。由于农历月份宽度比公历大切换后通常还要顺手调整各滚轮的权重示例代码中的setTimePickerChildWeight方法就是干这个的。切换后的效果可参考场景七不止时间——用选项选择器做省市区三级联动这个库的另一半能力是OptionsPickerView它不仅支持一、二、三级选项还允许选择是否联动。省市区是最经典的联动场景数据解析逻辑可以参考app/src/main/java/com/bigkoo/pickerviewdemo/JsonDataActivity.java。核心思路分三步解析 JSON 成三个层级的 List → 用setPicker把数据喂进去 → 回调里按索引取值。// 三个层级的数据结构注意是套娃泛型 ListProvinceBean options1Items; // 省级列表 ListListString options2Items; // 每个省的城市列表 ListListListString options3Items; // 每个城市的区县列表 pvOptions new OptionsPickerBuilder(this, new OnOptionsSelectListener() { Override public void onOptionsSelect(int options1, int options2, int options3, View v) { // 回调返回的是三个级别的选中索引不是字符串本身 String province options1Items.get(options1).getPickerViewText(); String city options2Items.get(options1).get(options2); String area options3Items.get(options1).get(options2).get(options3); btnAddress.setText(province city area); } }) .setTitleText(城市选择) // 顶部标题 .setSelectOptions(0, 1) // 默认选中第一个省的第一项索引从0开始 .build(); // 数据必须通过 setPicker 注入注意层级顺序 pvOptions.setPicker(options1Items, options2Items, options3Items); pvOptions.show();三个容易忽略的细节实体类要显示什么文字如果你的数据源是 JavaBean比如ProvinceBean必须实现IPickerViewData接口并提供getPickerViewText()方法滚轮才知道该显示哪个字段。这一接口定义在wheelview/src/main/java/com/contrarywind/interfaces/IPickerViewData.java。不想联动怎么办如果三个滚轮各自独立比如口味尺码颜色用setNPicker(list1, list2, list3)代替setPicker内部会把联动关掉。滚动实时回调setOptionsSelectChangeListener可以在用户滚动但未点击确定时实时拿到当前选中索引适合做选中即生效的交互。省市区联动的完整效果可以参考避坑指南六个容易翻车的细节把上面所有场景串起来我再帮你把高频坑位集中排一遍都是项目源码里注释过或真实踩过的Calendar 月份从 0 开始。set(2026, 8, 17)是 9 月 17 日。这条值得再次强调因为它不报错、只错值最阴险。setType数组长度必须为 6否则WheelTime构造时会抛RuntimeException(type[] length is not 6)。自定义布局的 id 不能改。timepicker或optionspicker以及六个滚轮子控件的 id 被源码硬编码引用删改必空指针。范围与默认值要成对设置。只设setRangDate不设setDate默认选中会被强制改为起始日期如果起始时间晚于结束时间TimePickerView会直接抛IllegalArgumentException。年份范围有硬边界。库只支持 1900–2100 年startDate早于 1900 或endDate晚于 2100 都会抛异常这是农历算法决定的不是 bug。三级联动数据结构必须层层对齐。options2Items的长度要等于第一级条数options3Items同理索引错位轻则显示错数据重则越界崩溃。JSON 数据量较大时记得在子线程解析参考JsonDataActivity的 HandlerThread 写法避免卡住 UI。结尾从会用到底层都懂回顾一下我们从一个真实业务场景出发完成了 Android-PickerView 时间选择器定制的完整闭环用setType隐藏指定时间单位用setLabel自定义年月日格式用setRangDate限制可选日期范围用颜色与间距配置贴合设计稿用setLayoutRes彻底替换外观用setLunarCalendar支持农历最后顺手把省市区三级联动也拿下了。整个过程中你始终只需要记住一条主线Builder 配置参数 →build()生成对象 →show()弹出 → 回调拿数据。剩下的一切细节都可以在源码里找到答案——想研究滚轮是如何驱动数据刷新的去看wheelview/src/main/java/com/contrarywind/view/WheelView.java想搞懂日期范围是如何动态收缩到指定月份的去读pickerview/src/main/java/com/bigkoo/pickerview/view/WheelTime.java里的setSolar方法。希望这篇 Android 滚轮选择器实战笔记能帮你少走弯路。下次再遇到选时间的需求别急着自己造轮子先想想 Android-PickerView 能不能接住你的需求——大概率几行配置就搞定了。【免费下载链接】Android-PickerViewThis is a picker view for android , support linkage effect, timepicker and optionspicker.时间选择器、省市区三级联动项目地址: https://gitcode.com/gh_mirrors/an/Android-PickerView创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考