Android仿IOS三级级联城市选择器

时间:2022-05-31 05:36:26

项目用到城市选择,就找了一个大神的代码,自己做了修改后使用的,本程序有两个地方不是很完美:

一、点击popupWindow空白的区域不能关闭popupWindow;

二、选择器那里,不是当前选中的值,不能做到类似IOS里面的折叠,就是类似滚轮的那种立体效果。

望路过的大神们可以加以完善下。

上代码:popup_select_city.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:layout_alignParentBottom="true">
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="44dp"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<Button
android:id="@+id/btn_cancel"
android:layout_width="66dp"
android:layout_height="match_parent"
android:text="取消"
android:layout_alignParentLeft="true"
android:textSize="18sp"
android:textColor="#4a8ff8"
android:gravity="center"
/>

<Button
android:id="@+id/btn_confirm"
android:layout_width="66dp"
android:layout_height="match_parent"
android:text="确定"
android:layout_alignParentRight="true"
android:textSize="18sp"
android:textColor="@color/tab_select"
android:gravity="center"
/>
<EditText
android:id="@+id/edt_city_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/btn_cancel"
android:layout_toLeftOf="@id/btn_confirm"
android:padding="5dp"
android:layout_centerVertical="true"
android:hint="请输入省、市、区(县)"
android:background="@drawable/border_corner_edit"
android:textSize="14sp"/>
</RelativeLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<kankan.wheel.widget.WheelView
android:id="@+id/id_province"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" >
</kankan.wheel.widget.WheelView>

<kankan.wheel.widget.WheelView
android:id="@+id/id_city"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" >
</kankan.wheel.widget.WheelView>

<kankan.wheel.widget.WheelView
android:id="@+id/id_district"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" >
</kankan.wheel.widget.WheelView>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

selectCity.java:

package com.shengping.pao.PopupWindow;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.json.JSONObject;

import kankan.wheel.widget.WheelView;
import kankan.wheel.widget.OnWheelChangedListener;
import kankan.wheel.widget.adapters.ArrayWheelAdapter;

import com.shengping.pao.R;
import com.shengping.pao.model.CityModel;
import com.shengping.pao.model.DistrictModel;
import com.shengping.pao.model.ProvinceModel;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.drawable.BitmapDrawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.PopupWindow;

