Android-PickerView 完全指南:时间选择器与省市区联动解决方案
1. 项目概述
Android-PickerView 是一个功能强大的 Android 选择器组件库,支持时间选择器(TimePicker)和选项选择器(OptionsPicker),特别适合实现省市区三级联动等复杂选择场景。该库提供了丰富的自定义选项和灵活的配置方式,能够满足各种界面需求。
1.1 核心功能
- 时间选择器(TimePickerView):支持年月日时分秒等多种时间格式选择
- 选项选择器(OptionsPickerView):支持一、二、三级联动选择
- 高度自定义:可自定义布局、颜色、文字大小、滚轮样式等
- 丰富的交互:支持循环滚动、联动效果、实时回调等功能
1.2 应用场景
2. 快速开始
2.1 环境准备
Android-PickerView 要求 Android API 级别 9+,兼容几乎所有现代 Android 设备。
2.2 安装方式
2.2.1 Gradle 依赖
dependencies {
implementation 'com.contrarywind:Android-PickerView:4.1.9'
}
2.2.2 源码集成
如果需要自定义修改或贡献代码,可以直接克隆仓库:
git clone https://gitcode.com/gh_mirrors/an/Android-PickerView.git
然后将 pickerview 和 wheelview 模块导入到你的 Android 项目中。
3. 核心组件详解
3.1 WheelView 基础控件
WheelView 是整个库的基础组件,提供了滚轮选择的核心功能。
// XML布局中添加
<com.contrarywind.view.WheelView
android:id="@+id/wheelview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// Java代码中配置
WheelView wheelView = findViewById(R.id.wheelview);
wheelView.setCyclic(false); // 是否循环滚动
wheelView.setAdapter(new ArrayWheelAdapter(mOptionsItems)); // 设置适配器
wheelView.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(int index) {
// 选中项变化回调
Toast.makeText(MainActivity.this, mOptionsItems.get(index), Toast.LENGTH_SHORT).show();
}
});
WheelView 主要方法:
| 方法 | 描述 |
|---|---|
setCyclic(boolean cyclic) | 设置是否循环滚动 |
setCurrentItem(int currentItem) | 设置当前选中项 |
setAdapter(WheelAdapter adapter) | 设置数据适配器 |
setOnItemSelectedListener(OnItemSelectedListener listener) | 设置选中项变化监听器 |
setItemsVisibleCount(int visibleCount) | 设置可见项数量 |
setAlphaGradient(boolean alphaGradient) | 设置是否启用透明度渐变效果 |
3.2 时间选择器(TimePickerView)
时间选择器用于选择日期和时间,支持多种时间格式和自定义选项。
3.2.1 基本用法
// 初始化时间选择器
Calendar selectedDate = Calendar.getInstance();
Calendar startDate = Calendar.getInstance();
startDate.set(2000, 0, 1);
Calendar endDate = Calendar.getInstance();
endDate.set(2030, 11, 31);
TimePickerView pvTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
@Override
public void onTimeSelect(Date date, View v) {
// 选中时间回调
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
String time = sdf.format(date);
Toast.makeText(MainActivity.this, time, Toast.LENGTH_SHORT).show();
}
})
.setType(new boolean[]{true, true, true, true, true, false}) // 年月日时分秒显示与否
.setRangDate(startDate, endDate) // 设置时间范围
.setTitleText("选择时间") // 设置标题
.build();
// 显示时间选择器
pvTime.show();
3.2.2 时间选择器配置项
TimePickerView pvTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
@Override
public void onTimeSelect(Date date, View v) {
// 时间选择回调
}
})
.setType(new boolean[]{true, true, true, false, false, false}) // 只显示年月日
.setCancelText("取消") // 取消按钮文字
.setSubmitText("确定") // 确认按钮文字
.setContentSize(18) // 滚轮文字大小
.setTitleSize(20) // 标题文字大小
.setTitleColor(Color.BLACK) // 标题文字颜色
.setSubmitColor(Color.BLUE) // 确认按钮文字颜色
.setCancelColor(Color.BLUE) // 取消按钮文字颜色
.setTitleBgColor(0xFFEEEEEE) // 标题背景颜色
.setBgColor(0xFFFFFFFF) // 滚轮背景颜色
.setDate(selectedDate) // 设置默认选中时间
.setRangDate(startDate, endDate) // 设置时间范围
.setLabel("年", "月", "日", "时", "分", "秒") // 设置单位标签
.isCenterLabel(false) // 是否只显示中间选中项的标签
.isCyclic(true) // 是否循环滚动
.setOutSideCancelable(true) // 点击外部是否取消
.isDialog(true) // 是否以对话框形式显示
.build();
3.3 选项选择器(OptionsPickerView)
选项选择器用于实现单项、多项或联动选择功能,最典型的应用是省市区三级联动。
3.3.1 基础用法
// 准备数据
List<String> options1Items = new ArrayList<>();
List<List<String>> options2Items = new ArrayList<>();
List<List<List<String>>> options3Items = new ArrayList<>();
// 初始化数据...
// 创建选项选择器
OptionsPickerView pvOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3, View v) {
// 返回的分别是三个级别的选中位置
String tx = options1Items.get(options1) +
options2Items.get(options1).get(option2) +
options3Items.get(options1).get(option2).get(options3);
Toast.makeText(MainActivity.this, tx, Toast.LENGTH_SHORT).show();
}
})
.setTitleText("城市选择")
.setCyclic(false, false, false)
.setSelectOptions(0, 0, 0) // 设置默认选中项
.build();
// 设置数据源
pvOptions.setPicker(options1Items, options2Items, options3Items);
// 显示选择器
pvOptions.show();
3.3.2 三级联动数据结构
三级联动需要的数据结构是:List<String>(一级)、List<List<String>>(二级)和 List<List<List<String>>>(三级)。
3.3.3 从JSON文件加载数据
// 使用工具类加载JSON数据
String jsonData = new GetJsonDataUtil().getJson(this, "province.json");
List<JsonBean> jsonBean = parseData(jsonData); // 解析JSON数据
// 填充数据
for (JsonBean jsonBean : jsonBeanList) {
List<String> cityList = new ArrayList<>();
List<List<String>> province_AreaList = new ArrayList<>();
options1Items.add(jsonBean.getName());
List<JsonBean.CityBean> cityBeanList = jsonBean.getCityList();
for (JsonBean.CityBean cityBean : cityBeanList) {
String cityName = cityBean.getName();
cityList.add(cityName);
List<String> areaList = cityBean.getArea();
province_AreaList.add(areaList);
}
options2Items.add(cityList);
options3Items.add(province_AreaList);
}
3. 高级自定义
3.1 自定义布局
Android-PickerView 支持完全自定义布局,以满足特殊的界面需求:
pvCustomOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3, View v) {
// 选择回调
}
})
.setLayoutRes(R.layout.pickerview_custom_options, new CustomListener() {
@Override
public void customLayout(View v) {
// 自定义布局控件初始化
TextView tvSubmit = v.findViewById(R.id.tv_finish);
TextView tvAdd = v.findViewById(R.id.tv_add);
ImageView ivCancel = v.findViewById(R.id.iv_cancel);
// 设置事件监听
tvSubmit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pvCustomOptions.returnData(tvSubmit);
}
});
ivCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pvCustomOptions.dismiss();
}
});
}
})
.build();
3.2 自定义滚轮样式
// 设置滚轮样式
.setDividerColor(Color.LTGRAY) // 分割线颜色
.setDividerType(WheelView.DividerType.CIRCLE) // 分割线类型
.setTextColorCenter(Color.BLACK) // 选中项文字颜色
.setTextColorOut(Color.GRAY) // 未选中项文字颜色
.setLineSpacingMultiplier(1.5f) // 行间距倍数
.setItemVisibleCount(5) // 可见项数量
.setAlphaGradient(true) // 启用透明度渐变
3.3 自定义动画效果
// 设置弹出动画
.setDecorView(null) // 设置自定义DecorView
.setOutSideColor(0x00000000) // 外部背景颜色
4. 实战案例
4.1 省市区三级联动
4.1.1 数据准备
首先,我们需要准备省市区数据,通常是JSON格式:
[
{
"name": "北京市",
"city": [
{
"name": "北京市",
"area": ["东城区", "西城区", "朝阳区", "丰台区", "石景山区", "海淀区"]
}
]
},
// 更多省份...
]
4.1.2 实现代码
// 加载省市区数据
private void initJsonData() {
String JsonData = new GetJsonDataUtil().getJson(this, "province.json");
List<JsonBean> jsonBean = parseData(JsonData);
for (JsonBean province : jsonBean) {
options1Items.add(province.getName());
List<String> cityList = new ArrayList<>();
List<List<String>> provinceAreaList = new ArrayList<>();
for (JsonBean.CityBean city : province.getCityList()) {
cityList.add(city.getName());
provinceAreaList.add(city.getArea());
}
options2Items.add(cityList);
options3Items.add(provinceAreaList);
}
}
// 显示选择器
private void showPickerView() {
OptionsPickerView pvOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3, View v) {
String address = options1Items.get(options1) + " " +
options2Items.get(options1).get(option2) + " " +
options3Items.get(options1).get(option2).get(options3);
tvAddress.setText(address);
}
})
.setTitleText("选择地区")
.setLabels("省", "市", "区")
.setCyclic(false, false, false)
.build();
pvOptions.setPicker(options1Items, options2Items, options3Items);
pvOptions.show();
}
4.2 出生日期选择器
private void showBirthdayPicker() {
Calendar startDate = Calendar.getInstance();
startDate.set(1900, 0, 1);
Calendar endDate = Calendar.getInstance();
TimePickerView pvTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
@Override
public void onTimeSelect(Date date, View v) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
tvBirthday.setText(sdf.format(date));
}
})
.setType(new boolean[]{true, true, true, false, false, false}) // 只显示年月日
.setLabel("年", "月", "日", "", "", "")
.setRangDate(startDate, endDate)
.setTitleText("选择出生日期")
.build();
pvTime.show();
}
4.3 自定义主题选择器
private void showThemePicker() {
List<String> themeList = new ArrayList<>();
themeList.add("默认主题");
themeList.add("深色主题");
themeList.add("蓝色主题");
themeList.add("绿色主题");
OptionsPickerView pvOptions = new OptionsPickerBuilder(this, new OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int option2, int options3, View v) {
applyTheme(options1); // 应用选中的主题
}
})
.setTitleText("选择主题")
.setContentTextSize(18)
.setCyclic(false)
.setSelectOptions(currentTheme) // 设置当前选中的主题
.build();
pvOptions.setPicker(themeList);
pvOptions.show();
}
5. 性能优化
5.1 数据处理优化
- 懒加载:对于大量数据,考虑使用懒加载方式,只加载当前需要显示的数据
- 分页加载:如果数据量过大,考虑分页加载数据
- 数据复用:避免频繁创建和销毁数据对象
5.2 内存管理
- 及时销毁:不需要时及时调用
dismiss()方法销毁PickerView - 避免内存泄漏:使用弱引用(WeakReference)保存上下文
// 使用弱引用避免内存泄漏
private WeakReference<Context> mContextWeakReference;
// 初始化
mContextWeakReference = new WeakReference<>(context);
// 使用时检查
if (mContextWeakReference.get() != null) {
// 执行操作
}
5.3 滑动优化
- 减少绘制:避免在滚动过程中执行复杂的绘制操作
- 平滑滚动:使用平滑滚动代替瞬时跳转
// 使用平滑滚动
wheelView.smoothScroll(WheelView.ACTION.FLING);
6. 常见问题与解决方案
6.1 时间选择器月份设置问题
问题:设置月份时出现错位,如设置2月实际显示3月。
原因:Calendar类的月份是从0开始的(0-11代表1-12月)。
解决方案:
// 错误方式
startDate.set(2020, 1, 1); // 实际会设置为2020年2月
// 正确方式
startDate.set(2020, 0, 1); // 设置为2020年1月
endDate.set(2020, 11, 31); // 设置为2020年12月
6.2 数据为空导致崩溃
问题:当数据源为空时,PickerView可能会崩溃。
解决方案:
// 使用前检查数据是否为空
if (options1Items != null && !options1Items.isEmpty()) {
pvOptions.setPicker(options1Items, options2Items, options3Items);
pvOptions.show();
} else {
Toast.makeText(this, "暂无数据", Toast.LENGTH_SHORT).show();
}
6.3 自定义布局不显示
问题:自定义布局后PickerView不显示或显示异常。
解决方案:
- 确保自定义布局中包含id为
optionspicker或timepicker的布局 - 检查自定义布局的根布局是否设置了正确的属性
- 确保自定义布局中的控件id与代码中引用的一致
7. 版本迁移指南
7.1 从3.x迁移到4.x
4.x版本相比3.x版本有一些API变化,主要包括:
- 方法重命名:
setBackgroundId()改为setOutSideColor() - 新增功能:支持设置最大可见项数量、透明度渐变等
- 回调接口调整:部分回调方法参数变化
迁移示例:
// 3.x版本
pvTime.setBackgroundId(0x00000000);
// 4.x版本
pvTime.setOutSideColor(0x00000000);
8. 总结与展望
Android-PickerView 是一个功能强大且高度可定制的选择器库,通过提供时间选择器和选项选择器,极大地简化了Android应用中复杂选择功能的实现。其丰富的自定义选项和灵活的配置方式,能够满足各种界面需求。
8.1 主要优势
- 功能全面:覆盖了时间选择和选项选择的各种需求
- 高度自定义:从布局到样式都可以深度定制
- 性能优良:优化的滚轮实现,流畅的滚动体验
- 易于集成:简单几步即可集成到项目中
8.2 未来展望
- 支持更多自定义动画效果
- 增加更多内置主题
- 优化大数据场景下的性能
- 支持更多交互方式
9. 附录
9.1 完整API参考
| 类名 | 主要方法 | 描述 |
|---|---|---|
| TimePickerView | setDate(Calendar date) | 设置默认日期 |
setRangDate(Calendar start, Calendar end) | 设置日期范围 | |
setType(boolean[] type) | 设置显示的时间类型 | |
setLunarCalendar(boolean lunar) | 设置是否显示农历 | |
| OptionsPickerView | setPicker(List<T> items) | 设置数据源 |
setSelectOptions(int... options) | 设置默认选中项 | |
setLabels(String... labels) | 设置单位标签 | |
setLinkage(boolean linkage) | 设置是否联动 | |
| BasePickerView | show() | 显示选择器 |
dismiss() | 关闭选择器 | |
setOnDismissListener(OnDismissListener listener) | 设置关闭监听器 | |
setOutSideCancelable(boolean cancelable) | 设置点击外部是否关闭 |
9.2 资源下载
9.3 贡献指南
如果你发现bug或有功能需求,可以通过以下方式贡献:
- Fork本仓库
- 创建分支 (
git checkout -b feature/amazing-feature) - 提交更改 (
git commit -m 'Add some amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 创建Pull Request
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



