PickerView 时间/城市联动选择器TimePickerBuilder+OptionsPickerBuilder

本文详细介绍如何使用Android-PickerView库定制时间与条件选择器,包括个性化布局、样式调整及事件监听等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//时间选择器库
implementation 'com.contrarywind:Android-PickerView:4.1.6'

//1  时间显示

//private TimePickerView pvTime;
/**
     * 生日选择器
     */
    private void initLunarPicker() {
        Calendar selectedDate = Calendar.getInstance();//系统当前时间
        selectedDate.set(1990, 1, 1);
        Calendar startDate = Calendar.getInstance();
        startDate.set(1970, 1, 1);
        Calendar endDate = Calendar.getInstance();
        //endDate.set(2069, 2, 28);

        pvTime = new TimePickerBuilder(this, (date, v) -> {//选中事件回调
            birth = date;
            tvBirthday.setText(TimeUtils.getYMDFormat(date));
//                mApplyBlindDateBir.setText(new SimpleDateFormat("yyyy-MM-dd").format(date));
        }).setDate(selectedDate)
                .setRangDate(startDate, endDate)
                .setContentTextSize(18)
                .setType(new boolean[]{true, true, true, false, false, false})
                .setLabel("年", "月", "日", null, null, null)
                .setLineSpacingMultiplier(1.2f)
                .setTextXOffset(0, 0, 0, 40, 0, -40)
                .isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。
                .build();
    }


 if (pvTime != null) {
                    pvTime.show();
                }

//2 条件选择

//条件选择器
 OptionsPickerView pvOptions = new OptionsPickerBuilder(MainActivity.this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int option2, int options3 ,View v) {
                //返回的分别是三个级别的选中位置
                String tx = options1Items.get(options1).getPickerViewText()
                        + options2Items.get(options1).get(option2)
                        + options3Items.get(options1).get(option2).get(options3).getPickerViewText();
                tvOptions.setText(tx);
            }
        }).build();
 pvOptions.setPicker(options1Items, options2Items, options3Items);
 pvNoLinkOptions.setSelectOptions(1, 1);
 pvOptions.show(); 

https://www.cnblogs.com/whycxb/p/9163456.html

比较好的讲解链接

效果图

使用步骤

一、项目组织结构图

package com.why.project.pickerviewdemo;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.method.ScrollingMovementMethod;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.bigkoo.pickerview.builder.OptionsPickerBuilder;
import com.bigkoo.pickerview.builder.TimePickerBuilder;
import com.bigkoo.pickerview.listener.OnOptionsSelectListener;
import com.bigkoo.pickerview.listener.OnTimeSelectListener;
import com.bigkoo.pickerview.view.OptionsPickerView;
import com.bigkoo.pickerview.view.TimePickerView;
import com.why.project.pickerviewdemo.bean.SpinnearBean;
import com.why.project.pickerviewdemo.util.DateTimeHelper;