public class Select_City implements OnClickListener, OnWheelChangedListener{
private Context context;
private View parentView;
/**
* 取消按钮
*/
private Button btnCancel;

/**
* 确定按钮
*/
private Button btnConfirm;

private PopupWindow popupWindow;

private WheelView mViewProvince;
private WheelView mViewCity;
private WheelView mViewDistrict;
/**
* 所有省
*/
protected String[] mProvinceDatas;
/**
* key - 省 value - 市
*/
protected Map<String, String[]> mCitisDatasMap = new HashMap<String, String[]>();
/**
* key - 市 values - 区
*/
protected Map<String, String[]> mDistrictDatasMap = new HashMap<String, String[]>();

/**
* key - 区 values - 邮编
*/
protected Map<String, String> mZipcodeDatasMap = new HashMap<String, String>();

/**
* 当前省的名称
*/
protected String mCurrentProviceName;
/**
* 当前市的名称
*/
protected String mCurrentCityName;
/**
* 当前区的名称
*/
protected String mCurrentDistrictName ="";

/**
* 当前区的邮政编码
*/
protected String mCurrentZipCode ="";
private View view;
private cityselectListener listener;
public Select_City(Context activity,View v){
this.context = activity;
parentView=v;
}
@SuppressWarnings("deprecation")
@SuppressLint("InflateParams")
public Select_City builder(){

// PopView
view = LayoutInflater.from(context).inflate(
R.layout.popup_select_city, null);

initView(view);
// android.view.View#getWindowToken()
popupWindow = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setBackgroundDrawable(new BitmapDrawable());
popupWindow.setOutsideTouchable(true);
popupWindow.setFocusable(true);
popupWindow.setTouchInterceptor(new OnTouchListener() {

@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent event) {
// System.out.println("touch:"+event.getAction());
if(event.getAction()==MotionEvent.ACTION_OUTSIDE){
popupWindow.dismiss();
return true;
}
return false;
}
});
popupWindow.setOnDismissListener(new PopDismissListener());
popupWindow.setAnimationStyle(R.style.PopAnimationDownUp);
return this;
}
public void initView(View contentView){
mViewProvince = (WheelView) contentView.findViewById(R.id.id_province);
mViewCity = (WheelView) contentView.findViewById(R.id.id_city);
mViewDistrict = (WheelView)contentView. findViewById(R.id.id_district);
btnConfirm = (Button)contentView. findViewById(R.id.btn_confirm);
btnCancel=(Button)contentView. findViewById(R.id.btn_cancel);

// 添加change事件
mViewProvince.addChangingListener(this);
// 添加change事件
mViewCity.addChangingListener(this);
// 添加change事件
mViewDistrict.addChangingListener(this);
// 添加onclick事件
btnConfirm.setOnClickListener(this);
btnCancel.setOnClickListener(this);

initProvinceDatas();
mViewProvince.setViewAdapter(new ArrayWheelAdapter<String>(context, mProvinceDatas));
// 设置可见条目数量
mViewProvince.setVisibleItems(7);
mViewCity.setVisibleItems(7);
mViewDistrict.setVisibleItems(7);
updateCities();
updateAreas();
EditText edt_search=(EditText)contentView.findViewById(R.id.edt_city_select);
edt_search.addTextChangedListener(new TextWatcher() {

@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {

}

@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
// TODO Auto-generated method stub

}

@Override
public void afterTextChanged(Editable arg0) {
String str=arg0.toString();
int i=0;
if(str.equals("")){
mViewProvince.setCurrentItem(i);
return ;
}
for(String provence:mProvinceDatas){
if(provence.contains(str)){
mViewProvince.setCurrentItem(i);
return;
}
i++;
}
int privenceIndex=0;
int cityIndex=0;
int districtIndex=0;
for(privenceIndex=0;privenceIndex<mProvinceDatas.length;privenceIndex++){
String[] citys=mCitisDatasMap.get(mProvinceDatas[privenceIndex]);
cityIndex=0;
for(String city:citys){
if(city.contains(str)){
mViewProvince.setCurrentItem(privenceIndex);
mViewCity.setCurrentItem(cityIndex);
return;
}
cityIndex++;
}
}

privenceIndex=0;
cityIndex=0;
for(privenceIndex=0;privenceIndex<mProvinceDatas.length;privenceIndex++){
String[] citys=mCitisDatasMap.get(mProvinceDatas[privenceIndex]);
cityIndex=0;
for(String city:citys){
String[] districts=mDistrictDatasMap.get(city);
districtIndex=0;
for(String district:districts){
if(district.contains(str)){
mViewProvince.setCurrentItem(privenceIndex);
mViewCity.setCurrentItem(cityIndex);
mViewDistrict.setCurrentItem(districtIndex);
if(districtIndex==0){
mCurrentDistrictName = district;
mCurrentZipCode = mZipcodeDatasMap.get(mCurrentDistrictName);
}
return;
}
districtIndex++;
}
cityIndex++;
}
}

}
});
}
/**
* 显示
*/
@SuppressLint("NewApi")
public void show(){
if (null != popupWindow){
setBackgroundAlpha(0.5f);
popupWindow.showAtLocation(parentView, Gravity.BOTTOM,0,0);
}
}

/**
* 撤销
*/
public void dismiss(){
if (null != popupWindow){
popupWindow.dismiss();
}
}

/**
* 设置背景透明度
* @param alpha 背景的Alpha
*/
private void setBackgroundAlpha(float alpha){
WindowManager.LayoutParams lp = ((Activity)context).getWindow().getAttributes();
lp.alpha = alpha;
((Activity)context).getWindow().setAttributes(lp);
}
private class PopDismissListener implements PopupWindow.OnDismissListener{

@Override
public void onDismiss() {
//更改背景透明度
setBackgroundAlpha(1.0f);
}
}

