1. 程式人生 > >PickerView--仿ios滾輪時間選擇、城市選擇效果

PickerView--仿ios滾輪時間選擇、城市選擇效果

在專案開發中Android基本都是在跟隨ios的風格,前段時間產品經理就要求按照ios的效果,做時間、城市選擇效果,真要全部自己寫還真有點蛋疼,所以在網上一搜,效果不少,其中PickerView用的人還是蠻多的,所以就決定用PickerView來實現;

PickerView提供了:時間選擇器和選項選擇器

——TimePickerView 時間選擇器,支援年月日時分,年月日,年月,時分等格式。
——OptionsPickerView 選項選擇器,支援一,二,三級選項選擇,並且可以設定是否聯動 。

  • 支援三級聯動
  • 設定是否聯動
  • 設定迴圈模式
  • 支援自定義佈局。
  • 支援item的分隔線設定。
  • 支援item間距設定。
  • 時間選擇器支援起始和終止日期設定。
  • 支援“年,月,日,時,分,秒”,“省,市,區”等選項的單位(label)顯示、隱藏和自定義。
  • 支援自定義文字、顏色、文字大小等屬性
  • Item的文字長度過長時,文字會自適應縮放到Item的長度,避免顯示不完全的問題
  • 支援Dialog 模式。
  • 支援自定義設定容器。
  • 實時回撥。

PickerView地址

首先先將庫依賴到自己的專案中;

compile 'com.contrarywind:Android-PickerView:4.1.6'

先來看時間選擇器,時間選擇器涉及到TimePickerView和TimePickerBuilder,這裡採用了鏈式程式設計和build設計模式;在TimePickerBuilder中提供了一系列呼叫設定的方法;

public class TimePickerBuilder {
    private PickerOptions mPickerOptions;
    //Required
    public TimePickerBuilder(Context context, OnTimeSelectListener listener) {
        mPickerOptions = new PickerOptions(PickerOptions.TYPE_PICKER_TIME);
        mPickerOptions.context = context;
        mPickerOptions.timeSelectListener = listener;
    }

    /**
     * 設定位置
     * @param gravity
     * @return
     */
    public TimePickerBuilder setGravity(int gravity) {
        mPickerOptions.textGravity = gravity;
        return this;
    }


    /**
     * new boolean[]{true, true, true, false, false, false}
     * control the "year","month","day","hours","minutes","seconds " display or hide.
     * 分別控制“年”“月”“日”“時”“分”“秒”的顯示或隱藏。
     *
     * @param type 布林型陣列,長度需要設定為6。
     * @return TimePickerBuilder
     */
    public TimePickerBuilder setType(boolean[] type) {
        mPickerOptions.type = type;
        return this;
    }

    /**
     * 設定提交文字
     * @param textContentConfirm
     * @return
     */
    public TimePickerBuilder setSubmitText(String textContentConfirm) {
        mPickerOptions.textContentConfirm = textContentConfirm;
        return this;
    }

    /**
     * 是否是dialog
     * @param isDialog
     * @return
     */
    public TimePickerBuilder isDialog(boolean isDialog) {
        mPickerOptions.isDialog = isDialog;
        return this;
    }

    /**
     * 設定取消文字
     * @param textContentCancel
     * @return
     */
    public TimePickerBuilder setCancelText(String textContentCancel) {
        mPickerOptions.textContentCancel = textContentCancel;
        return this;
    }

    /**
     * 設定標題文字
     * @param textContentTitle
     * @return
     */
    public TimePickerBuilder setTitleText(String textContentTitle) {
        mPickerOptions.textContentTitle = textContentTitle;
        return this;
    }

    /**
     * 設定提交文字顏色
     * @param textColorConfirm
     * @return
     */
    public TimePickerBuilder setSubmitColor(int textColorConfirm) {
        mPickerOptions.textColorConfirm = textColorConfirm;
        return this;
    }

    /**
     * 設定取消文字顏色
     * @param textColorCancel
     * @return
     */
    public TimePickerBuilder setCancelColor(int textColorCancel) {
        mPickerOptions.textColorCancel = textColorCancel;
        return this;
    }

    /**
     * ViewGroup 型別的容器
     *
     * @param decorView 選擇器會被新增到此容器中
     * @return TimePickerBuilder
     */
    public TimePickerBuilder setDecorView(ViewGroup decorView) {
        mPickerOptions.decorView = decorView;
        return this;
    }

    /**
     * 設定背景顏色
     * @param bgColorWheel
     * @return
     */
    public TimePickerBuilder setBgColor(int bgColorWheel) {
        mPickerOptions.bgColorWheel = bgColorWheel;
        return this;
    }

