滑动选择

先上图:

滑动选择

这是一个三级联动的选择城市控件(此处使用的是PopupWindow), 此效果最核心的就是CoreLibs中的ScrollerNumberPicker, 最难的部分则是数据分析. 如果明白了这个控件的原理, 那其他的比如三级不带联动, 二级不带联动(闹钟)等效果就不在话下了.

注: 由于是一个demo, 功能没有写全, 某些地方也不是很规范, 请谅解.

UI

三级联动式的城市选择一般以弹出窗的形式, 也有镶嵌在Activity/Fragment中的. 这里使用的PopupWindow, 当然也可以使用Dialog/View等. 原理都相通. 首先来看看xml布局文件:

布局的重点就是显示在底部的横向的LinearLayout, 该LinearLayout中包含了三个权重都是1的ScrollerNumberPicker. 这三个ScrollerNumberPicker就对应了示例图中的省市区三列. 接着看看ScrollerNumberPicker中有哪些可以自定义的属性:

  • normalTextColor 未被选中的文字的颜色

  • normalTextSize 未被选中的文字的大小

  • selectedTextColor 被选中的文字的颜色

  • selectedTextSize 被选中的文字的大小

  • unitHeight 条目的高度

  • itemNumber 一页显示多少个条目

  • lineColor 分割线的颜色

  • maskHeight 遮罩的高度

  • noEmpty 是否不允许为空

  • isEnable 是否被激活

接着看看PopupWindow的部分代码:

此时的PopupWindow更多的是承载一些显示的工作, 如果对PopupWindow不是很熟悉的可以参考 弹窗. 到此整个UI效果就出来了. 但是还缺少核心数据这一块.

数据

这一部分就要考验大家的数据分析能力了. 我不准备讲的很详细, 毕竟虽然数据是三级联动的重点, 但不是此章的重点, 此章的重点是告诉大家ScrollerNumberPicker的用法.

首先我们看看如何为ScrollerNumberPicker设置数据. ScrollerNumberPicker最常用的公共方法有:

  • void setData(ArrayList<String> data) 设置数据, 只接受ArrayList<String>类型的数据

  • int getSelected() 获取当前控件中, 被选中的项的下标

  • String getSelectedText() 获取当前控件中, 被选中的项的内容

  • void setDefault(int index) 设置当数据加载完成后默认选中第几条数据

  • void setOnSelectListener(OnSelectListener onSelectListener) 设置滑动监听

我们只需要调用void setData(ArrayList<String> data)为ScrollerNumberPicker设置数据即可. 同时也可以借助OnSelectListener的endSelect回调, 来实现联动. 比如当监听到用户选择了省, ScrollerNumberPicker停止滑动了, 我们就可以在endSelect里去设置市的数据.

然后分析一下, 要实现城市三级联动最重要的是什么? 是城市的数据. 后面的代码全部是围绕城市的数据来的. 本例的城市数据是以json形式放到assets文件夹中的. 其主要结构如下:

注: json带有省略号......的部分意味着还有不确定个数的Object, 结构与省略号前的Object相同. 整个json中共有三处省略.

分析json数据可以得到, 整个json是由一个叫leverArea的数组构成, leverArea中的每个元素, 又包含了一个名为nextArea的数组, nextArea中的每个元素的结构与leverArea中的元素的结构绝大部分一样, 同样, nextArea中的元素也包含了一个nextArea数组. 这是一个典型的递归结构, 可以无限延伸下去. 但是此json只有三层递归, 分别代表了省市区三层数据.

数据解析可以手动解析也可以Gson解析, 如果会递归算法应该不是难题. 实体类的设计也是显而易见的:

TCity实现了Cloneable接口, 方便克隆, 避免为nextArea赋值时引用的是同一个指针而产生意外的后果. 很多时候源数据都不是很符合我们的要求. 比如源数据中直辖市都只有两层, 不像省都是有三层的, 这个时候就需要我们自己处理, 为直辖市再加上一层数据. 具体算法就不贴出来了, 因为使用条件很特殊, 几乎没法复用.

解析完数据之后我们就得到了一个List<TCity>类型的集合, 这个集合还不能直接使用.

另一个待解决的问题就是, 如何做到选了省之后, 显示与之对应的市, 选好市, 显示与之对应区. 关键就在于对List<TCity>的操作. 我们需要将List<TCity>拆分成三个集合, 分别存放所有的省数据, 当前选择的省对应的市的数据, 以及当前选择的市对应的区的数据. 又因为ScrollerNumberPicker只接受ArrayList<String>类型的数据, 因此我们还需要三个集合, 分别存放所有的省的名称, 当前选择的省对应的市的名称, 以及当前选择的市对应的区的名称.

接下来, 借助OnSelectListener来获取被选中的项目的下标, 然后通过下标在List<TCity>中查找其nextArea属性, 获取下一级联动的数据.

最后, 我们可以借助getSelectedText()方法告诉外部我们选择了什么地址:

到此, 整个三级联动的骨架都实现了, 剩下需要根据需求自行去完善.

Last updated

Was this helpful?