@Override
public void onClick(View v) {
if(v.getId()==R.id.btn_cancel){
dismiss();
}else if(v.getId()==R.id.btn_confirm){
if(listener!=null){
JSONObject json=new JSONObject();
try{
json.put("mCurrentProviceName", mCurrentProviceName);
json.put("mCurrentCityName", mCurrentCityName);
json.put("mCurrentDistrictName", mCurrentDistrictName);
}catch(Exception e){
e.printStackTrace();
}
listener.selectok(json);
dismiss();
}
}
}
@Override
public void onChanged(WheelView wheel, int oldValue, int newValue) {
if (wheel == mViewProvince) {
updateCities();
} else if (wheel == mViewCity) {
updateAreas();
} else if (wheel == mViewDistrict) {
mCurrentDistrictName = mDistrictDatasMap.get(mCurrentCityName)[newValue];
mCurrentZipCode = mZipcodeDatasMap.get(mCurrentDistrictName);
}
}
/**
* 解析省市区的XML数据
*/

protected void initProvinceDatas()
{
List<ProvinceModel> provinceList = null;
AssetManager asset = context.getAssets();
try {
InputStream input = asset.open("province_data.xml");
// 创建一个解析xml的工厂对象
SAXParserFactory spf = SAXParserFactory.newInstance();
// 解析xml
SAXParser parser = spf.newSAXParser();
XmlParserHandler handler = new XmlParserHandler();
parser.parse(input, handler);
input.close();
// 获取解析出来的数据
provinceList = handler.getDataList();
//*/ 初始化默认选中的省、市、区
if (provinceList!= null && !provinceList.isEmpty()) {
mCurrentProviceName = provinceList.get(0).getName();
List<CityModel> cityList = provinceList.get(0).getCityList();
if (cityList!= null && !cityList.isEmpty()) {
mCurrentCityName = cityList.get(0).getName();
List<DistrictModel> districtList = cityList.get(0).getDistrictList();
mCurrentDistrictName = districtList.get(0).getName();
mCurrentZipCode = districtList.get(0).getZipcode();
}
}
//*/
mProvinceDatas = new String[provinceList.size()];
for (int i=0; i< provinceList.size(); i++) {
// 遍历所有省的数据
mProvinceDatas[i] = provinceList.get(i).getName();
List<CityModel> cityList = provinceList.get(i).getCityList();
String[] cityNames = new String[cityList.size()];
for (int j=0; j< cityList.size(); j++) {
// 遍历省下面的所有市的数据
cityNames[j] = cityList.get(j).getName();
List<DistrictModel> districtList = cityList.get(j).getDistrictList();
String[] distrinctNameArray = new String[districtList.size()];
DistrictModel[] distrinctArray = new DistrictModel[districtList.size()];
for (int k=0; k<districtList.size(); k++) {
// 遍历市下面所有区/县的数据
DistrictModel districtModel = new DistrictModel(districtList.get(k).getName(), districtList.get(k).getZipcode());
// 区/县对于的邮编,保存到mZipcodeDatasMap
mZipcodeDatasMap.put(districtList.get(k).getName(), districtList.get(k).getZipcode());
distrinctArray[k] = districtModel;
distrinctNameArray[k] = districtModel.getName();
}
// 市-区/县的数据,保存到mDistrictDatasMap
mDistrictDatasMap.put(cityNames[j], distrinctNameArray);
}
// 省-市的数据,保存到mCitisDatasMap
mCitisDatasMap.put(provinceList.get(i).getName(), cityNames);
}
} catch (Throwable e) {
e.printStackTrace();
} finally {

}
}
/**
* 根据当前的省,更新市WheelView的信息
*/
private void updateCities() {
int pCurrent = mViewProvince.getCurrentItem();
mCurrentProviceName = mProvinceDatas[pCurrent];
String[] cities = mCitisDatasMap.get(mCurrentProviceName);
if (cities == null) {
cities = new String[] { "" };
}
mViewCity.setViewAdapter(new ArrayWheelAdapter<String>(context, cities));
mViewCity.setCurrentItem(0);
updateAreas();
}
/**
* 根据当前的市,更新区WheelView的信息
*/
private void updateAreas() {
int pCurrent = mViewCity.getCurrentItem();
mCurrentCityName = mCitisDatasMap.get(mCurrentProviceName)[pCurrent];
String[] areas = mDistrictDatasMap.get(mCurrentCityName);

if (areas == null) {
areas = new String[] { "" };
}
mViewDistrict.setViewAdapter(new ArrayWheelAdapter<String>(context, areas));
mViewDistrict.setCurrentItem(0);
}
public interface cityselectListener{
public void selectok(JSONObject json);
}

