前言:这是一款仿iOS的PickerView控件,有时间选择器和选项选择,支持年月日时分,年月日,时分等格式。
主要目的:百度上有非常多写这个开源项目的用法,不过大多数都是照搬github上面复制过来的,少有人说明该如何设置三级联动的数据源,这里将身高、体重、年龄等进行简单的封装,使用只需一句代码,如需基本使用可参考原项目链接:
https://github.com/Bigkoo/Android-PickerView
更新时间:2020-5-10 新增三级城市读取本地JSON文件
新增province.json文件如下图:
工具类CitySelectPicker:
public class CitySelectPicker {
private OptionsPickerView optionsPickerView;
private ArrayList options1Items = new ArrayList<>();
private ArrayList> options2Items = new ArrayList<>();
private ArrayList>> options3Items = new ArrayList<>();
/**
* 获取省市区
*/
public void getInstance(final BaseMvpActivity context, final TextView textView,
int position1, int position2, int position3) {
initJsonData(context);
optionsPickerView = new OptionsPickerBuilder(context, (options1, options2, options3, v) -> {
//返回的分别是三个级别的选中位置
String tx = options1Items.get(options1).getPickerViewText() +
options2Items.get(options1).get(options2) +
options3Items.get(options1).get(options2).get(options3);
textView.setText(tx);
listener.onSelect(options1, options2, options3);
})
//.setTitleText("城市选择")
.setDividerColor(Color.parseColor(ColorConfig.LINE_COLOR))
.setTextColorCenter(Color.parseColor(ColorConfig.TEXT_COLOR)) //设置选中项文字颜色
.setContentTextSize(18)
.setLineSpacingMultiplier(2.0f)
.setSelectOptions(position1, position2, position3)
//.setSelectOptions(6, 6, 6)
.setLayoutRes(R.layout.item_picker_options, v -> {
TextView tvSubmit = v.findViewById(R.id.tv_finish);
TextView tvCancel = v.findViewById(R.id.tv_cancel);
//TextView tvUnit = v.findViewById(R.id.tv_unit);
//tvUnit.setText(title);
tvSubmit.setOnClickListener(v1 -> {
optionsPickerView.dismiss();
optionsPickerView.returnData();
//Tool.openBottomUIMenu((Activity) context);
});
tvCancel.setOnClickListener(v12 -> {
optionsPickerView.dismiss();
optionsPickerView.returnData();
//Tool.openBottomUIMenu((Activity) context);
});
})
.setDecorView(context.getWindow().getDecorView().findViewById(android.R.id.content))
.build();
/*pvOptions.setPicker(options1Items);//一级选择器
pvOptions.setPicker(options1Items, options2Items);//二级选择器*/
optionsPickerView.setPicker(options1Items, options2Items, options3Items);//三级选择器
optionsPickerView.show();
}
private void initJsonData(Context context) {//解析数据
/**
* 注意:assets 目录下的Json文件仅供参考,实际使用可自行替换文件
* 关键逻辑在于循环体
* */
String JsonData = getJson(context, "province.json");//获取assets目录下的json文件数据
ArrayList jsonBean = parseData(JsonData);//用Gson 转成实体
/**
* 添加省份数据
*
* 注意:如果是添加的JavaBean实体,则实体类需要实现 IPickerViewData 接口,
* PickerView会通过getPickerViewText方法获取字符串显示出来。
*/
options1Items = jsonBean;
for (int i = 0; i < jsonBean.size(); i++) {//遍历省份
ArrayList CityList = new ArrayList<>();//该省的城市列表(第二级)
ArrayList> 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 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);
}
}
public static OnPickerListener listener;
public void setListener(OnPickerListener listener) {
this.listener = listener;
}
public interface OnPickerListener {
void onSelect(int position1, int position2, int position3);
}
/**
* 读取Json文件
*/
private String getJson(Context context, String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
AssetManager assetManager = context.getAssets();
BufferedReader bf = new BufferedReader(new InputStreamReader(assetManager.open(fileName)));
String line;
while ((line = bf.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
/**
* 解析数据
*/
private ArrayList parseData(String result) {//Gson 解析
ArrayList detail = new ArrayList<>();
try {
JSONArray data = new JSONArray(result);
Gson gson = new Gson();
for (int i = 0; i < data.length(); i++) {
Data entity = gson.fromJson(data.optJSONObject(i).toString(), Data.class);
detail.add(entity);
}
} catch (Exception e) {
e.printStackTrace();
}
return detail;
}
}
使用方法:
private int options1, options2, options3;
CitySelectPicker picker = new CitySelectPicker();
picker.setListener((position1, position2, position3) -> {
options1 = position1;
options2 = position2;
options3 = position3;
});
picker.getInstance(“上下文”, “TextView等控件”, options1, options2, options3);
文件上传不了,格式如下,需要文件可前往gitHub或私信于我
1、效果图:
项目build.gradle中加入:
implementation 'com.contrarywind:Android-PickerView:4.1.6'
studio版本3.0以下 implementation 换成compile
2、Data.java 三级联动实体类
public class Data implements IPickerViewData{
private String name;
private List city;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getCity() {
return city;
}
public void setCity(List city) {
this.city = city;
}
@Override
public String getPickerViewText() {
return name;
}
public static class CityBean {
private String name;
private List area;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getArea() {
return area;
}
public void setArea(List area) {
this.area = area;
}
}
}
注意实现IPickerViewData接口,并return对应的显示内容,代码中也有说明
3、SingleOptionsPicker封装类:
public class SingleOptionsPicker {
private Activity activity;
//回调接口
private OnPickerOptionsClickListener listener;
private OptionsPickerView pvOptions;
//一级联动或条件数据源
private List options1Items;
//二级联动数据源
private List> options2Items;
//三级联动数据源
private List>> options3Items;
//默认选中的位置
private int options1, options2, options3;
/**
*普通条件选择项方法
*/
public SingleOptionsPicker(Activity activity, String select, List options1Items, OnPickerOptionsClickListener listener) {
this.activity = activity;
this.listener = listener;
this.options1Items = options1Items;
boolean isContinue = true;
for (int i = 0; i < options1Items.size() && isContinue; i++) {
//设置选中项
if (select.equals(options1Items.get(i))) {
options1 = i;
isContinue = false;
}
}
getInstance();
}
/**
*三级联动选择项方法
*/
public SingleOptionsPicker(Activity activity, int options1, int options2, int options3, List options1Items, List> options2Items,
List>> options3Items, OnPickerOptionsClickListener listener) {
this.activity = activity;
this.listener = listener;
this.options1 = options1;
this.options2 = options2;
this.options3 = options3;
this.options1Items = options1Items;
this.options2Items = options2Items;
this.options3Items = options3Items;
getInstance();
}
private OptionsPickerView getInstance() {
pvOptions = new OptionsPickerBuilder(activity, new OnOptionsSelectListener() {
@Override
public void onOptionsSelect(int options1, int options2, int options3, View v) {
if (listener != null) {
listener.onOptionsSelect(options1, options2, options3, v);
}
}
})
//分隔线颜色。
.setDividerColor(Color.parseColor("#BBBBBB"))
//滚轮背景颜色
.setBgColor(Color.parseColor("#F5F5F5"))
//设置两横线之间的间隔倍数
.setLineSpacingMultiplier(1.8f)
//设置选中项的颜色
.setTextColorCenter(Color.parseColor("#333333"))
//是否只显示中间选中项的label文字,false则每项item全部都带有label
.isCenterLabel(true)
////设置选择的三级单位
.setLabels("", "", "")
//标题文字
.setTitleText("标题文字")
//默认选中项
.setSelectOptions(options1, options2, options3)
.setLayoutRes(R.layout.item_picker_options, new CustomListener() {//自定义布局
@Override
public void customLayout(View v) {
final TextView tvSubmit = v.findViewById(R.id.tv_finish);
tvSubmit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pvOptions.returnData();
pvOptions.dismiss();
}
});
}
})
.build();
pvOptions.setPicker(options1Items, options2Items, options3Items);//三级选择器
return pvOptions;
}
public static void openOptionsPicker(Activity activity, List list, final int type, final TextView textView) {
String select = textView.getText().toString();
new SingleOptionsPicker(activity, select, list,
new SingleOptionsPicker.OnPickerOptionsClickListener() {
@Override
public void onOptionsSelect(int options1, int options2, int options3, View view) {
if (type == 1) {
textView.setText(options1 + "岁");
} else if (type == 2) {
//注意起始数值
textView.setText(options1 + 80 + "CM");
} else if (type == 3) {
textView.setText(options1 + 30 + "KG");
}
}
}).show();
}
/**
* 显示选择器
*/
public void show() {
if (pvOptions != null && !pvOptions.isShowing()) {
pvOptions.show();
}
}
/**
* 关闭选择器
*/
public void dismiss() {
if (pvOptions != null && pvOptions.isShowing()) {
pvOptions.dismiss();
}
}
/**
* 选择项回调
*/
public interface OnPickerOptionsClickListener {
void onOptionsSelect(int options1, int options2, int options3, View view);
}
}
对应的自定义布局:item_picker_options
4、TestActivity 测试类也就是使用方法:
public class TestActivity extends AppCompatActivity implements View.OnClickListener {
private TextView textview1, textview2, textview3, textview4;
private ArrayList options1Items = new ArrayList<>();
private ArrayList> options2Items = new ArrayList<>();
private ArrayList>> options3Items = new ArrayList<>();
//默认选中项(第一次为0,0,0)
private int select1, select2, select3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
textview1 = this.findViewById(R.id.textview1);
textview2 = this.findViewById(R.id.textview2);
textview3 = this.findViewById(R.id.textview3);
textview4 = this.findViewById(R.id.textview4);
textview1.setOnClickListener(this);
textview2.setOnClickListener(this);
textview3.setOnClickListener(this);
textview4.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.textview1:
List list1 = new ArrayList<>();
for (int i = 0; i < 101; i++) {
list1.add(i + "岁");
}
SingleOptionsPicker.openOptionsPicker(this, list1, 1, textview1);
break;
case R.id.textview2:
List list2 = new ArrayList<>();
for (int i = 80; i < 200; i++) {
list2.add(i + "CM");
}
SingleOptionsPicker.openOptionsPicker(this, list2, 2, textview2);
break;
case R.id.textview3:
List list3 = new ArrayList<>();
for (int i = 30; i < 100; i++) {
list3.add(i + "KG");
}
SingleOptionsPicker.openOptionsPicker(this, list3, 3, textview3);
break;
case R.id.textview4:
//省份
ArrayList list = new ArrayList<>();
//添加3个个省份
Data data1 = new Data();
Data data2 = new Data();
Data data3 = new Data();
data1.setName("江西省");
data2.setName("河南省");
data3.setName("湖北省");
//添加市数据(省下面的数据)
ArrayList cityBeans1 = new ArrayList<>();
ArrayList cityBeans2 = new ArrayList<>();
ArrayList cityBeans3 = new ArrayList<>();
//(第一个省份下面的市数据)
Data.CityBean cityBean1 = new Data.CityBean();
Data.CityBean cityBean2 = new Data.CityBean();
Data.CityBean cityBean3 = new Data.CityBean();
cityBean1.setName("南昌");
cityBean2.setName("九江");
cityBean3.setName("赣州");
cityBeans1.add(cityBean1);
cityBeans1.add(cityBean2);
cityBeans1.add(cityBean3);
//(第二个省份下面的市数据)
Data.CityBean cityBean4 = new Data.CityBean();
Data.CityBean cityBean5 = new Data.CityBean();
Data.CityBean cityBean6 = new Data.CityBean();
cityBean4.setName("洛阳");
cityBean5.setName("开封");
cityBean6.setName("许昌");
cityBeans2.add(cityBean4);
cityBeans2.add(cityBean5);
cityBeans2.add(cityBean6);
//(第三个省份下面的市数据)
Data.CityBean cityBean7 = new Data.CityBean();
Data.CityBean cityBean8 = new Data.CityBean();
Data.CityBean cityBean9 = new Data.CityBean();
cityBean7.setName("黄冈");
cityBean8.setName("荆州");
cityBean9.setName("鄂州");
cityBeans3.add(cityBean7);
cityBeans3.add(cityBean8);
cityBeans3.add(cityBean9);
//为市添加区(一共为9个市)
ArrayList area1 = new ArrayList<>();
ArrayList area2 = new ArrayList<>();
ArrayList area3 = new ArrayList<>();
ArrayList area4 = new ArrayList<>();
ArrayList area5 = new ArrayList<>();
ArrayList area6 = new ArrayList<>();
ArrayList area7 = new ArrayList<>();
ArrayList area8 = new ArrayList<>();
ArrayList area9 = new ArrayList<>();
area1.add("东湖区");
area1.add("西湖区");
area1.add("青云谱区");
area2.add("浔阳区");
area2.add("濂溪区");
area2.add("九江县");
area3.add("赣县");
area3.add("信丰");
area3.add("南康");
area4.add("西工区");
area4.add("洛龙区");
area4.add("涧西区");
area5.add("鼓楼区");
area5.add("龙亭区");
area5.add("金明区");
area6.add("禹州市");
area6.add("长葛市");
area6.add("许昌县");
area7.add("团风县");
area7.add("浠水县");
area7.add("罗田县");
area8.add("荆州区");
area8.add("沙市区");
area8.add("江陵县");
area9.add("鄂城区");
area9.add("华容区");
area9.add("梁子湖区");
//将区添加至市中(27个区)
cityBean1.setArea(area1);
cityBean2.setArea(area2);
cityBean3.setArea(area3);
cityBean4.setArea(area4);
cityBean5.setArea(area5);
cityBean6.setArea(area6);
cityBean7.setArea(area7);
cityBean8.setArea(area8);
cityBean9.setArea(area9);
//将市添加至省中(9个市)
data1.setCity(cityBeans1);
data2.setCity(cityBeans2);
data3.setCity(cityBeans3);
//将市添加至省中(3个省)
list.add(data1);
list.add(data2);
list.add(data3);
/**
* 添加省份数据(也可以添加string)
* 注意:如果是添加的JavaBean实体,则实体类需要实现 IPickerViewData 接口,
* PickerView会通过getPickerViewText方法获取字符串显示出来。
*/
options1Items = list;
for (int i = 0; i < list.size(); i++) {//遍历省份
ArrayList CityList = new ArrayList<>();//该省的城市列表(第二级)
ArrayList> Province_AreaList = new ArrayList<>();//该省的所有地区列表(第三极)
for (int c = 0; c < list.get(i).getCity().size(); c++) {//遍历该省份的所有城市
String CityName = list.get(i).getCity().get(c).getName();
CityList.add(CityName);//添加城市
ArrayList City_AreaList = new ArrayList<>();//该城市的所有地区列表
//如果无地区数据,建议添加空字符串,防止数据为null 导致三个选项长度不匹配造成崩溃
if (list.get(i).getCity().get(c).getArea() == null
|| list.get(i).getCity().get(c).getArea().size() == 0) {
City_AreaList.add("");
} else {
City_AreaList.addAll(list.get(i).getCity().get(c).getArea());
}
Province_AreaList.add(City_AreaList);//添加该省所有地区数据
}
options2Items.add(CityList);
options3Items.add(Province_AreaList);
}
new SingleOptionsPicker(this, select1, select2, select3,
options1Items, options2Items, options3Items,
new SingleOptionsPicker.OnPickerOptionsClickListener() {
@Override
public void onOptionsSelect(int options1, int options2, int options3, View view) {
//返回的分别是三个级别的选中位置
String string = options1Items.get(options1).getPickerViewText() +
options2Items.get(options1).get(options2) +
options3Items.get(options1).get(options2).get(options3);
textview4.setText(string);
//将选择后的选中项赋值
select1 = options1;
select2 = options2;
select3 = options3;
}
}).show();
break;
}
}
}
对应布局:activity_test
github上解析的是province.json文件,实际数据源应该是动态的。