import org.json.JSONArray;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

    private TextView hobbyTv;//选择爱好
    /**爱好列表集合*/
    private ArrayList<SpinnearBean> mHobbyList;
    private ArrayList<String> mHobbyNameList;//用于选择器显示
    private OptionsPickerView mHobbyPickerView;//选择器

    private TextView addressTv;//选择地址
    /**地址列表集合*/
    private ArrayList<SpinnearBean> mAddressList;
    private ArrayList<String> mAddressNameList;//用于选择器显示
    private OptionsPickerView mAddressPickerView;//选择器

    private TextView startdateTv;//开始日期
    private TimePickerView mStartDatePickerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initViews();
        initDatas();
        initEvents();
    }

    private void initViews() {
        hobbyTv = findViewById(R.id.hobbyTv);
        addressTv = (TextView) findViewById(R.id.addressTv);
        //实现文本区域可滑动的效果,用于当显示的文本过长时
        addressTv.setMovementMethod(ScrollingMovementMethod.getInstance());//实现可滑动,但是和ScrollView滑动冲突,需要处理下
        //https://blog.youkuaiyun.com/qq_36070190/article/details/70053228
        addressTv.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                if(event.getAction()==MotionEvent.ACTION_DOWN){
                    //通知父控件不要干扰
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                }
                if(event.getAction()==MotionEvent.ACTION_MOVE){
                    //通知父控件不要干扰
                    v.getParent().requestDisallowInterceptTouchEvent(true);
                }
                if(event.getAction()==MotionEvent.ACTION_UP){
                    v.getParent().requestDisallowInterceptTouchEvent(false);
                }
                return false;
            }
        });
        startdateTv = findViewById(R.id.startdateTv);
    }

    private void initDatas() {
        //========================================初始化爱好列表集合========================================
        mHobbyList = new ArrayList<SpinnearBean>();
        mHobbyNameList = new ArrayList<String>();

        //模拟获取数据集合
        try{
            mHobbyList = parseJsonArray("spinners.txt");
        }catch (Exception e) {
            e.printStackTrace();
        }
        for(SpinnearBean spinnearBean : mHobbyList){
            mHobbyNameList.add(spinnearBean.getParaName());
        }

        //============初始化选择器============
        initHobbyOptionPicker();
        //如果想要直接赋值的话,这样写
        /*if(mHobbyNameList.size() > 0){
            hobbyTv.setText(mHobbyNameList.get(0));//默认展现第一个
        }*/

        //========================================初始化地址列表集合========================================
        mAddressList = new ArrayList<SpinnearBean>();
        mAddressNameList = new ArrayList<String>();
        //模拟获取数据集合
        try{
            mAddressList = parseJsonArray("spinners2.txt");
        }catch (Exception e) {
            e.printStackTrace();
        }
        for(SpinnearBean spinnearBean : mAddressList){
            mAddressNameList.add(spinnearBean.getParaName());
        }
        //============初始化选择器============
        initAddressOptionPicker();
        //当选择器列表项文本过长时,直接赋值的话,会有问题:高度变小了。不太明白什么原因,所以需要延迟设置文本,实际过程中因为有网络请求,所以一般没有问题
        /*new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                if(mAddressNameList.size() > 0){
                    addressTv.setText(mAddressNameList.get(0));//默认展现第一个
                }
            }
        }, 500);*/

        //========================================初始化开始日期选择器控件========================================
        initStartTimePicker();//初始化开始日期选择器控件
    }

    //初始化爱好选择器
    private void initHobbyOptionPicker() {
        mHobbyPickerView = new OptionsPickerBuilder(MainActivity.this, new OnOptionsSelectListener() {
                @Override
                public void onOptionsSelect(int options1, int option2, int options3, View v) {
                        //返回的分别是三个级别的选中位置
                        String tx = mHobbyNameList.get(options1);
                        hobbyTv.setText(tx);
                    }
                })
                .setDecorView((RelativeLayout)findViewById(R.id.activity_rootview))//必须是RelativeLayout,不设置setDecorView的话,底部虚拟导航栏会显示在弹出的选择器区域
                .setTitleText("选择爱好")//标题文字
                .setTitleSize(20)//标题文字大小
                .setTitleColor(getResources().getColor(R.color.pickerview_title_text_color))//标题文字颜色
                .setCancelText("取消")//取消按钮文字
                .setCancelColor(getResources().getColor(R.color.pickerview_cancel_text_color))//取消按钮文字颜色
                .setSubmitText("确定")//确认按钮文字
                .setSubmitColor(getResources().getColor(R.color.pickerview_submit_text_color))//确定按钮文字颜色
                .setContentTextSize(20)//滚轮文字大小
                .setTextColorCenter(getResources().getColor(R.color.pickerview_center_text_color))//设置选中文本的颜色值
                .setLineSpacingMultiplier(1.8f)//行间距
                .setDividerColor(getResources().getColor(R.color.pickerview_divider_color))//设置分割线的颜色
                .setSelectOptions(0)//设置选择的值
                .build();

        mHobbyPickerView.setPicker(mHobbyNameList);//添加数据
    }

    //初始化地址选择器
    private void initAddressOptionPicker() {
        mAddressPickerView = new OptionsPickerBuilder(MainActivity.this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int option2, int options3, View v) {
                //返回的分别是三个级别的选中位置
                String tx = mAddressNameList.get(options1);
                addressTv.setText(tx);
            }
        })
                .setDecorView((RelativeLayout)findViewById(R.id.activity_rootview))//必须是RelativeLayout,不设置setDecorView的话,底部虚拟导航栏会显示在弹出的选择器区域
                .setTitleText("选择地址")//标题文字
                .setTitleSize(20)//标题文字大小
                .setTitleColor(getResources().getColor(R.color.pickerview_title_text_color))//标题文字颜色
                .setCancelText("取消")//取消按钮文字
                .setCancelColor(getResources().getColor(R.color.pickerview_cancel_text_color))//取消按钮文字颜色
                .setSubmitText("确定")//确认按钮文字
                .setSubmitColor(getResources().getColor(R.color.pickerview_submit_text_color))//确定按钮文字颜色
                .setContentTextSize(20)//滚轮文字大小
                .setTextColorCenter(getResources().getColor(R.color.pickerview_center_text_color))//设置选中文本的颜色值
                .setLineSpacingMultiplier(1.8f)//行间距
                .setDividerColor(getResources().getColor(R.color.pickerview_divider_color))//设置分割线的颜色
                .setSelectOptions(0)//设置选择的值
                .build();

        mAddressPickerView.setPicker(mAddressNameList);//添加数据
    }

    /**初始化开始日期选择器控件*/
    private void initStartTimePicker() {
        //控制时间范围(如果不设置范围,则使用默认时间1900-2100年,此段代码可注释)
        //因为系统Calendar的月份是从0-11的,所以如果是调用Calendar的set方法来设置时间,月份的范围也要是从0-11
        Calendar selectedDate = Calendar.getInstance();
        //设置最小日期和最大日期
        Calendar startDate = Calendar.getInstance();
        try {
            startDate.setTime(DateTimeHelper.parseStringToDate("1970-01-01"));//设置为2006年4月28日
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar endDate = Calendar.getInstance();//最大日期是今天

        //时间选择器
        mStartDatePickerView = new TimePickerBuilder(MainActivity.this, new OnTimeSelectListener() {
                    @Override
                    public void onTimeSelect(Date date, View v) {//选中事件回调
                        // 这里回调过来的v,就是show()方法里面所添加的 View 参数,如果show的时候没有添加参数,v则为null
                        startdateTv.setText(DateTimeHelper.formatToString(date,"yyyy-MM-dd"));
                    }
                })
                .setDecorView((RelativeLayout)findViewById(R.id.activity_rootview))//必须是RelativeLayout,不设置setDecorView的话,底部虚拟导航栏会显示在弹出的选择器区域
                //年月日时分秒 的显示与否,不设置则默认全部显示
                .setType(new boolean[]{true, true, true, false, false, false})
                .setLabel("", "", "", "", "", "")
                .isCenterLabel(false)//是否只显示中间选中项的label文字,false则每项item全部都带有label。
                .setTitleText("开始日期")//标题文字
                .setTitleSize(20)//标题文字大小
                .setTitleColor(getResources().getColor(R.color.pickerview_title_text_color))//标题文字颜色
                .setCancelText("取消")//取消按钮文字
                .setCancelColor(getResources().getColor(R.color.pickerview_cancel_text_color))//取消按钮文字颜色
                .setSubmitText("确定")//确认按钮文字
                .setSubmitColor(getResources().getColor(R.color.pickerview_submit_text_color))//确定按钮文字颜色
                .setContentTextSize(20)//滚轮文字大小
                .setTextColorCenter(getResources().getColor(R.color.pickerview_center_text_color))//设置选中文本的颜色值
                .setLineSpacingMultiplier(1.8f)//行间距
                .setDividerColor(getResources().getColor(R.color.pickerview_divider_color))//设置分割线的颜色
                .setRangDate(startDate, endDate)//设置最小和最大日期
                .setDate(selectedDate)//设置选中的日期
                .build();
    }

    private void initEvents() {
        //选择爱好的下拉菜单点击事件
        hobbyTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mHobbyPickerView.show();
            }
        });

        //选择地址的下拉菜单点击事件
        addressTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mAddressPickerView.show();
            }
        });

        //开始日期的下拉菜单点击事件
        startdateTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mStartDatePickerView.show();
            }
        });
    }





    /*===========读取assets目录下的js字符串文件(js数组和js对象),然后生成List集合===========*/
    public static final String LISTROOTNODE = "spinnerList";
    public static final String KEY_LISTITEM_NAME = "paraName";
    public static final String KEY_LISTITEM_VALUE = "paraValue";
    public static final String KEY_LISTITEM_CHECKCOLOR = "checkColor";

    /**
     * 解析JSON文件的简单数组
     */
    private ArrayList<SpinnearBean> parseJsonArray(String fileName) throws Exception{

        ArrayList<SpinnearBean> itemsList = new ArrayList<SpinnearBean>();

        String jsonStr = getStringFromAssert(MainActivity.this, fileName);
        if(jsonStr.equals("")){
            return null;
        }
        JSONObject allData = new JSONObject(jsonStr);  //全部内容变为一个项
        JSONArray jsonArr = allData.getJSONArray(LISTROOTNODE); //取出数组
        for(int x = 0;x<jsonArr.length();x++){
            SpinnearBean model = new SpinnearBean();
            JSONObject jsonobj = jsonArr.getJSONObject(x);
            model.setParaName(jsonobj.getString(KEY_LISTITEM_NAME));
            model.setParaValue(jsonobj.getString(KEY_LISTITEM_VALUE));
            if(jsonobj.has(KEY_LISTITEM_CHECKCOLOR)){
                model.setCheckColor(jsonobj.getString(KEY_LISTITEM_CHECKCOLOR));
            }
            model.setSelectedState(false);
            itemsList.add(model);
            model = null;
        }
        return itemsList;
    }

    /**
     * 访问assets目录下的资源文件,获取文件中的字符串
     * @param filePath - 文件的相对路径,例如:"listdata.txt"或者"/www/listdata.txt"
     * @return 内容字符串
     * */
    public String getStringFromAssert(Context mContext, String filePath) {

        String content = ""; // 结果字符串
        try {
            InputStream is = mContext.getResources().getAssets().open(filePath);// 打开文件
            int ch = 0;
            ByteArrayOutputStream out = new ByteArrayOutputStream(); // 实现了一个输出流
            while ((ch = is.read()) != -1) {
                out.write(ch); // 将指定的字节写入此 byte 数组输出流
            }
            byte[] buff = out.toByteArray();// 以 byte 数组的形式返回此输出流的当前内容
            out.close(); // 关闭流
            is.close(); // 关闭流
            content = new String(buff, "UTF-8"); // 设置字符串编码
        } catch (Exception e) {
            Toast.makeText(mContext, "对不起,没有找到指定文件!", Toast.LENGTH_SHORT)
                    .show();
        }
        return content;
    }
}

 