public cityselectListener getListener() {
return listener;
}
public void setListener(cityselectListener listener) {
this.listener = listener;
}
}
XmlParserHandler.java

package com.shengping.pao.PopupWindow;

import java.util.ArrayList;
import java.util.List;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.shengping.pao.model.CityModel;
import com.shengping.pao.model.DistrictModel;
import com.shengping.pao.model.ProvinceModel;

public class XmlParserHandler extends DefaultHandler {

/**
* 存储所有的解析对象
*/
private List<ProvinceModel> provinceList = new ArrayList<ProvinceModel>();

public XmlParserHandler() {

}

public List<ProvinceModel> getDataList() {
return provinceList;
}

@Override
public void startDocument() throws SAXException {
// 当读到第一个开始标签的时候,会触发这个方法
}

ProvinceModel provinceModel = new ProvinceModel();
CityModel cityModel = new CityModel();
DistrictModel districtModel = new DistrictModel();

@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
// 当遇到开始标记的时候,调用这个方法
if (qName.equals("province")) {
provinceModel = new ProvinceModel();
provinceModel.setName(attributes.getValue(0));
provinceModel.setCityList(new ArrayList<CityModel>());
} else if (qName.equals("city")) {
cityModel = new CityModel();
cityModel.setName(attributes.getValue(0));
cityModel.setDistrictList(new ArrayList<DistrictModel>());
} else if (qName.equals("district")) {
districtModel = new DistrictModel();
districtModel.setName(attributes.getValue(0));
districtModel.setZipcode(attributes.getValue(1));
}
}

@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
// 遇到结束标记的时候,会调用这个方法
if (qName.equals("district")) {
cityModel.getDistrictList().add(districtModel);
} else if (qName.equals("city")) {
provinceModel.getCityList().add(cityModel);
} else if (qName.equals("province")) {
provinceList.add(provinceModel);
}
}

@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
}

}

CityModel.java

package com.shengping.pao.model;

import java.util.List;

public class CityModel {
private String name;
private List<DistrictModel> districtList;

public CityModel() {
super();
}

public CityModel(String name, List<DistrictModel> districtList) {
super();
this.name = name;
this.districtList = districtList;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<DistrictModel> getDistrictList() {
return districtList;
}

public void setDistrictList(List<DistrictModel> districtList) {
this.districtList = districtList;
}

@Override
public String toString() {
return "CityModel [name=" + name + ", districtList=" + districtList
+ "]";
}

}

DistrictModel.java

package com.shengping.pao.model;

public class DistrictModel {
private String name;
private String zipcode;

public DistrictModel() {
super();
}

public DistrictModel(String name, String zipcode) {
super();
this.name = name;
this.zipcode = zipcode;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getZipcode() {
return zipcode;
}

public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}

@Override
public String toString() {
return "DistrictModel [name=" + name + ", zipcode=" + zipcode + "]";
}

}

ProvinceModel.java

package com.shengping.pao.model;

import java.util.List;

public class ProvinceModel {
private String name;
private List<CityModel> cityList;

public ProvinceModel() {
super();
}

public ProvinceModel(String name, List<CityModel> cityList) {
super();
this.name = name;
this.cityList = cityList;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<CityModel> getCityList() {
return cityList;
}

public void setCityList(List<CityModel> cityList) {
this.cityList = cityList;
}

@Override
public String toString() {
return "ProvinceModel [name=" + name + ", cityList=" + cityList + "]";
}

}

Android仿IOS三级级联城市选择器

使用者只要实现 citiyselectListener接口,即可监听“完成”按钮事件  并获取当前选中的值:

Select_City select_city=new Select_City(getContext(),contentView);

select_city=select_city.builder();

select_city.setListener(listener);

select_city.show();