这可能是史上最好用的PickerView库了
从去年开始大概已经有10个月没有更新博客了,去年结婚,也有了个小baby,导致这么长时间没有更新博客,从今天起博客正常更新,一切步入正轨。今天受一个好朋友的重托分享他的一篇文章:这可能是史上最好用的PickerView库了,同时也欢迎大家关注他的简书账号:笨小孩。下面开启“CV”模式!!!
如果你看到了这篇文章,希望你耐心的把它看完。我保证你认真看完了全部,一定不会让你失望。因为你可能遇到了史上最好用的PickerView库。 本文会通过和目前github上最受欢迎的PickerView库做详细的对比,通过对比你可以清晰的感受它的好用和强大。
先来一下github地址方便那些喜欢直接看代码的同志们:https://github.com/jaaksi/pickerview.
一个非常好用的Android PickerView库,内部提供3种常用类型的Picker。支持扩展自定义Picker。
- TimePicker:时间选择器,包含日期
- MixedTimePicker::聚合的时间选择器
- OptionPicker:联动选择器
Screenshot
MixedTimePicker.gif
custom.png
APK
Demo App下载连接
PickerView
README
Picker
通过组装PickerView实现常用的Picker选择器。上面已经列举提供的3中常用的Picker。
BasePicker
Picker基类:封装了TopBar,PickerView容器,create and add PickerView方法,Picker弹窗等方法。 三种Picker都继承自BasePicker,你也可以继承它扩展自己的Picker。
API
api | description |
---|---|
setPickerBackgroundColor | 设置picker背景 |
setPadding | 设置PickerView父容器padding 单位:px |
setTag | 给Picker 设置tag,用于区分不同的picker等。用法同View setTag |
getRootLayout | 获取PickerView的父容器,创建DefaultTopBar时必须指定 |
setOnPickerChooseListener | 设置picker取消,确定按钮监听。可用于拦截选中操作 |
setTopBar | 设置自定义TopBar |
setInterceptor | 设置拦截器 |
createPickerView | 创建PickerView |
getPickerViews | 获取Picker中所有的pickerview集合 |
addPicker | 将创建的PickerView 添加到上面集合中,createPickerView内部已调用该方法 |
findPickerViewByTag | 通过tag找到对应的PickerView |
isScrolling | 是否滚动未停止。滚动未停止的时候,不响应Picker的取消,确定按键 |
getPickerDialog | 获取Picker弹窗。可以在new之后设置dialog属性 |
show | 显示picker弹窗 |
对比github上最受欢迎的同类库 Android-PickerView
本库将TopBar等通用相关逻辑封装在基类中,并提供代码中创建PickerView方法,不需要再依赖xml。用户自定义Picker时,继承BasePicker,只需要处理自己的逻辑即可,简单便捷。
而对Android-PickerView来说,实现自定义Picker,依然需要处理TopBar等逻辑。造成大量重复代码。
TopBar
TopBar:TopBar通过抽象接口ITopBar来管理,实现Picker与TopBar的解耦。提供默认实现DefaultTopBar。可实现接口定制自己的TopBar。
public interface ITopBar {/*** @return topbar view*/View getTopBarView();/*** @return 取消按钮view*/View getBtnCancel();/*** @return 确定按钮view*/View getBtnConfirm();/*** @return title view*/TextView getTitleView();}
DefaultTopBar API
api | description |
---|---|
setDividerColor | 设置topbar bottom line color |
setDividerHeight | 设置bottom divider line height |
getDivider | 获取TopBar bottom line |
getTitleView | 获取TopBar title view |
Interceptor
拦截器:用于在pickerview创建时拦截,设置pickerview的属性。
Picker内部并不提供对PickerView的设置方法,而是通过Interceptor实现。这种设计用来实现Picker和PickerView的属性设置完美解耦。
private void init(){mTimePicker.setInterceptor(new BasePicker.Interceptor() {@Override public void intercept(PickerView pickerView) {pickerView.setVisibleItemCount(5);// 将年月设置为循环的int type = (int) pickerView.getTag();if (type == TimePicker.TYPE_YEAR || type == TimePicker.TYPE_MONTH) {pickerView.setIsCirculation(true);}}})}
这一点对比 Android-PickerView, 每个Picker都需要声明对PickerView的设置方法,与PickerView严重耦合。需要开发者copy大量重复代码,且无法区分每一个PickerView设置不同的属性。
TimePicker
常用的时间选择器,支持 年、月、日、时、分
常用的时间选择器,支持 年、月、日、时、分
- 时间类型type的设计:自由组合、随心所欲(当然应该是有意义的)
TYPE_YEAR | TYPE_MONTH | TYPE_DAY | TYPE_HOUR | TYPE_MINUTE
对比 Android-PickerView TimePickerView
/*** Android-PickerView中的设置type方法:参数设置麻烦且不易理解* 长度必须为6的数组,表示年月日时分秒 的显示与否,不设置则默认全部显示*/setType(boolean[] type)// 本项目设置type方法:简单易懂,组合方便setType(TYPE_DATE | TYPE_HOUR)
- 完美支持时间区间设置以及选中联动
- 支持Format,如显示今年,明年
API
api | description |
---|---|
type | 时间类型,需要在Builder构造方法中指定,不能改变 |
OnTimeSelectListener | 选中时间回调,需要在Builder构造方法中指定,不能改变 |
setRangDate | 设置起止时间 |
setSelectedDate | 设置选中时间戳 |
setInterceptor | 设置拦截器 |
setFormatter | 设置Formatter,内部提供默认的Formatter |
create | 通过Builder构建 TimePicker |
以上是TimePicker.Builder的,下面是TimePicker的 | |
setFormatter | 同上 |
setSelectedDate | 同上 |
getType | 获取type |
hasType | 判断是否包含某种type |
Formatter
TimePicker Formatter:用于根据type和num格式化时间文案
public interface Formatter {/*** 根据type和num格式化时间** @param picker picker* @param type 并不是模式,而是当前item所属的type,如年,时* @param position position* @param num position item显示的数字*/CharSequence format(TimePicker picker, int type, int position, int num);}
内部提供默认的 Formatter实现DefaultFormatter。用户可以设置自定义Formatter或继承DefaultFormatter进行扩展。
TimePicker初始化,如果未设置时间区间,会使用默认区间。三种Picker都采用Builder模式初始化。且用户自定义的Picker也应该采用这种模式进行初始化。
Simple Example
mTimePicker = new TimePicker.Builder(mActivity, type, this)// 设置时间区间.setRangDate(1526361240000L, 1893563460000L)// 设置选中时间//.setSelectedDate()// 设置pickerview样式.setInterceptor(new BasePicker.Interceptor() {@Override public void intercept(PickerView pickerView) {pickerView.setVisibleItemCount(5);// 将年月设置为循环的int type = (int) pickerView.getTag();if (type == TimePicker.TYPE_YEAR || type == TimePicker.TYPE_MONTH) {pickerView.setIsCirculation(true);}}})// 设置 Formatter.setFormatter(new TimePicker.DefaultFormatter() {// 自定义Formatter显示去年,今年,明年@Override public CharSequence format(TimePicker picker, int type, int position, int num) {if (type == TimePicker.TYPE_YEAR) {int offset = num - mCurrYear;if (offset == -1) return "去年";if (offset == 0) return "今年";if (offset == 1) return "明年";return num + "年";}return super.format(picker, type, position, num);}}).create();//mTimePicker.setSelectedDate(1549349843000L);mTimePicker.show();
MixedTimePicker
常用的聚合时间选择器。日期(年、月、日)聚合,时间(小时、分钟)聚合。
- 混合模式:github上的TimePicker库基本都不提供该种类型的Picker
- 支持自定义日期格式,时间格式
- 支持设置时间间隔
- 支持设置区间以及选中联动
- 支持设置纯日期,纯时间模式,采用type同TimePicker
API
api | description |
---|---|
type | 类型,需要在Builder构造方法中指定,不能改变 |
OnTimeSelectListener | 选中时间回调,需要在Builder构造方法中指定,不能改变 |
setRangDate | 设置起止时间 |
setSelectedDate | 设置选中时间戳 |
setTimeMinuteOffset | 设置时间间隔分钟数(60%offset==0才有效),以0为起始边界 |
setContainsStarDate | 设置mTimeMinuteOffset作用时,是否包含超出的startDate |
setContainsEndDate | 设置mTimeMinuteOffset作用时,是否包含超出的endDate |
setInterceptor | 设置拦截器 |
setFormatter | 设置Formatter,内部提供默认的Formatter |
create | 通过Builder构建 MixedTimePicker |
以上是MixedTimePicker.Builder的,下面是MixedTimePicker的 | |
setFormatter | 同上 |
setSelectedDate | 同上 |
getType | 获取type |
hasType | 判断是否包含某种type |
Formatter
MixedTimePicker Formatter:用于自定义日期和时间格式。内部提供默认的 Formatter实现。
public interface Formatter {/*** 用户可以自定义日期格式和时间格式** @param picker picker* @param date 当前状态对应的日期或者时间* @param position 当前type所在的position*/CharSequence format(MixedTimePicker picker, int type, Date date, int position);}
MixedTimePicker 的 Formatter 完美体现了Formatter设计的精妙之处。用户可以根据回调中的type和date自定义日期和时间格式。比如显示今天,或 xx月xx日 星期 x
Simple Example
mTimePicker = new MixedTimePicker.Builder(mActivity, MixedTimePicker.TYPE_ALL, this)// 设置不包含超出的结束时间<=.setContainsEndDate(false)// 设置时间间隔为30分钟.setTimeMinuteOffset(30).setRangDate(1517771651000L, 1577976666000L).setFormatter(new MixedTimePicker.DefaultFormatter() {@Overridepublic CharSequence format(MixedTimePicker picker, int type, Date date, int position) {if (type == MixedTimePicker.TYPE_DATE) {CharSequence text;int dayOffset = DateUtil.getDayOffset(date.getTime(), System.currentTimeMillis());if (dayOffset == 0) {text = "今天";} else if (dayOffset == 1) {text = "明天";} else { // xx月xx日 星期 xtext = mDateFormat.format(date);}return text;}return super.format(picker, type, date, position);}}).create();// 2018/2/5 03:14:11 - 2020/1/2 22:51:6Dialog pickerDialog = mTimePicker.getPickerDialog();pickerDialog.setCanceledOnTouchOutside(true);DefaultTopBar topBar = (DefaultTopBar) mTimePicker.getTopBar();topBar.getTitleView().setText("请选择时间");
不同于TimePicker, MixedTimePicker 由于支持纯时间模式(日期取选中时间的日期),<font color=red>不提供默认区间。如果模式中包含日期模式,则会强制要求设置时间区间</font>
OptionPicker
- 支持设置层级
- 构造数据源及其简单,只需要实现OptionDataSet接口
- 支持通过对应选中的values设置选中项。内部处理选中项逻辑,避免用户记录下标且麻烦的遍历处理
对比 Android-PickerView的 OptionsPickerView
function | Android-PickerViews | 本控件 |
---|---|---|
多级 | 最多支持3级(写死的) | 构造时设置级别(无限制) |
构造数据源 | 需要构建每一级的集合,二三级为嵌套 | 一级数据entity实现OptionDataSet接口即可 |
设置数据源 | 提供三个方法,分别用于一、二、三级的 | 只需要设置一级数据集 |
联动选中 |
提供三个,只能设置选中的下标。 需要用户自己通过多层遍历定位每一级别选中的下标,然后再设置 |
只需要传入选中的values(可变长数组),不需要任何计算 |
Android-PickerView 中的 OptionsPickerView 代码。由于不知道层级,所以每个方法都提供3个用来对应(最多)3级选择。
// 提供3个选中的方法,分别对应1,2,3级联动的情况public void setSelectOptions(int option1)public void setSelectOptions(int option1, int option2)public void setSelectOptions(int option1, int option2, int option3)// 提供3个设置数据源的方法,分别对应1,2,3级联动的情况public void setPicker(List<T> optionsItems)public void setPicker(List<T> options1Items, List<List<T>> options2Items)public void setPicker(List<T> options1Items, List<List<T>> options2Items,List<List<List<T>>> options3Items) {}
本库中的OptionPicker
/*** 根据选中的values初始化选中的position并初始化pickerview数据** @param options data* @param values 选中数据的value{@link OptionDataSet#getValue()}*/public void setDataWithValues(List<? extends OptionDataSet> options, String... values) {mOptions = options;setSelectedWithValues(values);}/*** 根据选中的values初始化选中的position** @param values 选中数据的value{@link OptionDataSet#getValue()},如果values[0]==null,则进行默认选中,其他为null认为没有该列*/public void setSelectedWithValues(String... values) {...}
如上面对比表格中所列举的,无论是层级,构造数据源和设置数据源,还是设置选中的选项,本库的API都十分简单,方便。
API
api | description |
---|---|
mHierarchy | 层级,需要在Builder构造方法中指定,不能改变 |
OnOptionSelectListener | 选中回调,需要在Builder构造方法中指定,不能改变 |
setInterceptor | 设置拦截器 |
setFormatter | 设置Formatter |
create | 通过Builder构建 OptionPicker |
以上是OptionPicker.Builder的,下面是OptionPicker的 | |
setFormatter | 同上 |
setDataWithValues |
根据选中的values初始化选中的position并初始化pickerview数据。 values参数为可变长数组,可以不设置。 |
setDataWithIndexs | 设置数据和选中position。不建议使用,建议使用 setDataWithValues |
setSelectedWithValues | 根据选中的values初始化选中的position |
setSelectedWithIndexs | 设置选中的position。不建议使用,建议使用 setSelectedWithValues |
getOptions | 获取数据集 |
getSelectedPosition | 获取选中的下标,数组size=mHierarchy,如果为-1表示该列没有数据 |
getSelectedOptions | 获取选中的选项,如果指定index为null则表示该列没有数据 |
需要注意的是:本库中的OptionPicker只用于联动的,不支持多级别且不联动。 基本没有这种需求,如果大家有这种需求,我会在后续迭代中支持。
Others
奇葩设计:部分default属性声明为static而非final
全局设置default属性
奇葩也好,亮点也罢。作为一个UI控件,不同的app,不同的UI,不同的产品自然会有不同的样式。
考虑到在一个app中我们会用到很多Picker,而我们又需要定制自己的UI的样式,如果通过动态方法设置样式就太麻烦了。
故做此设计。你可以通过配置这些static变量来快速定制一个满足自己app样式需求的Picker。
当然你也可以通过封装方法来处理PickerView,Picker,装饰器等样式,但这样一样十分麻烦。我相信你自己都会烦。
静态默认值
所有的这些静态属性值都以 sDefault 开头
- BasePickerView
field | description | defaultValue |
---|---|---|
sDefaultVisibleItemCount | 默认可见的item个数 | 5 |
sDefaultItemSize | 默认itemSize | 50(dp) |
sDefaultIsCirculation | 默认是否循环 | false |
- PickerView
field | description | defaultValue |
---|---|---|
sOutTextSize | default out text size | 18(dp) |
sCenterTextSize | default center text size | 22(dp) |
sCenterColor | default center text color | Color.BLUE |
sOutColor | default out text color | Color.GRAY |
- BasePicker
field | description | defaultValue |
---|---|---|
sDefaultPaddingRect | pickerView父容器的 default padding | null(无padding) |
sDefaultPickerBackgroundColor | default picker background color | Color.WHITE |
sDefaultTopBarCreator | 用于构建自定义defaultTopBar的接口 | null |
- DefaultCenterDecoration
field | description | defaultValue |
---|---|---|
sDefaultLineColor | default line color | Color.BLUE |
sDefaultLineWidth | default line width | 1(dp) |
sDefaultDrawable | default item background drawable | null |
sDefaultMarginRect | default line margin | null(无margin) |
建议初始化这些属性值放到Application中完成,避免app发生crash而导致失效
Simple Example
public class MyApplication extends Application {@Override public void onCreate() {super.onCreate();// 建议在application中初始化picker 默认属性实现全局设置initDefaultPicker();}private void initDefaultPicker() {// 利用修改静态默认属性值,快速定制一套满足自己app样式需求的Picker.// BasePickerViewPickerView.sDefaultVisibleItemCount = 3;PickerView.sDefaultItemSize = 50;PickerView.sDefaultIsCirculation = true;// PickerViewPickerView.sOutTextSize = 18;PickerView.sCenterTextSize = 18;PickerView.sCenterColor = Color.RED;PickerView.sOutColor = Color.GRAY;// BasePickerint padding = Util.dip2px(this, 20);BasePicker.sDefaultPaddingRect = new Rect(padding, padding, padding, padding);BasePicker.sDefaultPickerBackgroundColor = Color.WHITE;// 自定义 TopBarBasePicker.sDefaultTopBarCreator = new BasePicker.IDefaultTopBarCreator() {@Override public ITopBar createDefaultTopBar(LinearLayout parent) {return new CustomTopBar(parent);}};// DefaultCenterDecorationDefaultCenterDecoration.sDefaultLineWidth = 1;DefaultCenterDecoration.sDefaultLineColor = Color.RED;//DefaultCenterDecoration.sDefaultDrawable = new ColorDrawable(Color.WHITE);int leftMargin = Util.dip2px(this, 10);int topMargin = Util.dip2px(this, 2);DefaultCenterDecoration.sDefaultMarginRect =new Rect(leftMargin, -topMargin, leftMargin, -topMargin);}
}
Change Log
v1.0.0(2018-03-03)
- release v1.0.0
Gradle
compile 'org.jaaksi:pickerview:1.0.0@aar'
Thanks
- ScrollPickerView
- Android-PickerView
感谢你耐心的看完。欢迎大家提意见。
Github地址:https://github.com/jaaksi/pickerview
这可能是史上最好用的PickerView库了相关推荐
- 玩转Python必备:史上最全的Python库,【值得收藏,事半功倍】
库名称 简介 Chardet字符编码探测器,可以自动检测文本.网页.xml的编码. colorama主要用来给文本添加各种颜色,并且非常简单易用. Prettytable主要用于在终端或浏览器 ...
- 华为认证hcia含金量_【华为认证】HCIA-DATACOM史上最全精选题库(附答案解析)...
1.下列路由协议中优先级最高的是? (单选题) A.Static B.RIP C.OSPF D.Direct 参考答案:D 解析: 路由协议中,pre值越小,优先级越高 华为产品默认各路由协议优先级如 ...
- 【华为认证】HCIA-DATACOM史上最全精选题库续(附答案解析)
20.如下图所示,下列说法正确是? (单选题) 主机A和主机B的广播地址相同 主机A可以ping通主机B 主机A和主机B不能技取对方的MAC地址 主机A的ARP缓存中存在如下条目10.0.12. 5 ...
- 分享!史上最全的STM32库....
做了1年多的比赛,攒出来一套库,然后总结说明了一下,里面是以正点原子库为基础,包含泰庆.优信.龙邱和一些从网上大神的程序里面借鉴,还是相对比较全的库,适合入门级使用哈,精度中下等,需要高精度高要求需要 ...
- android 更新 18个月,MIUI迎接史上最大更新:18个月内绝不卡顿,友商羡慕!
原标题:MIUI迎接史上最大更新:18个月内绝不卡顿,友商羡慕! 大家都知道,早年间安卓手机比iPhone卡多了,尤其是iPhone1-4的时代,初始化的安卓手机UI流畅度都成问题.而随着近五年安卓系 ...
- 史上最强女游戏程序员
也许你听说过John Carmack 和Tim Sweeney等大牛的名字,而向来游戏工业都是阳盛阴衰,适逢国际妇女节,今天我为大家介绍游戏业界一位史上最强女游戏程序员:Corrinne Yu. 简历 ...
- 【云周刊】第146期:史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技...
摘要: 史上最大规模人机协同的双11,12位技术大V揭秘背后黑科技,INTERSPEECH 2017系列 | 语音识别之语言模型技术,机器学习初学者必须知道的十大算法,云数据库SQL Server 2 ...
- Windows遭遇史上最大攻击:微软却在疯狂圈粉
从本月中旬开始爆发的WannCry病毒让全球数十万PC感染,其中Windows XP.Windows 7成为重灾区. 原本想着如此严重的漏洞攻击肯定会让微软信誉扫地,导致大量用户逃离Windows系统 ...
- 史上世界上最惨烈的几次股灾!
史上世界上最惨烈的几次股灾! ●最惨烈的几次股灾 1美国股灾 1.1 1929年大股灾 1929年10月24日,星 期四.灾难的发生是毫无征兆的,开盘时,并没有出现什么值得注意的迹象,而且有一段 ...
最新文章
- 对于图像分类任务,相对于全连接的DNN,CNN模型的主要优点有哪些?
- java 封装 继承 堕胎_JAVA封装、继承、多态
- 【c语言】蓝桥杯算法提高 算术运算
- Linux_《Linux命令行与shell脚本编程大全》第十八章学习总结
- 新版Bintray-极简上传Library到JCenter
- HDFS HA介绍及配置理解
- MIR6校验时移动平均价为负的原因及解决
- NLP预训练模型学习全攻略(内附前沿论文解读直播)
- Linux——主流发行版本
- 反射机制----Method,Field,Constructor
- vue中使用 i18n 遇到的问题
- ASP.NET Core 2.0 全局配置项
- Mac下Apache使用
- Ios 被拒出现3.1.1
- java 内置jetty_内置jetty
- AI大咖们的18岁照,你能认对几个?
- 一个人独立完成一个网站上线的前前后后
- lingo求优化模型
- 这可能是最详细的Arduino制作4*4光立方教程(原创)
- 拿什么拯救你的硬盘?十大绝招帮你忙
热门文章
- DirectX的error LNK2019: 无法解析的外部符号 _Direct3DCreate9@4,该符号在函数 long __cdecl InitD3D(struct HWND__ *)
- 嵌入式开发--STM32上实现驱动注册initcall机制(类linux)
- fseek函数的用法(用于设定指针位置)
- 如何将字符串转换为日期– Java
- 七、BDB JE与BDB Java API
- C语言实现shell
- Android 自动化测试Monkey、Monkeyrunner
- WPF x:Key标签
- 研发流程:产品设计流程与文档规范
- Zotero翻译插件Zotero PDF Translate无法正常翻译,百度、彩云、腾讯翻译api接入