    /**
     * 設定標題背景顏色
     * @param bgColorTitle
     * @return
     */
    public TimePickerBuilder setTitleBgColor(int bgColorTitle) {
        mPickerOptions.bgColorTitle = bgColorTitle;
        return this;
    }

    /**
     * 設定標題顏色
     * @param textColorTitle
     * @return
     */
    public TimePickerBuilder setTitleColor(int textColorTitle) {
        mPickerOptions.textColorTitle = textColorTitle;
        return this;
    }

    public TimePickerBuilder setSubCalSize(int textSizeSubmitCancel) {
        mPickerOptions.textSizeSubmitCancel = textSizeSubmitCancel;
        return this;
    }

    /**
     * 設定標題文字大小
     * @param textSizeTitle
     * @return
     */
    public TimePickerBuilder setTitleSize(int textSizeTitle) {
        mPickerOptions.textSizeTitle = textSizeTitle;
        return this;
    }

    /**
     * 設定當前文字大小
     * @param textSizeContent
     * @return
     */
    public TimePickerBuilder setContentTextSize(int textSizeContent) {
        mPickerOptions.textSizeContent = textSizeContent;
        return this;
    }

    /**
     * 因為系統Calendar的月份是從0-11的,所以如果是呼叫Calendar的set方法來設定時間,月份的範圍也要是從0-11
     *
     * @param date
     * @return TimePickerBuilder
     */
    public TimePickerBuilder setDate(Calendar date) {
        mPickerOptions.date = date;
        return this;
    }

    /**
     * 設定自定義佈局
     * @param res 資源佈局id
     * @param customListener 設定自定義佈局回撥
     * @return
     */
    public TimePickerBuilder setLayoutRes(int res, CustomListener customListener) {
        mPickerOptions.layoutRes = res;
        mPickerOptions.customListener = customListener;
        return this;
    }


    /**
     * 設定起始時間
     * 因為系統Calendar的月份是從0-11的,所以如果是呼叫Calendar的set方法來設定時間,月份的範圍也要是從0-11
     */

    public TimePickerBuilder setRangDate(Calendar startDate, Calendar endDate) {
        mPickerOptions.startDate = startDate;
        mPickerOptions.endDate = endDate;
        return this;
    }


    /**
     * 設定間距倍數,但是隻能在1.0-4.0f之間
     *
     * @param lineSpacingMultiplier
     */
    public TimePickerBuilder setLineSpacingMultiplier(float lineSpacingMultiplier) {
        mPickerOptions.lineSpacingMultiplier = lineSpacingMultiplier;
        return this;
    }

    /**
     * 設定分割線的顏色
     *
     * @param dividerColor
     */
    public TimePickerBuilder setDividerColor(int dividerColor) {
        mPickerOptions.dividerColor = dividerColor;
        return this;
    }

    /**
     * 設定分割線的型別
     *
     * @param dividerType
     */
    public TimePickerBuilder setDividerType(WheelView.DividerType dividerType) {
        mPickerOptions.dividerType = dividerType;
        return this;
    }

    /**
     * //顯示時的外部背景色顏色,預設是灰色
     *
     * @param backgroundId
     */

    public TimePickerBuilder setBackgroundId(int backgroundId) {
        mPickerOptions.backgroundId = backgroundId;
        return this;
    }

    /**
     * 設定分割線之間的文字的顏色
     *
     * @param textColorCenter
     */
    public TimePickerBuilder setTextColorCenter(int textColorCenter) {
        mPickerOptions.textColorCenter = textColorCenter;
        return this;
    }

    /**
     * 設定分割線以外文字的顏色
     *
     * @param textColorOut
     */
    public TimePickerBuilder setTextColorOut(int textColorOut) {
        mPickerOptions.textColorOut = textColorOut;
        return this;
    }

    public TimePickerBuilder isCyclic(boolean cyclic) {
        mPickerOptions.cyclic = cyclic;
        return this;
    }

    /**
     * 點選外部區域是否隱藏
     * @param cancelable
     * @return
     */
    public TimePickerBuilder setOutSideCancelable(boolean cancelable) {
        mPickerOptions.cancelable = cancelable;
        return this;
    }

    public TimePickerBuilder setLunarCalendar(boolean lunarCalendar) {
        mPickerOptions.isLunarCalendar = lunarCalendar;
        return this;
    }


