一、背景介绍
在开发Android应用时,通过使用WheelView控件,我们可以实现像省市区选择器、时间选择器等功能。本文着重介绍如何使用自定义的WheelView控件实现地区选择三级联动的功能。
二、自定义WheelView控件
为了实现三级联动的地区选择功能,我们需要先自定义一个可以支持多级数据的控件。这里我们借鉴开源控件library中的WheelView,并添加一些内容使其支持三级联动地区选择。关于怎样添加一个库到Android Studio的方法,在这里就不再赘述了,在此假设我们已经添加了WheelView控件库。
在WheelView的package命名空间下创建一个新的java类:ProvinceCityAreaPicker.java。这个类中主要包含以下内容:
-
ViewPager:支持左右滑动三个WheelView同时更新数据,实现三级联动选择的效果。
-
TabLayout:控制ViewPager,每次只有一个Tab可以被选中。
下面上代码:
public class ProvinceCityAreaPicker extends LinearLayout {
private static final int PROVINCE_WHEEL_VIEW_INDEX = 0;
private static final int CITY_WHEEL_VIEW_INDEX = 1;
private static final int AREA_WHEEL_VIEW_INDEX = 2;
private Context mContext;
private TabLayout mTabLayout;
private ViewPager mViewPager;
private List<WheelView> mWheelViewList = new ArrayList<>();
private List<AreaEntity> mProvinceList;
private List<AreaEntity> mCityList;
private List<AreaEntity> mAreaList;
private int mLastSelectedProvincePosition;
private int mLastSelectedCityPosition;
private int mLastSelectedAreaPosition;
public ProvinceCityAreaPicker(Context context) {
this(context, null);
}
public ProvinceCityAreaPicker(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProvinceCityAreaPicker(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
mContext = context;
View contentView = View.inflate(mContext, R.layout.wheelview_layout_province_city_area_picker, this);
mTabLayout = (TabLayout) contentView.findViewById(R.id.tablayout);
mViewPager = (ViewPager) contentView.findViewById(R.id.viewpager);
setUpViewPager();
}
private void setUpViewPager() {
mProvinceList = AreaDataManager.getAllProvince(); //获取所有省份列表
if (mProvinceList == null || mProvinceList.isEmpty()) {
return;
}
mCityList = AreaDataManager.getCityByProvinceId(mProvinceList.get(0).getId()); //获取第一个省份的城市列表
if (mCityList == null || mCityList.isEmpty()) {
return;
}
mAreaList = AreaDataManager.getAreaByCityId(mCityList.get(0).getId()); //获取第一个城市的地区列表
if (mAreaList == null || mAreaList.isEmpty()) {
return;
}
List<View> viewList = new ArrayList<>();
viewList.add(createWheelView(mContext, mProvinceList, mCityList, mAreaList, PROVINCE_WHEEL_VIEW_INDEX));
viewList.add(createWheelView(mContext, mCityList, mAreaList, CITY_WHEEL_VIEW_INDEX));
viewList.add(createWheelView(mContext, mAreaList, null, AREA_WHEEL_VIEW_INDEX));
mViewPager.setAdapter(new ProvinceCityAreaPagerAdapter(viewList));
mTabLayout.setupWithViewPager(mViewPager); //TabLayout与ViewPager绑定
mTabLayout.setTabMode(TabLayout.MODE_FIXED);
mTabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
mTabLayout.getTabAt(0).select();
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
...
});
...
}
private WheelView createWheelView(Context context, List<AreaEntity> data, List<AreaEntity> data2, List<AreaEntity> data3, int tag) {
View contentView = LayoutInflater.from(context).inflate(R.layout.wheelview_layout_single, null);
WheelView wheelView = contentView.findViewById(R.id.wheelview);
mWheelViewList.add(wheelView);
wheelView.setItemTextSize(mContext.getResources().getDimensionPixelSize(R.dimen.wheelview_item_text_size));
wheelView.setWheelAdapter(new ArrayWheelAdapter(mContext));
wheelView.setSkin(WheelView.Skin.Common);
wheelView.setWheelSize(5);
wheelView.setOnWheelItemSelectedListener(new WheelView.OnWheelItemSelectedListener() {
...
});
wheelView.setTag(tag);
updateData(data, null, null);
return wheelView;
}
...
}
可见,在new ProvinceCityAreaPicker时传递的三个参数分别是:Context、AttributeSet、DefStyleAttr,这个类继承了LinearLayout,把ViewPager和TabLayout放在了LinearLayout中。
其中,setUpViewPager()方法主要用于初始化ViewPager和TabLayout。通过AreaDataManager获取所有省份列表,并利用createWheelView创建Title为"省"的WheelView。接着,获取第一个省份的城市列表,创建Title为"市"的WheelView,并设置TabLayout与ViewPager的绑定。下面是createWheelView()方法的详细说明:
-
创建一个WheelView,设置数据和样式等。
-
设置WheelView的监听器,根据tag标识判断当前选中的是哪一个WheelView,并自动更新下级联动的数据。
-
返回创建好的WheelView。
三、数据管理
接下来我们添加一个DataManager数据管理类,这个类是用于获取相应地区的列表信息,同时支持模糊查询,方便根据关键字查询特定地区。下面是DataManager类的具体代码:
public class AreaDataManager {
private static final String TAG = AreaDataManager.class.getSimpleName();
private static final String AREA_FILE_NAME = "area.xml";
private static List<AreaEntity> sProvinceList;
/**
* 获取所有省份列表
*
* @return 所有省份列表
*/
public static List<AreaEntity> getAllProvince() {
InputStream inputStream = null;
try {
inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
List<AreaEntity> allAreaList = parseXml(inputStream);
sProvinceList = new ArrayList<>();
for (AreaEntity area : allAreaList) {
if (area.getLevel() == AreaEntity.AREA_LEVEL_PROVINCE) {
sProvinceList.add(area);
}
}
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
Log.e(TAG, "getAllProvince: ", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return sProvinceList;
}
/**
* 获取城市列表
*
* @param provinceId 省份id
* @return 城市列表
*/
public static List<AreaEntity> getCityByProvinceId(int provinceId) {
InputStream inputStream = null;
try {
inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
List<AreaEntity> allAreaList = parseXml(inputStream);
List<AreaEntity> cityList = new ArrayList<>();
for (AreaEntity area : allAreaList) {
if (area.getParentId() == provinceId) {
if (area.getLevel() == AreaEntity.AREA_LEVEL_CITY) {
cityList.add(area);
}
}
}
return cityList;
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
Log.e(TAG, "getCityByProvinceId: ", e);
return null;
} finally {
IOUtils.closeQuietly(inputStream);
}
}
/**
* 获取所有地区列表
*
* @param cityId 城市id
* @return 所有地区列表
*/
public static List<AreaEntity> getAreaByCityId(int cityId) {
InputStream inputStream = null;
try {
inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
List<AreaEntity> allAreaList = parseXml(inputStream);
List<AreaEntity> areaList = new ArrayList<>();
for (AreaEntity area : allAreaList) {
if (area.getParentId() == cityId) {
areaList.add(area);
}
}
return areaList;
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
Log.e(TAG, "getAreaByCityId: ", e);
return null;
} finally {
IOUtils.closeQuietly(inputStream);
}
}
/**
* 获取省份名字
*
* @param id 省份id
* @return id对应的省份名字
*/
public static String getProvinceNameById(int id) {
if (sProvinceList == null || sProvinceList.isEmpty()) {
return "";
}
for (AreaEntity province : sProvinceList) {
if (province.getId() == id) {
return province.getName();
}
}
return "";
}
/**
* 获取城市名字
*
* @param id 城市id
* @return id对应的城市名字
*/
public static String getCityNameById(int id) {
InputStream inputStream = null;
try {
inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
List<AreaEntity> allAreaList = parseXml(inputStream);
for (AreaEntity area : allAreaList) {
if (area.getId() == id && area.getLevel() == AreaEntity.AREA_LEVEL_CITY) {
return area.getName();
}
}
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
Log.e(TAG, "getCityNameById: ", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return "";
}
/**
* 获取地区名字
*
* @param id 地区id
* @return id对应的地区名字
*/
public static String getAreaNameById(int id) {
InputStream inputStream = null;
try {
inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
List<AreaEntity> allAreaList = parseXml(inputStream);
for (AreaEntity area : allAreaList) {
if (area.getId() == id && area.getLevel() == AreaEntity.AREA_LEVEL_AREA) {
return area.getName();
}
}
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
Log.e(TAG, "getAreaNameById: ", e);
} finally {
IOUtils.closeQuietly(inputStream);
}
return "";
}
private static List<AreaEntity> parseXml(InputStream inputStream) throws XmlPullParserException, IOException {
List<AreaEntity> areaList = new ArrayList<>();
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setInput(new InputStreamReader(inputStream));
int eventType = xmlPullParser.getEventType();
AreaEntity areaEntity = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
String tagName = xmlPullParser.getName();
switch (eventType) {
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG:
switch (tagName) {
case "province":
areaEntity = new AreaEntity();
areaEntity.setId(Integer.valueOf(xmlPullParser.getAttributeValue(0)));
areaEntity.setName(xmlPullParser.getAttributeValue(1));
areaEntity.setLevel(AreaEntity.AREA_LEVEL_PROVINCE);
areaList.add(areaEntity);
break;
case "city":
areaEntity = new AreaEntity();
areaEntity.setId(Integer.valueOf(xmlPullParser.getAttributeValue(0)));
areaEntity.setName(xmlPullParser.getAttributeValue(1));
areaEntity.setParentId(Integer.valueOf(xmlPullParser.getAttributeValue(2)));
areaEntity.setLevel(AreaEntity.AREA_LEVEL_CITY);
areaList.add(areaEntity);
break;
case "area":
areaEntity = new AreaEntity();
areaEntity.setId(Integer.valueOf(xmlPullParser.getAttributeValue(0)));
areaEntity.setName(xmlPullParser.getAttributeValue(1));
areaEntity.setParentId(Integer.valueOf(xmlPullParser.getAttributeValue(2)));
areaEntity.setLevel(AreaEntity.AREA_LEVEL_AREA);
areaList.add(areaEntity);
break;
}
break;
case XmlPullParser.END_TAG:
break;
}
eventType = xmlPullParser.next();
}
return areaList;
}
/**
* 关键字匹配检索
*
* @param keyword 检索关键字
* @return 检索结果列表
*/
public static List<AreaEntity> search(String keyword) {
if (StringUtils.isBlank(keyword)) {
return new ArrayList<>();
}
List<AreaEntity> allAreaList = getAll();
List<AreaEntity> resultAreaList = new ArrayList<>();
for (AreaEntity area : allAreaList) {
if (area.getName().contains(keyword) && area.getLevel() == AreaEntity.AREA_LEVEL_AREA) {
resultAreaList.add(area);
}
}
return resultAreaList;
}
/**
* 获取所有地区列表
*
* @return 所有地区列表
*/
public static List<AreaEntity> getAll() {
InputStream inputStream = null;
try {
inputStream = AppContext.getInstance().getAssets().open(AREA_FILE_NAME);
return parseXml(inputStream);
} catch (IOException | XmlPullParserException e) {
e.printStackTrace();
Log.e(TAG, "getAll: ", e);
return new ArrayList<>();
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}
四、实现地区选择三级联动
在以上步骤的基础上,我们完成了一个可以支持三级联动的省市区选择控件。
在应用中,只需按如下方式使用即可:
ProvinceCityAreaPicker picker = new ProvinceCityAreaPicker(this);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
picker.setLayoutParams(layoutParams);
在实际使用的时候,需要添加在布局文件中添加如下代码:
<com.linhongzheng.weixin.shoppingmall.ui.widget.ProvinceCityAreaPicker
android:id="@+id/province_city_area_picker"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
五、示例说明
- 使用自定义WheelView控件实现颜色选择器
首先在xml文件中添加如下代码:
<com.linhongzheng.weixin.shoppingmall.ui.widget.WheelView
android:id="@+id/wheelview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
然后在java代码中初始化控件,并添加适配器:
private void initWheelView() {
WheelView wheelview = (WheelView) findViewById(R.id.wheelview);
wheelview.setWheelAdapter(new ColorWheelAdapter(this));//设置适配器
wheelview.setSkin(WheelView.Skin.Holo);//设置滚轮样式
wheelview.setWheelData(getData());//设置数据源
}
其中,ColorWheelAdapter是一种自定义适配器,getData()方法返回的是一个颜色的集合,需要根据实际需求编写相应的逻辑。
- 使用自定义WheelView控件实现周视图选择器
首先在xml文件中添加如下代码:
<com.linhongzheng.weixin.shoppingmall.ui.widget.WheelView
android:id="@+id/wheelview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
然后在java代码中初始化控件,并添加适配器:
private void initWheelView() {
WheelView wheelview = (WheelView) findViewById(R.id.wheelview);
wheelview.setWheelAdapter(new WeekWheelAdapter(this));//设置适配器
wheelview.setSkin(WheelView.Skin.Holo);//设置滚轮样式
wheelview.setWheelData(getData());//设置数据源
}
其中,WeekWheelAdapter是一种自定义适配器,getData()方法返回的是一周七天内的日期,需要根据实际需求编写相应的逻辑。
至此,我们已经实现了两种基于自定义WheelView控件的水平无限滑动选择器示例。
六、总结
本文主要介绍了如何使用自定义的WheelView控件实现地区选择三级联动的功能,具体过程分为以下几个步骤:
-
自定义WheelView控件:添加ViewPager和TabLayout,实现三级联动选择的效果;
-
数据管理:添加一个用于获取相应地区的列表信息的DataManager类,支持模糊查询;
-
实现地区选择三级联动;
-
展示两个基于自定义WheelView控件的水平无限滑动选择器示例。
这里我们需要特别强调的一点是,这里使用的方法老旧、过时,仅供学习、参考使用,不能用于实际应用中。实际使用时,建议使用第
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android自定义WheelView地区选择三级联动 - Python技术站