这可能是史上最好用的PickerView库了

    从去年开始大概已经有10个月没有更新博客了,去年结婚,也有了个小baby,导致这么长时间没有更新博客,从今天起博客正常更新,一切步入正轨。今天受一个好朋友的重托分享他的一篇文章:这可能是史上最好用的PickerView库了,同时也欢迎大家关注他的简书账号:笨小孩。下面开启“CV”模式!!!

如果你看到了这篇文章,希望你耐心的把它看完。我保证你认真看完了全部,一定不会让你失望。因为你可能遇到了史上最好用的PickerView库。 本文会通过和目前github上最受欢迎的PickerView库做详细的对比,通过对比你可以清晰的感受它的好用和强大。

    先来一下github地址方便那些喜欢直接看代码的同志们:https://github.com/jaaksi/pickerview.

    一个非常好用的Android PickerView库,内部提供3种常用类型的Picker。支持扩展自定义Picker。

  1. TimePicker:时间选择器,包含日期
  2. MixedTimePicker::聚合的时间选择器
  3. 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

apidescription
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

apidescription
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

apidescription
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

apidescription
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() {
        @Override
        public 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日 星期 x
              text = mDateFormat.format(date);
            }
            return text;
          }
          return super.format(picker, type, date, position);
        }
      })
      .create();
    // 2018/2/5 03:14:11 - 2020/1/2 22:51:6
    Dialog 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

functionAndroid-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

apidescription
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
fielddescriptiondefaultValue
sDefaultVisibleItemCount默认可见的item个数5
sDefaultItemSize默认itemSize50(dp)
sDefaultIsCirculation默认是否循环false
  • PickerView
fielddescriptiondefaultValue
sOutTextSizedefault out text size18(dp)
sCenterTextSizedefault center text size22(dp)
sCenterColordefault center text colorColor.BLUE
sOutColordefault out text colorColor.GRAY
  • BasePicker
fielddescriptiondefaultValue
sDefaultPaddingRectpickerView父容器的 default paddingnull(无padding)
sDefaultPickerBackgroundColordefault picker background colorColor.WHITE
sDefaultTopBarCreator用于构建自定义defaultTopBar的接口null
  • DefaultCenterDecoration
fielddescriptiondefaultValue
sDefaultLineColordefault line colorColor.BLUE
sDefaultLineWidthdefault line width1(dp)
sDefaultDrawabledefault item background drawablenull
sDefaultMarginRectdefault line marginnull(无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.
    // BasePickerView
    PickerView.sDefaultVisibleItemCount = 3;
    PickerView.sDefaultItemSize = 50;
    PickerView.sDefaultIsCirculation = true;

    // PickerView
    PickerView.sOutTextSize = 18;
    PickerView.sCenterTextSize = 18;
    PickerView.sCenterColor = Color.RED;
    PickerView.sOutColor = Color.GRAY;

    // BasePicker
    int padding = Util.dip2px(this, 20);
    BasePicker.sDefaultPaddingRect = new Rect(padding, padding, padding, padding);
    BasePicker.sDefaultPickerBackgroundColor = Color.WHITE;
    // 自定义 TopBar
    BasePicker.sDefaultTopBarCreator = new BasePicker.IDefaultTopBarCreator() {
      @Override public ITopBar createDefaultTopBar(LinearLayout parent) {
        return new CustomTopBar(parent);
      }
    };

    // DefaultCenterDecoration
    DefaultCenterDecoration.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

感谢你耐心的看完。欢迎大家提意见。
Github地址:https://github.com/jaaksi/pickerview

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
作者jaaksi,源码pickerview,一个非常好用的 Android PickerView ,内部提供 3 种常用类型的 Picker,支持扩展自定义 Picker:TimePicker:时间选择器,支持聚合模式的时间选择器(合并 v1.x 的 MixedTimePicker)OptionPicker:联动选择器效果图  APKDemo App下载连接PickerView READMEPicker通过组装 PickerView 实现常用的 Picker 选择器。上面已经列举提供的 3 中常用的 Picker。BasePickerPicker 基类:封装了 TopBar,PickerView 容器,create and add PickerView 方法,Picker 弹窗等方法。 三种 Picker 都继承自 BasePicker,你也可以继承它扩展自己的 Picker。APIapidescriptionsetPickerBackgroundColor设置 picker 背景setPadding设置 PickerView 父容器 padding 单位:pxsetTag给 Picker 设置 tag,用于区分不同的 picker 等。用法同 View setTaggetRootLayout获取 PickerView 的父容器,创建 DefaultTopBar 时必须指定setOnPickerChooseListener设置 picker 取消,确定按钮监听。可用于拦截选中操作setTopBar设置自定义 TopBarsetInterceptor设置拦截器createPickerView创建 PickerViewgetPickerViews获取 Picker 中所有的 pickerview 集合addPicker将创建的 PickerView 添加到上面集合中,createPickerView 内部已调用该方法findPickerViewByTag通过 tag 找到对应的 PickerViewisScrolling是否滚动未停止。滚动未停止的时候,不响应 Picker 的取消,确定按键getPickerDialog获取 Picker 弹窗。可以在 new 之后设置 dialog 属性show显示 picker 弹窗对比 github 上最受欢迎的同类 Android-PickerView ,本将 TopBar 等通用相关逻辑封装在基类中,并提供代码中创建 PickerView 方法,不需要再依赖 xml。用户自定义 Picker 时,继承 BasePicker,只需要处理自己的逻辑即可,简单便捷。 而对 Android-PickerView 来说,实现自定义 Picker,依然需要处理 TopBar 等逻辑,造成大量重复代码。TopBarTopBar: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 APIapidescriptionsetDividerColor设置 topbar bottom line colorsetDividerHeight设置 bottom divider line heightgetDivider获取 TopBar bottom linegetTitleView获取 TopBar title viewInterceptor拦截器:用于在 pickerview 创建时拦截,设置 pickerview 的属性。Picker 内部并不提供对 PickerView 的设置方法,而是通过 Interceptor 实现。这种设计用来实现 PickerPickerView 的属性设置完美解耦。   private void init(){     mTimePicker.setInterceptor(new 

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值