    public TimePickerBuilder setLabel(String label_year, String label_month, String label_day, String label_hours, String label_mins, String label_seconds) {
        mPickerOptions.label_year = label_year;
        mPickerOptions.label_month = label_month;
        mPickerOptions.label_day = label_day;
        mPickerOptions.label_hours = label_hours;
        mPickerOptions.label_minutes = label_mins;
        mPickerOptions.label_seconds = label_seconds;
        return this;
    }

    /**
     * 設定X軸傾斜角度[ -90 , 90°]
     *
     * @param x_offset_year    年
     * @param x_offset_month   月
     * @param x_offset_day     日
     * @param x_offset_hours   時
     * @param x_offset_minutes 分
     * @param x_offset_seconds 秒
     * @return
     */
    public TimePickerBuilder setTextXOffset(int x_offset_year, int x_offset_month, int x_offset_day,
                                            int x_offset_hours, int x_offset_minutes, int x_offset_seconds) {
        mPickerOptions.x_offset_year = x_offset_year;
        mPickerOptions.x_offset_month = x_offset_month;
        mPickerOptions.x_offset_day = x_offset_day;
        mPickerOptions.x_offset_hours = x_offset_hours;
        mPickerOptions.x_offset_minutes = x_offset_minutes;
        mPickerOptions.x_offset_seconds = x_offset_seconds;
        return this;
    }

    public TimePickerBuilder isCenterLabel(boolean isCenterLabel) {
        mPickerOptions.isCenterLabel = isCenterLabel;
        return this;
    }

    /**
     * @param listener 切換item項滾動停止時,實時回撥監聽。
     * @return
     */
    public TimePickerBuilder setTimeSelectChangeListener(OnTimeSelectChangeListener listener) {
        mPickerOptions.timeSelectChangeListener = listener;
        return this;
    }

    /**
     * 呼叫build方法構建TimePickerView物件
     * @return
     */
    public TimePickerView build() {
        return new TimePickerView(mPickerOptions);
    }
}

在例項化時可以根據自己需要呼叫相應方法;先看下非自定義佈局的實現;

/**
     * 初始化時間滾輪控制元件
     */
    private void initTimePicker() {
        pvTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
            @Override
            public void onTimeSelect(Date date, View v) {
                //選擇回撥
                Toast.makeText(MainActivity.this,getTime(date),Toast.LENGTH_LONG).show();
            }
        })
                .setTimeSelectChangeListener(new OnTimeSelectChangeListener() {
                    @Override
                    public void onTimeSelectChanged(Date date) {
                        //時間滾動監控回撥
                        Log.e("pvTime-->",getTime(date));
                    }
                })
                .setType(new boolean[]{true, true, true, true, true, true,})
                .isDialog(true)//預設設定false ,內部實現將DecorView 作為它的父控制元件。
                .build();
        Dialog dialog = pvTime.getDialog();
        if(dialog!=null){
            FrameLayout.LayoutParams params=new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    Gravity.BOTTOM
            );
            params.leftMargin=0;
            params.rightMargin=0;
            pvTime.getDialogContainerLayout().setLayoutParams(params);
            Window dialogWindow = dialog.getWindow();
            if(dialogWindow!=null){
                //修改動畫樣式
                dialogWindow.setWindowAnimations(com.bigkoo.pickerview.R.style.picker_view_slide_anim);
                //改成Bottom,底部顯示
                dialogWindow.setGravity(Gravity.BOTTOM);
            }
        }
    }

呼叫上面這段程式碼,再呼叫show方法,效果就出來了;

公農曆切換效果:

