省份城市选择估计每个入门的 android 码农都做过,而且 Gayhub 上的相关组件琳琅满目搞得大家目不暇接,那么究竟哪个最好用呢,这个我也不知道,只能说大家看项目需求。都不能满足的话只能自己撸一个了,不过我也不是轮子哥,本着站(shen)在巨(shou)人肩上(dang)的原则,这里我用的是 AndroidPicker。
AndroidPicker
https://github.com/gzu-liyujiang/AndroidPicker
然而这个东西可不仅仅是用来做省市地区联动选择用的,还能做文件选择,以及日期选择器、时间选择器、单项选择器、双项选择器、城市地址选择器、车牌号选择器、数字选择器、星座选择器、生肖选择器、颜色选择器、文件选择器、目录选择器等,可自定义顶部及底部界面,可自定义窗口动画 ...
而且估计很多人已经都用过了,用法很简单:
// 创建 AddressPicker 实例
AddressPicker addressPicker = new AddressPicker(当前Activity, 所有省份城市集合);
// 隐藏县、区的选择
addressPicker.setHideCounty(true);
int colorBlack = Color.parseColor("#000000");
// 分割线的颜色
addressPicker.setDividerColor(colorBlack);
addressPicker.setLabelTextColor(colorBlack);
// 取消 文字颜色
addressPicker.setCancelTextColor(Color.parseColor("#FFFFFF"));
// 按下文字时的颜色
addressPicker.setPressedTextColor(Color.parseColor("#FF0030"));
// 确定 文字的颜色
addressPicker.setSubmitTextColor(Color.parseColor("#FF0030"));
// 文字 颜色
addressPicker.setTextColor(colorBlack);
// 对话框标题颜色
addressPicker.setTopBackgroundColor(colorBlack);
addressPicker.setTopLineColor(colorBlack);
// 设置默认省市
addressPicker.setSelectedItem(默认省份, 默认城市);
// 设置事件监听
addressPicker.setOnAddressPickListener(new AddressPicker.OnAddressPickListener() {
@Override
public void onAddressPicked(cn.qqtheme.framework.entity.Province province, cn.qqtheme.framework.entity.City city, County county) {
// TODO
}
});
// 显示选择对话框
addressPicker.show();
对于城市数据,大家可以在作者的项目 Assets 文件夹中找到 city2.json ,反序列化为 ArrayList<Province> 即可。
https://github.com/gzu-liyujiang/AndroidPicker/blob/master/app/src/main/assets/city2.json
大家以为做项目就这么容易了吗?
Too young,too simple,sometimes naive.
大部分公司开始 app 项目的时候,网站应用已经很完善了,像会员系统牵扯的东西又多,有可能使用的省市数据已经固化到数据库了,并且他们的 id 也不可能有变动,跟业务逻辑耦合了,那么也不可能直接拿 AddressPicker 的数据直接来用了,比如像我的项目中省市数据格式如下:
这和作者的 city2.json 完全长得不是一毛一样,话说作者的这个 json 格式也是 google 地图的格式相同。
那么还有什么办法抢救一下呢,问了接口的少年,人家不愿意再做一层转换 。
那么,自己动手丰衣足食,操起 C# 撸起袖子干了,对项目的 json 实施大转换了。
首先将这个 AddressPicker 的省市区的实体类抄到 C# 中,当然你继续 Java 也可以,我可是把 C# 当 Python 用,写起来也得心应手。
https://github.com/gzu-liyujiang/AndroidPicker/tree/master/library/WheelPicker/src/main/java/cn/qqtheme/framework/entity
对应的 C# 实体类,稍作精简,因为只是做转换。
namespace Java
{
public class Area
{
public String areaId { get; set; }
public String areaName { get; set; }
}
public class City :Area
{
public String provinceId { get; set; }
public List<County> counties { get; set; }
}
public class County : Area
{
public String cityId { get; set; }
}
public class Province : Area
{
public List<City> cities { get; set; }
}
}
接下来是接口中的。
public class Province
{
public int ProvinceID { get; set; }
public string ProvinceName { get; set; }
public List<City> Cities { get; set; }
}
public class City
{
public int CityID { get; set; }
public string CityName { get; set; }
}
将接口 json 保存为文件,然后转换为 AddressPicker 所需的 json。
namespace CityBeanConvertor
{
class Program
{
static void Main(string[] args)
{
string inputJson = System.IO.File.ReadAllText("GetAllArea.json");
var provinces = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Province>>(inputJson);
List<Java.Province> cda = new List<Java.Province>();
foreach (var item in provinces)
{
Java.Province pp = new Java.Province();
pp.areaId = item.ProvinceID.ToString();
pp.areaName = item.ProvinceName;
var cs = item.Cities;
List<Java.City> ccs = new List<Java.City>();
if (cs.Count() > 0)
{
foreach (var ct in cs)
{
Java.City c2 = new Java.City();
c2.areaId = ct.CityID.ToString();
c2.areaName = ct.CityName;
c2.counties = new List<Java.County>();
ccs.Add(c2);
}
}
pp.cities = ccs;
cda.Add(pp);
}System.IO.File.WriteAllText("city_igs.json",Newtonsoft.Json.JsonConvert.SerializeObject(cda));
}
}
}
最终生成的 city_igs.json 就是符合 AddressPicker 的数据了,至于如果大家项目中也遇到这种问题,完全也可以按照这种办法来了。
理论来说作者的这个组件城市数据包含非常齐全了,完全可以按照字符串是否相等来判断,事实这也可以,但是真实的业务往往比较残忍,比如万一你接口的开发人员打错了一个字?比如你们项目没有覆盖指定的城市的业务所有省份城市列表里没有这个城市。
OK,废话不多说,搞完了就看看实际效果吧,还凑合。