通过继承,反射,获取自己想要的样式

 <style name="divider_hor_gray">
        <item name="android:layout_height">1dp</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:background">@color/divider_gray</item>
    </style>
/**
 * 反射 options并修改样式   picker
 */
public class MyOptionsPickerBuilder extends OptionsPickerBuilder {
    //Required
    public MyOptionsPickerBuilder(Context context, OnOptionsSelectListener listener) {
        super(context,listener);
        // 通过这种方法可以得到父类的所有属性 -→ 先得到父类class 然后class调用getDeclaredFields()方法
        Class<?> superclass = getClass().getSuperclass();
        try {
            Field mFiled = superclass.getDeclaredField("mPickerOptions");
            mFiled.setAccessible(true);
            PickerOptions mPickerOptions = (PickerOptions) mFiled.get(this);
            //1布局修正
            mPickerOptions.layoutRes = R.layout.my_pickerview_options;
            //2最大显示条目修正
            mPickerOptions.itemsVisibleCount = 5;
            //3当行高度修正
            mPickerOptions.lineSpacingMultiplier = 3.0f;
            setTitleBgColor(context.getResources().getColor(R.color.white));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上边是OptionsPicker下面介绍TimePicker 自定义

/**
 * 扩展原来的布局时间选择器布局
 */
public class MyTimePickerBuilder extends TimePickerBuilder {
    private View customView;
    //Required
    public MyTimePickerBuilder(Context context, OnTimeSelectListener listener) {
        super(context,listener);
        setLayoutRes(R.layout.my_pickerview_time, v -> customView = v);
    }
    /**
     * 使用通用配置样式  *需要放构造方法后面
     */
    public MyTimePickerBuilder useCommonStyle(Context context){
        setItemVisibleCount(5);
        setLineSpacingMultiplier(3.0f);
        setTitleBgColor(context.getResources().getColor(R.color.white));
        setCancelColor(context.getResources().getColor(R.color.text_black));//取消按钮文字颜色
        setSubmitColor(context.getResources().getColor(R.color.text_black));//确定按钮文字颜色
        return this;
    }
    @Override
    public TimePickerView build() {
        TimePickerView pickerView = super.build();
        //使用自定义布局之后,不会再按照options去初始化,需要自己手动添加初始化代码
        if (customView != null)initCustomView(customView, pickerView);
        return pickerView;
    }

    /**
     *反射options 按照options去初始化
     * @param view 自定义view
     * @param listener  还原原来的布局初始化
     */
    private void initCustomView(@NonNull View view,View.OnClickListener listener){
        //顶部标题
        TextView tvTitle =  view.findViewById(R.id.tvTitle);
        RelativeLayout rv_top_bar =  view.findViewById(R.id.rv_topbar);

        //确定和取消按钮
        Button btnSubmit =  view.findViewById(R.id.btnSubmit);
        Button btnCancel =  view.findViewById(R.id.btnCancel);
        //TimePickerView 中一摸一样才行,取消,确定根据的是这两个tag 自定义View不设置会空指针
        String TAG_SUBMIT = "submit";
        String TAG_CANCEL = "cancel";
        btnSubmit.setTag(TAG_SUBMIT);
        btnCancel.setTag(TAG_CANCEL);

        btnSubmit.setOnClickListener(listener);
        btnCancel.setOnClickListener(listener);
       // 通过这种方法可以得到父类的所有属性 -→ 先得到父类class 然后class调用getDeclaredFields()方法
        Class<?> superclass = getClass().getSuperclass();
        try {
            if (superclass == null) return;
            Field mFiled = superclass.getDeclaredField("mPickerOptions");
            mFiled.setAccessible(true);
            PickerOptions mPickerOptions = (PickerOptions) mFiled.get(this);


            //设置文字
            btnSubmit.setText(TextUtils.isEmpty(mPickerOptions.textContentConfirm) ? mPickerOptions.context.getResources().getString(R.string.pickerview_submit) : mPickerOptions.textContentConfirm);
            btnCancel.setText(TextUtils.isEmpty(mPickerOptions.textContentCancel) ? mPickerOptions.context.getResources().getString(R.string.pickerview_cancel) : mPickerOptions.textContentCancel);
            tvTitle.setText(TextUtils.isEmpty(mPickerOptions.textContentTitle) ? "" : mPickerOptions.textContentTitle);//默认为空

            //设置color
            btnSubmit.setTextColor(mPickerOptions.textColorConfirm);
            btnCancel.setTextColor(mPickerOptions.textColorCancel);
            tvTitle.setTextColor(mPickerOptions.textColorTitle);
            rv_top_bar.setBackgroundColor(mPickerOptions.bgColorTitle);

            //设置文字大小
            btnSubmit.setTextSize(mPickerOptions.textSizeSubmitCancel);
            btnCancel.setTextSize(mPickerOptions.textSizeSubmitCancel);
            tvTitle.setTextSize(mPickerOptions.textSizeTitle);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

自定义布局

private void initCustomOptionPicker() {//条件选择器初始化,自定义布局
    /**
     * @description
     *
     * 注意事项:
     * 自定义布局中,id为 optionspicker 或者 timepicker 的布局以及其子控件必须要有,否则会报空指针。
     * 具体可参考demo 里面的两个自定义layout布局。
     */
    pvCustomOptions = new OptionsPickerView.Builder(this, new OptionsPickerView.OnOptionsSelectListener() {
        @Override
        public void onOptionsSelect(int options1, int option2, int options3, View v) {
            //返回的分别是三个级别的选中位置
            String tx = cardItem.get(options1).getPickerViewText();
            btn_CustomOptions.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);//添加数据
 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值