/**
     * 初始化時間選擇器 公農曆切換
     * 時間範圍(1900-2100)
     */
    private void initLunarPicker() {
        //系統當前時間
        Calendar selectedDate = Calendar.getInstance();
        //開始時間
        Calendar startDate = Calendar.getInstance();
        startDate.set(1900, 1, 01);
        //結束時間
        Calendar endDate = Calendar.getInstance();
        endDate.set(2100, 2, 28);
        //時間選擇器 自定義佈局
        pvCustomLunar = new TimePickerBuilder(this, new OnTimeSelectListener() {
            @Override
            public void onTimeSelect(Date date, View v) {
                String time = getTime(date);
                //選中事件回撥
                Toast.makeText(MainActivity.this, time, Toast.LENGTH_LONG).show();
            }
        })
                .setDate(selectedDate)//設定選中的時間為系統當前時間
                .setRangDate(startDate, endDate)//設定時間返回
                .setLayoutRes(R.layout.pickerview_custom_lunar, new CustomListener() {
                    @Override
                    public void customLayout(final View v) {
                        //通過setLayoutRes設定自定義佈局效果
                        //在customLayout中進行邏輯處理
                        //完成
                        TextView tvFinish = v.findViewById(R.id.tv_finish);
                        tvFinish.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                //點選完成呼叫returnData方法會觸發onTimeSelect進行回撥
                                pvCustomLunar.returnData();
                                //隱藏佈局
                                pvCustomLunar.dismiss();
                            }
                        });
                        //取消
                        ImageView ivCancel = v.findViewById(R.id.iv_cancel);
                        ivCancel.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                //隱藏佈局
                                pvCustomLunar.dismiss();
                            }
                        });
                        //切換農曆
                        CheckBox cbLunar = v.findViewById(R.id.cb_lunar);
                        cbLunar.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                            @Override
                            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                                pvCustomLunar.setLunarCalendar(!pvCustomLunar.isLunarCalendar());
                                //自適應寬
                                setTimePickerChildWeight(v, isChecked ? 0.8f : 1f, isChecked ? 1f : 1.1f);
                            }
                        });
                    }
                })
                .setType(new boolean[]{true, true, true, false, false, false})//boolean陣列,設定條碼顯示 true顯示,false隱藏
                .isCenterLabel(false)
                .setDividerColor(Color.RED)//設定分割線顏色
                .build();
    }
/**
     * 公農曆切換後調整寬
     *
     * @param v
     * @param yearWeight
     * @param weight
     */
    private void setTimePickerChildWeight(View v, float yearWeight, float weight) {
        ViewGroup timePicker = (ViewGroup) v.findViewById(R.id.timepicker);
        View year = timePicker.getChildAt(0);
        LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams) year.getLayoutParams());
        lp.weight = yearWeight;
        year.setLayoutParams(lp);
        for (int i = 1; i < timePicker.getChildCount(); i++) {
            View childAt = timePicker.getChildAt(i);
            LinearLayout.LayoutParams childLp = ((LinearLayout.LayoutParams) childAt.getLayoutParams());
            childLp.weight = weight;
            childAt.setLayoutParams(childLp);
        }
    }

自定義時間選擇器佈局效果:

/**
 * @description
 *
 * 注意事項:
 * 1.自定義佈局中,id為 optionspicker 或者 timepicker 的佈局以及其子控制元件必須要有,否則會報空指標.
 * 具體可參考demo 裡面的兩個自定義layout佈局。
 * 2.因為系統Calendar的月份是從0-11的,所以如果是呼叫Calendar的set方法來設定時間,月份的範圍也要是從0-11
 * setRangDate方法控制起始終止時間(如果不設定範圍,則使用預設時間1900-2100年,此段程式碼可註釋)
 */
        Calendar selectedDate = Calendar.getInstance();//系統當前時間
        Calendar startDate = Calendar.getInstance();
        startDate.set(2014, 1, 23);
        Calendar endDate = Calendar.getInstance();
        endDate.set(2027, 2, 28);
        //時間選擇器 ,自定義佈局
        pvCustomTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
            @Override
            public void onTimeSelect(Date date, View v) {//選中事件回撥
                btnCustomTime.setText(getTime(date));
            }
        })
                .setDate(selectedDate)
                .setRangDate(startDate, endDate)
                .setLayoutRes(R.layout.pickerview_custom_time, new CustomListener() {

                    @Override
                    public void customLayout(View v) {
                        final TextView tvSubmit = (TextView) v.findViewById(R.id.tv_finish);
                        ImageView ivCancel = (ImageView) v.findViewById(R.id.iv_cancel);
                        tvSubmit.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                pvCustomTime.returnData();
                                pvCustomTime.dismiss();
                            }
                        });
                        ivCancel.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                pvCustomTime.dismiss();
                            }
                        });
                    }
                })
                .setContentTextSize(18)
                .setType(new boolean[]{false, false, false, true, true, true})
                .setLabel("年", "月", "日", "時", "分", "秒")
                .setLineSpacingMultiplier(1.2f)
                .setTextXOffset(0, 0, 0, 40, 0, -40)
                .isCenterLabel(false) //是否只顯示中間選中項的label文字,false則每項item全部都帶有label。
                .setDividerColor(0xFF24AD9D)
                .build();
    }
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#EEEEEE">
        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="#aaa" />
        <ImageView
            android:id="@+id/iv_cancel"
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="17dp"
            android:padding="8dp"
            android:src="@mipmap/to_down" />
        <TextView
            android:id="@+id/tv_finish"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="17dp"
            android:padding="8dp"
            android:text="完成"
            android:textColor="#24AD9D"
            android:textSize="18sp" />
        <View
            android:layout_width="match_parent"
            android:layout_height="0.5dp"
            android:background="#aaa" />
    </RelativeLayout>
    <!--此部分需要完整複製過去,刪減或者更改ID會導致初始化找不到內容而報空-->
    <LinearLayout
        android:id="@+id/timepicker"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:orientation="horizontal">
        <com.contrarywind.view.WheelView
            android:id="@+id/year"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1" />
        <com.contrarywind.view.WheelView
            android:id="@+id/month"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.1" />
        <com.contrarywind.view.WheelView
            android:id="@+id/day"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.1" />
        <com.contrarywind.view.WheelView
            android:id="@+id/hour"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.1" />
        <com.contrarywind.view.WheelView
            android:id="@+id/min"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.1" />
        <com.contrarywind.view.WheelView
            android:id="@+id/second"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1.1" />
    </LinearLayout>
