# android 城市选择器 **Repository Path**: nnddkj/android-city-selector ## Basic Information - **Project Name**: android 城市选择器 - **Description**: android 城市选择器 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-16 - **Last Updated**: 2026-08-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 城市 / 区域选择 Library(CityPicker) > 一个开箱即用的 Android 城市 / 区域选择组件库,支持 **省、市、区/县、镇/街道(1~5 级联动)**, > 内置 **5 种主流交互方式**、**3 种数据来源**、**全版本权限自动处理**, > 同时兼容 **Java / Kotlin**,适配 **Android 5.0(API 21)~ Android 17(API 37)**。 ``` 开发者:宁工``` --- ## 目录 1. [特性总览](#一特性总览) 2. [快速接入](#二快速接入) 3. [五种交互方式(含 Kotlin / Java 示例)](#三五种交互方式) 4. [联动级别配置(1~5 级)](#四联动级别配置) 5. [数据来源配置(本地 / 在线 / 业务服务器)](#五数据来源配置) 6. [JSON 数据格式说明](#六json-数据格式说明) 7. [自定义样式与文案](#七自定义样式与文案) 8. [权限与系统兼容性(自动处理)](#八权限与系统兼容性) 9. [定位当前城市与自定义定位 Provider](#九定位当前城市) 10. [MVVM 架构中使用](#十mvvm-架构中使用) 11. [对外开放 API 总览](#十一对外开放-api-总览) 12. [常见问题 FAQ](#十二常见问题-faq) 13. [数据维护与更新](#十三数据维护与更新) --- ## 一、特性总览 | 特性 | 说明 | | ---- | ---- | | 美团风格全屏城市选择 | 搜索 + 当前定位 + 热门城市 + 字母索引 + 多级联动,点击逐级下钻,面包屑返回 | | 全屏选项卡联动 | 京东 / 淘宝式「省 / 市 / 区县 / 镇 / 街道」选项卡,点击下一级,点选项卡回退 | | 底部弹窗联动 | 商业 App 最常用的「选择收货地址」弹窗,带已选路径 + 确定 / 取消 | | 搜索选择弹窗 | 支持城市名 / 全拼 / 首字母实时搜索,空态展示热门城市 | | 滚轮联动选择 | 自绘滚轮(无第三方依赖),拖动 + 惯性滑动 + 回弹吸附,多级联动 | | 1~5 级联动 | 省 → 市 → 区/县 → 镇 → 街道(村/社区),级别可配置 | | 3 种数据来源 | 本地 JSON(默认)/ 在线最新数据 / 业务服务器数据,可切换 | | 全国真实数据 | 内置 34 省级 / 342 市级 / 2978 区县 / 4.1 万+ 乡镇街道(含港澳台) | | 权限全自动 | 定位权限在库内部按系统版本自动申请(Android 5.0 ~ 17),接入方零处理 | | Java / Kotlin 双语言 | 库为纯 Java 实现,Java、Kotlin 均可直接调用 | | 系统兼容 | minSdk 21(Android 5.0),向上兼容至 Android 17 | | MVVM 友好 | 全部回调式 API,可在 ViewModel / 业务层使用 `loadData` 预加载数据 | | 自定义样式 | 主色、选中色、背景、字号、弹窗高度、滚轮尺寸、热门城市、各级文案均可配置 | | 开发者信息 | 宁工| --- ## 二、快速接入 ### 1. 添加依赖 `settings.gradle.kts` 中已包含 `:citypicker` 模块,在业务 App 的 `build.gradle.kts` 中加入: ```kotlin dependencies { implementation(project(":citypicker")) } ``` > 也可以将 `citypicker` 模块打成 AAR(`./gradlew :citypicker:assembleRelease`)后放入 `libs/` 目录 > 用 `implementation(files("libs/citypicker-release.aar"))` 引入,或发布到私有 Maven 仓库。 ### 2. 初始化(建议在 Application.onCreate 中调用一次) **Kotlin:** ```kotlin CityPicker.init(this) // 默认配置:本地 JSON、最多 5 级联动 ``` **Java:** ```java CityPicker.init(this); // 默认配置 ``` ### 3. 弹出选择器 **Kotlin:** ```kotlin CityPicker.showCityPicker(this, object : CityPickerCallback { override fun onSelected(path: List) { // path = [浙江省, 杭州市, 西湖区] val text = path.joinToString(" / ") { it.name } } override fun onCancel() {} override fun onError(message: String) {} }) ``` **Java:** ```java CityPicker.showCityPicker(this, new CityPickerCallback() { @Override public void onSelected(List path) { /* ... */ } @Override public void onCancel() { } @Override public void onError(String message) { } }); ``` --- ## 三、五种交互方式 | 方法 | 交互样式 | 适用场景 | | ---- | ---- | ---- | | `CityPicker.showCityPicker(activity, cb)` | 美团风格全屏城市选择页 | 需要定位 / 热门 / 搜索 / 索引的完整城市选择 | | `CityPicker.showLinkagePage(activity, cb)` | 全屏选项卡联动选择页 | 收货地址、省市区镇街道选择 | | `CityPicker.showLinkageDialog(activity, cb)` | 底部弹窗选项卡联动 | 表单页内嵌选择收货地址 | | `CityPicker.showSearchDialog(activity, cb)` | 搜索选择弹窗 | 快速搜索城市 | | `CityPicker.showWheelDialog(activity, cb)` | 滚轮联动选择弹窗 | 个性化交互 / 日期地址类滚轮风格 | 统一入口方式(按配置的样式弹出): ```kotlin // PickerStyle: CITY_PICKER / LINKAGE_PAGE / LINKAGE_DIALOG / SEARCH_DIALOG / WHEEL_DIALOG CityPicker.show(activity, CityPickerConfig.PickerStyle.LINKAGE_DIALOG, callback) ``` --- ## 四、联动级别配置 通过 `setMaxLevel(n)` 控制联动级数(1~5),实际联动级数为 `数据深度` 与 `maxLevel` 的较小值。 > 联动页选项卡、滚轮弹窗的数量会自动收敛到数据实际深度,不会出现"配置了 5 级但数据只有 4 级时, > 第 5 级选项卡/滚轮显示出来却永远选不了"的情况。 | 级别 | 含义 | 配置示例 | | ---- | ---- | ---- | | 1 | 仅省份 | `setMaxLevel(1)` | | 2 | 省 → 市 | `setMaxLevel(2)` | | 3 | 省 → 市 → 区/县(默认推荐) | `setMaxLevel(3)` | | 4 | 省 → 市 → 区/县 → 镇/街道 | `setMaxLevel(4)` | | 5 | 省 → 市 → 区/县 → 镇 → 街道/社区 | `setMaxLevel(5)` | ```kotlin CityPicker.setConfig( CityPickerConfig.Builder() .setMaxLevel(3) .build() ) CityPicker.showLinkageDialog(this, callback) ``` > 内置 `city_data.json` 为全国 **4 级**(省市区镇)真实数据(34 省级 / 342 市 / 2978 区县 / 4.1 万+ 镇街); > 5 级演示数据见 `app/src/main/assets/demo_city_5level.json`(覆盖全国 34 省,每省 2 市×2 区×2 镇×2 街, > 格式与标准完全一致,可直接替换为全国村级数据)。 --- ## 五、数据来源配置 支持 3 种数据来源,通过 `CityPickerConfig.DataSourceType` 配置,默认 **本地 JSON**。 | 类型 | 枚举值 | 说明 | | ---- | ---- | ---- | | 本地 JSON(默认) | `LOCAL_ASSETS` | 读取 assets 中 `city_data.json`,离线可用、秒开 | | 在线最新数据 | `ONLINE_URL` | 从配置的 URL 实时拉取最新行政区划数据,失败自动回退本地 | | 业务服务器数据 | `BUSINESS_SERVER` | 使用业务方自己的城市接口(支持业务自定义行政区划),失败自动回退本地 | ```kotlin // 在线最新数据 CityPicker.setConfig( CityPickerConfig.Builder() .setDataSourceType(CityPickerConfig.DataSourceType.ONLINE_URL) .setOnlineUrl("https://raw.githubusercontent.com/modood/Administrative-divisions-of-China/master/dist/pcas-code.json") .build() ) // 业务服务器数据 CityPicker.setConfig( CityPickerConfig.Builder() .setDataSourceType(CityPickerConfig.DataSourceType.BUSINESS_SERVER) .setBusinessUrl("https://your-server.com/api/city") .build() ) ``` > 切换数据源 / 级别后,库会自动失效旧缓存并按新配置重新加载。 --- ## 六、JSON 数据格式说明 三种数据源共用同一种 JSON 格式(树形,支持任意层级深度): ```json [ { "code": "330000", "name": "浙江省", "children": [ { "code": "330100", "name": "杭州市", "children": [ { "code": "330106", "name": "西湖区", "children": [ { "code": "330106001", "name": "北山街道", "children": [ { "code": "330106001001", "name": "上保社区" } ] } ] } ] } ] } ] ``` 字段说明: | 字段 | 必填 | 说明 | | ---- | ---- | ---- | | `name` | 是 | 地区名称(省 / 市 / 区县 / 镇 / 街道) | | `code` | 否 | 行政区划代码(也可用 `area`,二者兼容) | | `children` | 否 | 子级地区数组,无子级表示叶子节点 | | `hot` | 否 | 标记热门城市(`true`),当前用于热门城市快捷入口 | --- ## 七、自定义样式与文案 `CityPickerConfig.Builder` 支持以下配置项(全部可选): ```kotlin CityPicker.init(this, CityPickerConfig.Builder() .setMaxLevel(3) // 联动级别 1~5 .setDataSourceType(CityPickerConfig.DataSourceType.LOCAL_ASSETS) .setAssetsFileName("city_data.json") // 本地数据文件名 .setOnlineUrl("...") // 在线最新数据地址 .setBusinessUrl("...") // 业务服务器地址 .setPrimaryColor(0xFFFF6B3D) // 主色(按钮 / 高亮 / 索引气泡) .setPageBgColor(0xFFF5F6FA) // 页面背景 .setItemTextColor(0xFF333333) // 列表项文字 .setItemSelectedColor(0xFFFFEEE6) // 选中项背景 .setSelectedTextColor(0xFFFF6B3D) // 选中文字 / 选项卡高亮 .setDividerColor(0xFFF0F0F0) // 分隔线 .setTitleTextColor(0xFF111111) // 标题文字 .setIndexBarColor(0xFF999999) // 索引条 / 次级文字 .setTitleText("选择城市") // 标题 .setConfirmText("确定") // 确定按钮 .setCancelText("取消") // 取消按钮 .setSearchHint("输入城市名/拼音/首字母") // 搜索框提示 .setHotCities(listOf("北京", "上海", "杭州")) // 热门城市 .setLocateEnabled(true) // 是否显示当前定位 .setSearchEnabled(true) // 是否显示搜索 .setHotCitiesEnabled(true) // 是否显示热门城市 .setLetterIndexEnabled(true) // 是否显示字母索引 .setAnimationEnabled(true) // 弹窗动画 .setDialogHeightRatio(0.72f) // 弹窗高度占屏幕比例 .setWheelVisibleCount(5) // 滚轮可见条目数(奇数) .setWheelItemHeightDp(42) // 滚轮单项高度 .setLocationProvider(myProvider) // 注入自定义定位 .build() ) ``` --- ## 八、权限与系统兼容性 > **接入方完全不需要处理权限**,库内部已按系统版本自动完成全流程。 - **Android 6.0(API 23)以下**:定位权限安装即授予,直接使用。 - **Android 6.0 ~ 17(API 23 ~ 37)**:库自动发起运行时权限申请(`ACCESS_FINE_LOCATION` / `ACCESS_COARSE_LOCATION`),并处理拒绝后的引导(打开系统设置页)。 - 定位权限由库的 Manifest 自动合并进接入方 App,无需手动声明。 - 兼容 Android 5.0(API 21)~ Android 17,无需任何系统版本分支判断。 --- ## 九、定位当前城市 - 默认使用系统 `LocationManager`(网络 / GPS 最后已知位置)+ `Geocoder` 反查城市名,仅用于「当前定位城市」快捷入口,选择失败不影响其它功能。 - 如需高精度定位(如高德 / 百度定位 SDK),实现 `ILocationProvider` 接口并注入即可: ```kotlin class AmapProvider : ILocationProvider { override fun getCurrentCity(context: Context, callback: ILocationProvider.Callback) { // 在这里调用高德/百度定位,成功后 callback.onLocated("杭州") // 失败时 callback.onFailed("定位失败") } } CityPicker.setConfig( CityPickerConfig.Builder() .setLocationProvider(AmapProvider()) .build() ) ``` --- ## 十、MVVM 架构中使用 所有 API 均为回调式,天然适用于 MVVM / MVI 架构: **ViewModel 中预加载数据:** ```kotlin class AddressViewModel : ViewModel() { private val _provinceList = MutableLiveData>() val provinceList: LiveData> = _provinceList fun loadData() { CityPicker.loadData(object : OnCityDataListener { override fun onSuccess(data: List) { _provinceList.value = data } override fun onError(message: String) { _error.value = message } }) } /** 业务侧直接使用数据树做级联逻辑(如省市区三张独立列表) */ fun getCityList(province: CityBean): List = province.children } ``` **Activity / Fragment 中弹出选择:** ```kotlin viewModel.loadData() CityPicker.showLinkageDialog(this) { path -> // lambda 化 viewModel.submitAddress(path) } ``` --- ## 十一、对外开放 API 总览 | API | 说明 | | ---- | ---- | | `CityPicker.init(context)` | 使用默认配置初始化 | | `CityPicker.init(context, config)` | 使用自定义配置初始化 | | `CityPicker.getConfig()` | 获取当前全局配置 | | `CityPicker.setConfig(config)` | 运行时修改全局配置 | | `CityPicker.show(activity, style, cb)` | 按样式统一弹出 | | `CityPicker.showCityPicker(activity, cb)` | 美团风格全屏城市选择页 | | `CityPicker.showLinkagePage(activity, cb)` | 全屏选项卡联动选择页 | | `CityPicker.showLinkageDialog(activity, cb)` | 底部弹窗联动选择 | | `CityPicker.showSearchDialog(activity, cb)` | 搜索选择弹窗 | | `CityPicker.showWheelDialog(activity, cb)` | 滚轮联动选择弹窗 | | `CityPicker.loadData(listener)` / `loadDataAsync(listener)` | 异步加载城市数据(MVVM 可用) | | `CityPicker.getData()` | 获取已加载的省级根节点(未加载返回 null) | | `CityPicker.isDataLoaded()` | 数据是否已加载 | | `CityPicker.release()` | 释放数据缓存 | | `CityDataManager.get().search(roots, keyword, maxLevel, limit)` | 全树搜索(中文/全拼/首字母) | | `CityDataManager.get().findPathByName(roots, name)` | 按名称查找完整路径(定位匹配用) | | `CityDataManager.get().parse(context, json, maxLevel)` | 手动解析 JSON 为标准数据树 | **回调接口:** - `CityPickerCallback`:`onSelected(List path)` / `onCancel()` / `onError(String)` - `CityPickerCallbackAdapter`:空实现适配器,只需覆写需要的方法 - `OnCityDataListener`:`onSuccess(List)` / `onError(String)` - `ILocationProvider`:自定义定位 Provider 接口 **数据模型:** - `CityBean`:`code` / `name` / `level` / `hot` / `children` / `pinyin` / `initial` - `CitySearchResult`:搜索命中结果(`path` 完整路径 + `pathText`) --- ## 十二、常见问题 FAQ **Q1:可以只选省(一级)吗?** 可以,`setMaxLevel(1)` 即可,此时点击省份直接回调。 **Q2:数据是实时的吗?** 默认本地 JSON 为随库内置的行政区划数据;如需最新,使用 `ONLINE_URL` 数据源,或定期更新本地文件(见第十三节)。 **Q3:我的业务有自定义区域(如海外仓地址),怎么办?** 使用 `BUSINESS_SERVER` 数据源,返回的 JSON 格式与本地完全一致,支持任意深度与层级数量。 **Q4:不想让用户看到定位功能?** `setLocateEnabled(false)` 即可隐藏「当前定位城市」。 **Q5:库包体积有多大?** 核心代码极小,数据文件约 1.8MB(全国 4 级数据),拼音字典约 0.5MB,可按需裁剪。 **Q6:为什么选择结果里「北京市」会显示两级(北京市 → 北京市)?** 内置数据遵循民政部标准结构:直辖市下存在「北京市市辖区」这一行政区划层级, 与美团 / 京东等 App 的展示方式一致,属正常现象。 **Q7:支持暗色模式吗?** 当前版本为浅色商业风格,颜色全部可配置,可自行接入深色主题。 --- ## 十三、数据维护与更新 库内置数据来源(真实行政区划,含港澳台): | 文件 | 说明 | | ---- | ---- | | `citypicker/src/main/assets/city_data.json` | 全国 34 省级 / 342 市级 / 2978 区县 / 4.1万+ 镇街(4 级) | | `citypicker/src/main/assets/dict_pinyin.txt` | 汉字拼音字典(约 4.4 万词条,来自 pinyin-data) | 更新数据的方法: 1. 从 [Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China) 下载最新的 `pcas-code.json`,放入 `citypicker/src/main/assets/`; 2. 运行转换脚本生成标准格式: ```bash node tools/convert_city_data.js ``` 3. 若要完整 5 级(村级)数据,将村级数据按相同格式合并进 `children` 数组即可。 --- ## 开源许可 / 联系 - 本项目只是demo,仅供项目快速集成,可自由使用。 - 数据来源:民政部行政区划([Administrative-divisions-of-China](https://github.com/modood/Administrative-divisions-of-China))、 拼音([pinyin-data](https://github.com/mozillazg/pinyin-data)),在此致谢。 ``` 开发者:宁工```