</LinearLayout>

接下來看下條件選擇器的大致使用,條件選擇器例項化時主要涉及到OptionsPickerView和OptionsPickerBuilder,和時間選擇器一樣採用鏈式程式設計和build設計模式;

public class OptionsPickerBuilder {

    //配置類
    private PickerOptions mPickerOptions;


    //Required
    public OptionsPickerBuilder(Context context, OnOptionsSelectListener listener) {
        mPickerOptions = new PickerOptions(PickerOptions.TYPE_PICKER_OPTIONS);
        mPickerOptions.context = context;
        mPickerOptions.optionsSelectListener = listener;
    }

    //Option
    public OptionsPickerBuilder setSubmitText(String textContentConfirm) {
        mPickerOptions.textContentConfirm = textContentConfirm;
        return this;
    }

    public OptionsPickerBuilder setCancelText(String textContentCancel) {
        mPickerOptions.textContentCancel = textContentCancel;
        return this;
    }

    public OptionsPickerBuilder setTitleText(String textContentTitle) {
        mPickerOptions.textContentTitle = textContentTitle;
        return this;
    }

    public OptionsPickerBuilder isDialog(boolean isDialog) {
        mPickerOptions.isDialog = isDialog;
        return this;
    }

    public OptionsPickerBuilder setSubmitColor(int textColorConfirm) {
        mPickerOptions.textColorConfirm = textColorConfirm;
        return this;
    }

    public OptionsPickerBuilder setCancelColor(int textColorCancel) {
        mPickerOptions.textColorCancel = textColorCancel;
        return this;
    }

    /**
     * 顯示時的外部背景色顏色,預設是灰色
     *
     * @param backgroundId color resId.
     * @return
     */
    public OptionsPickerBuilder setBackgroundId(int backgroundId) {
        mPickerOptions.backgroundId = backgroundId;
        return this;
    }

    /**
     * ViewGroup 型別
     * 設定PickerView的顯示容器
     *
     * @param decorView Parent View.
     * @return
     */
    public OptionsPickerBuilder setDecorView(ViewGroup decorView) {
        mPickerOptions.decorView = decorView;
        return this;
    }

    public OptionsPickerBuilder setLayoutRes(int res, CustomListener listener) {
        mPickerOptions.layoutRes = res;
        mPickerOptions.customListener = listener;
        return this;
    }

    public OptionsPickerBuilder setBgColor(int bgColorWheel) {
        mPickerOptions.bgColorWheel = bgColorWheel;
        return this;
    }

    public OptionsPickerBuilder setTitleBgColor(int bgColorTitle) {
        mPickerOptions.bgColorTitle = bgColorTitle;
        return this;
    }

    public OptionsPickerBuilder setTitleColor(int textColorTitle) {
        mPickerOptions.textColorTitle = textColorTitle;
        return this;
    }

    public OptionsPickerBuilder setSubCalSize(int textSizeSubmitCancel) {
        mPickerOptions.textSizeSubmitCancel = textSizeSubmitCancel;
        return this;
    }

    public OptionsPickerBuilder setTitleSize(int textSizeTitle) {
        mPickerOptions.textSizeTitle = textSizeTitle;
        return this;
    }

    public OptionsPickerBuilder setContentTextSize(int textSizeContent) {
        mPickerOptions.textSizeContent = textSizeContent;
        return this;
    }

    public OptionsPickerBuilder setOutSideCancelable(boolean cancelable) {
        mPickerOptions.cancelable = cancelable;
        return this;
    }


    public OptionsPickerBuilder setLabels(String label1, String label2, String label3) {
        mPickerOptions.label1 = label1;
        mPickerOptions.label2 = label2;
        mPickerOptions.label3 = label3;
        return this;
    }

    /**
     * 設定Item 的間距倍數,用於控制 Item 高度間隔
     *
     * @param lineSpacingMultiplier 浮點型,1.0-4.0f 之間有效,超過則取極值。
     */
    public OptionsPickerBuilder setLineSpacingMultiplier(float lineSpacingMultiplier) {
        mPickerOptions.lineSpacingMultiplier = lineSpacingMultiplier;
        return this;
    }

    /**
     * Set item divider line type color.
     *
     * @param dividerColor color resId.
     */
    public OptionsPickerBuilder setDividerColor(int dividerColor) {
        mPickerOptions.dividerColor = dividerColor;
        return this;
    }

    /**
     * Set item divider line type.
     *
     * @param dividerType enum Type {@link WheelView.DividerType}
     */
    public OptionsPickerBuilder setDividerType(WheelView.DividerType dividerType) {
        mPickerOptions.dividerType = dividerType;
        return this;
    }

    /**
     * Set the textColor of selected item.
     *
     * @param textColorCenter color res.
     */
    public OptionsPickerBuilder setTextColorCenter(int textColorCenter) {
        mPickerOptions.textColorCenter = textColorCenter;
        return this;
    }

    /**
     * Set the textColor of outside item.
     *
     * @param textColorOut color resId.
     */
    public OptionsPickerBuilder setTextColorOut(int textColorOut) {
        mPickerOptions.textColorOut = textColorOut;
        return this;
    }

    public OptionsPickerBuilder setTypeface(Typeface font) {
        mPickerOptions.font = font;
        return this;
    }

    public OptionsPickerBuilder setCyclic(boolean cyclic1, boolean cyclic2, boolean cyclic3) {
        mPickerOptions.cyclic1 = cyclic1;
        mPickerOptions.cyclic2 = cyclic2;
        mPickerOptions.cyclic3 = cyclic3;
        return this;
    }

    public OptionsPickerBuilder setSelectOptions(int option1) {
        mPickerOptions.option1 = option1;
        return this;
    }

    public OptionsPickerBuilder setSelectOptions(int option1, int option2) {
        mPickerOptions.option1 = option1;
        mPickerOptions.option2 = option2;
        return this;
    }

    public OptionsPickerBuilder setSelectOptions(int option1, int option2, int option3) {
        mPickerOptions.option1 = option1;
        mPickerOptions.option2 = option2;
        mPickerOptions.option3 = option3;
        return this;
    }

    public OptionsPickerBuilder setTextXOffset(int xoffset_one, int xoffset_two, int xoffset_three) {
        mPickerOptions.x_offset_one = xoffset_one;
        mPickerOptions.x_offset_two = xoffset_two;
        mPickerOptions.x_offset_three = xoffset_three;
        return this;
    }

    public OptionsPickerBuilder isCenterLabel(boolean isCenterLabel) {
        mPickerOptions.isCenterLabel = isCenterLabel;
        return this;
    }

    /**
     * 切換選項時,是否還原第一項
     *
     * @param isRestoreItem true:還原; false: 保持上一個選項
     * @return TimePickerBuilder
     */
    public OptionsPickerBuilder isRestoreItem(boolean isRestoreItem) {
        mPickerOptions.isRestoreItem = isRestoreItem;
        return this;
    }

    /**
     * @param listener 切換item項滾動停止時,實時回撥監聽。
     * @return
     */
    public OptionsPickerBuilder setOptionsSelectChangeListener(OnOptionsSelectChangeListener listener) {
        mPickerOptions.optionsSelectChangeListener = listener;
        return this;
    }


    public <T> OptionsPickerView<T> build() {
        return new OptionsPickerView<>(mPickerOptions);
    }
}

OptionsPickerBuilder提供了一系列設定方法;根據效果的需要呼叫相應的方法;

先看一般的條件選擇器的效果:

/**
     * 初始化條件選擇器
     */
    private void initOptionPicker(){
        pvOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int options2, int options3, View v) {
                //返回的分別是三個級別的選中位置
                String tx = options1Items.get(options1).getPickerViewText()
                        + options2Items.get(options1).get(options2);
                btnOptions.setText(tx);
            }
        })
                .setTitleText("城市選擇")//設定標題
                .setContentTextSize(20)//設定滾輪文字大小
                .setDividerColor(Color.LTGRAY)//設定分割線的顏色
                .setSelectOptions(0, 1)//預設選中項
                .setBgColor(Color.BLACK)//設定背景色
                .setTitleBgColor(Color.DKGRAY)//設定標題背景色
                .setTitleColor(Color.LTGRAY)//設定標題顏色
                .setCancelColor(Color.YELLOW)//設定取消字型顏色
                .setSubmitColor(Color.YELLOW)//設定提交字型顏色
                .setTextColorCenter(Color.LTGRAY)//設定中間字型顏色
                .isRestoreItem(true)//切換時是否還原,設定預設選中第一項。
                .isCenterLabel(false) //是否只顯示中間選中項的label文字,false則每項item全部都帶有label。
                .setLabels("省", "市", "區")
                .setBackgroundId(0x00000000) //設定外部遮罩顏色
                .setOptionsSelectChangeListener(new OnOptionsSelectChangeListener() {
                    @Override
                    public void onOptionsSelectChanged(int options1, int options2, int options3) {
                        String str = "options1: " + options1 + "\noptions2: " + options2 + "\noptions3: " + options3;
                        Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
                    }
                })
                .build();

//        pvOptions.setSelectOptions(1,1);
        /*pvOptions.setPicker(options1Items);//一級選擇器*/
        pvOptions.setPicker(options1Items, options2Items);//二級選擇器
        /*pvOptions.setPicker(options1Items, options2Items,options3Items);//三級選擇器*/
    }

條件選擇器自定義佈局效果:
/**
     * 條件選擇器初始化,自定義佈局
     */
    private void initCustomOptionPicker() {
        /**
         * @description
         *
         * 注意事項:
         * 自定義佈局中,id為 optionspicker 或者 timepicker 的佈局以及其子控制元件必須要有,否則會報空指標。
         * 具體可參考demo 裡面的兩個自定義layout佈局。
         */
        pvCustomOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int option2, int options3, View v) {
                //返回的分別是三個級別的選中位置
                String tx = cardItem.get(options1).getPickerViewText();
                btnCustomOptions.setText(tx);
            }
        })
                .setLayoutRes(R.layout.pickerview_custom_options, new CustomListener() {
                    @Override
                    public void customLayout(View v) {
                        final TextView tvSubmit = (TextView) v.findViewById(R.id.tv_finish);
                        final TextView tvAdd = (TextView) v.findViewById(R.id.tv_add);
                        ImageView ivCancel = (ImageView) v.findViewById(R.id.iv_cancel);
                        tvSubmit.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                pvCustomOptions.returnData();
                                pvCustomOptions.dismiss();
                            }
                        });

                        ivCancel.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                pvCustomOptions.dismiss();
                            }
                        });

                        tvAdd.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                getCardData();
                                pvCustomOptions.setPicker(cardItem);
                            }
                        });
                    }
                })
                .isDialog(true)
                .build();
        pvCustomOptions.setPicker(cardItem);//新增資料
    }

條件選擇(不聯動效果):

/**
     * 不聯動的多級選項
     */
    private void initNoLinkOptionsPicker() {
        pvNoLinkOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {

            @Override
            public void onOptionsSelect(int options1, int options2, int options3, View v) {

                String str = "food:" + food.get(options1)
                        + "\nclothes:" + clothes.get(options2)
                        + "\ncomputer:" + computer.get(options3);

                Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
            }
        })
                .setOptionsSelectChangeListener(new OnOptionsSelectChangeListener() {
                    @Override
                    public void onOptionsSelectChanged(int options1, int options2, int options3) {
                        String str = "options1: " + options1 + "\noptions2: " + options2 + "\noptions3: " + options3;
                        Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
                    }
                })
                // .setSelectOptions(0, 1, 1)
                .build();
        pvNoLinkOptions.setNPicker(food, clothes, computer);
        pvNoLinkOptions.setSelectOptions(0, 1, 1);
    }

最後來看下城市地址選擇效果的實現:

public class JsonDataActivity extends AppCompatActivity implements View.OnClickListener {
    private ArrayList<JsonBean> options1Items = new ArrayList<>();
    private ArrayList<ArrayList<String>> options2Items = new ArrayList<>();
    private ArrayList<ArrayList<ArrayList<String>>> options3Items = new ArrayList<>();
    private Thread thread;
    private static final int MSG_LOAD_DATA = 0x0001;
    private static final int MSG_LOAD_SUCCESS = 0x0002;
    private static final int MSG_LOAD_FAILED = 0x0003;
    private boolean isLoaded = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_json_data);
        initView();
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_LOAD_DATA:
                    if (thread == null) {//如果已建立就不再重新建立子執行緒了
                        Toast.makeText(JsonDataActivity.this, "Begin Parse Data", Toast.LENGTH_SHORT).show();
                        thread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                // 子執行緒中解析省市區資料
                                initJsonData();
                            }
                        });
                        thread.start();
                    }
                    break;

                case MSG_LOAD_SUCCESS:
                    Toast.makeText(JsonDataActivity.this, "Parse Succeed", Toast.LENGTH_SHORT).show();
                    isLoaded = true;
                    break;

                case MSG_LOAD_FAILED:
                    Toast.makeText(JsonDataActivity.this, "Parse Failed", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    private void initView() {
        findViewById(R.id.btn_data).setOnClickListener(this);
        findViewById(R.id.btn_show).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_data:
                mHandler.sendEmptyMessage(MSG_LOAD_DATA);
                break;
            case R.id.btn_show:
                if (isLoaded) {
                    showPickerView();
                } else {
                    Toast.makeText(JsonDataActivity.this, "Please waiting until the data is parsed", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
    private void showPickerView() {// 彈出選擇器
        OptionsPickerView pvOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int options2, int options3, View v) {
                //返回的分別是三個級別的選中位置
                String tx = options1Items.get(options1).getPickerViewText() +
                        options2Items.get(options1).get(options2) +
                        options3Items.get(options1).get(options2).get(options3);

                Toast.makeText(JsonDataActivity.this, tx, Toast.LENGTH_SHORT).show();
            }
        })
                .setTitleText("城市選擇")
                .setDividerColor(Color.BLACK)
                .setTextColorCenter(Color.BLACK) //設定選中項文字顏色
                .setContentTextSize(20)
                .build();

        /*pvOptions.setPicker(options1Items);//一級選擇器
        pvOptions.setPicker(options1Items, options2Items);//二級選擇器*/
        pvOptions.setPicker(options1Items, options2Items, options3Items);//三級選擇器
        pvOptions.show();
    }

    private void initJsonData() {//解析資料

        /**
         * 注意:assets 目錄下的Json檔案僅供參考,實際使用可自行替換檔案
         * 關鍵邏輯在於迴圈體
         *
         * */
        String JsonData = new GetJsonDataUtil().getJson(this, "province.json");//獲取assets目錄下的json檔案資料

        ArrayList<JsonBean> jsonBean = parseData(JsonData);//用Gson 轉成實體

        /**
         * 新增省份資料
         *
         * 注意:如果是新增的JavaBean實體,則實體類需要實現 IPickerViewData 介面,
         * PickerView會通過getPickerViewText方法獲取字串顯示出來。
         */
        options1Items = jsonBean;

        for (int i = 0; i < jsonBean.size(); i++) {//遍歷省份
            ArrayList<String> CityList = new ArrayList<>();//該省的城市列表(第二級)
            ArrayList<ArrayList<String>> Province_AreaList = new ArrayList<>();//該省的所有地區列表(第三極)

            for (int c = 0; c < jsonBean.get(i).getCityList().size(); c++) {//遍歷該省份的所有城市
                String CityName = jsonBean.get(i).getCityList().get(c).getName();
                CityList.add(CityName);//新增城市
                ArrayList<String> City_AreaList = new ArrayList<>();//該城市的所有地區列表

                //如果無地區資料,建議新增空字串,防止資料為null 導致三個選項長度不匹配造成崩潰
                if (jsonBean.get(i).getCityList().get(c).getArea() == null
                        || jsonBean.get(i).getCityList().get(c).getArea().size() == 0) {
                    City_AreaList.add("");
                } else {
                    City_AreaList.addAll(jsonBean.get(i).getCityList().get(c).getArea());
                }
                Province_AreaList.add(City_AreaList);//新增該省所有地區資料
            }

            /**
             * 新增城市資料
             */
            options2Items.add(CityList);

            /**
             * 新增地區資料
             */
            options3Items.add(Province_AreaList);
        }
        mHandler.sendEmptyMessage(MSG_LOAD_SUCCESS);
    }


    public ArrayList<JsonBean> parseData(String result) {//Gson 解析
        ArrayList<JsonBean> detail = new ArrayList<>();
        try {
            JSONArray data = new JSONArray(result);
            Gson gson = new Gson();
            for (int i = 0; i < data.length(); i++) {
                JsonBean entity = gson.fromJson(data.optJSONObject(i).toString(), JsonBean.class);
                detail.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
            mHandler.sendEmptyMessage(MSG_LOAD_FAILED);
        }
        return detail;
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mHandler != null) {
            mHandler.removeCallbacksAndMessages(null);
        }
    }
}

原始碼地址