所有的原理的说明都在注释里,按顺序看非常详细。
布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="match_parent">
<Spinner
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:id="@+id/sheng"
android:background="#e7e8e9"></Spinner>
<Spinner
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:id="@+id/shi"
android:background="#a92521"></Spinner>
<Spinner
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:id="@+id/qu"
android:background="#615221"></Spinner>
</LinearLayout>
主程序:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private Spinner spinner_sheng,spinner_shi,spinner_qu;
//全局的jsonObject
private JSONObject jsonObject;//把全国的省市区的信息以json的格式保存,解析完成后赋值为null
private String[] allProv;//所有的省
private static final String TAG = "我想告诉你:";
private ArrayAdapter<String> provinceAdapter;//省份数据适配器
private ArrayAdapter<String> cityAdapter;//城市数据适配器
private ArrayAdapter<String> areaAdapter;//区县数据适配器
private String[] allSpinList;//在spinner中选出来的地址,后面需要用空格隔开省市区
private String provinceName;//省的名字
private String areaName;//区的名字
private Map<String, String[]> cityMap = new HashMap<String, String[]>();
//key:省p---value:市n value是一个集合
private Map<String, String[]> areaMap = new HashMap<String, String[]>();
//key:市n---value:区s 区也是一个集合
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();//初始化Spinner按钮
initJsonData();//初始化json数据,此方法用来打开json文件并且存储里面的内容到json对象
initCityData();//现在city.json的所有内容都在jsonObject中,解析此对象;
//上面这个方法运行后,我获得三个东西,一个是 allProv[]数组,里面存着所有的省,里面有34个数据
//一个是 cityMap,里面存着的键值都是allProv[]里面的省,与键值相对应的是allCity[]数组,里面是这个省的市。共有34个键值。
//还有一个是areaMap,里面的键值都是allCity[]里面的市,与键值相对应的是allArea[]数组,里面是这个省市的左右的区县,共有2766个键值
//心得:属于json解析的已经完成了,其实解析json对象就那么一两个方法,但是得十分明白json数据的结构
//才能在面对复杂的json对象时,从中获取全部的键值;接下来就是属于从Map中取值以及Spinner组件的使用方法啦
setSpinnerData();//为spinner设置值
//所谓的省市县三级联动,无非是靠三个嵌套完成的,整个过程是这样的:首先在页面启动的时候
//执行spinner_sheng.setSelection(0);这个方法的效果跟点击Spinner的省按钮选择第一条一样,然后
//然后执行省按钮的点击事件里的updateCity("北京", null);然后在updateCity方法里面执行updateArea(cities[0]);
//默认显示第一个市。然后再在updateArea设置当存在县级单位时选择第一个;当选择省时只是spinner_sheng.setSelection()
//方法执行时参数不是0罢了。因为你选择了省才会为你更新市的信息,选择了市才会为你更新县的消息,所以说这种嵌套是单向性的
}
private void setSpinnerData() {
provinceAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);//系统默认的
for (int i = 0; i < allProv.length; i++) {
//给spinner省赋值,设置默认值
provinceAdapter.add(allProv[i]);//添加每一个省
}
provinceAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);//按下的效果
spinner_sheng.setAdapter(provinceAdapter);
spinner_sheng.setSelection(0);//设置选中的省,默认
//市
cityAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
cityAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_shi.setAdapter(cityAdapter);
//区县
areaAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item);
areaAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner_qu.setAdapter(areaAdapter);
setListener();//设置spinner的点击监听
}
private void setListener() {
spinner_sheng.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
provinceName = adapterView.getSelectedItem() + "";//获取点击列表spinner item的省名字
updateCity(provinceName, null);
Log.i(TAG, "选择了:"+provinceName + "省");
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
spinner_shi.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Log.i(TAG, "你点击了"+parent.getSelectedItem()+"市");
updateArea(parent.getSelectedItem() + "");
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
spinner_qu.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Log.i(TAG, "你点击了"+parent.getSelectedItem());
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
private void updateCity(Object object, Object city) {
String[] cities = cityMap.get(object);
cityAdapter.clear();//清空adapter的数据
for (int i = 0; i < cities.length; i++){
cityAdapter.add(cities[i]);//将这个列表“市”添加到adapter中
}
Log.i(TAG, "这是"+object+"所有的市级行政单位");
cityAdapter.notifyDataSetChanged();//刷新
if (city == null) {
updateArea(cities[0]);//更新区,没有市则默认第一个给它
}
//spinner_shi.setSelection(0);
}
private void updateArea(Object object) {
String[] area = areaMap.get(object);
areaAdapter.clear();//清空
if (area != null) {
for (int i = 0; i < area.length; i++){
areaAdapter.add(area[i]);//填入到这个列表
}
areaAdapter.notifyDataSetChanged();//刷新
Log.i(TAG, "这是"+String.valueOf(area)+"所有的县或区");
}else {
Log.i(TAG, String.valueOf(object)+"没有下一级行政单位");
}
}
private void initCityData() {
int x = 0;
int q =0;
try {
//观察city.json你会发现(如果city.json你打开后是乱码,那就换一种编码方式):
// 第1层 citylist
//第2层 "p" "c"
// 第3层 "n" "a"
// 第4层 "s"
//citylist这个数组里面的包含的子项是一个键值"p"对应的省名和一个键值"c"对应的市或者区的
//数组,其中c数组里面的键值"n"对应的是市名或者区名,"a"对应的是一个数组里面的子项"s"
//均是县名,所以说初始化这个json对象时,其结构有三层最多需要遍历数组三次。取得时候只取每层
//对应名字。比如说遍历第一层,只取p对应的value值就行啦;
JSONArray jsonProv = jsonObject.getJSONArray("citylist");//获取整个json数据,就是获取
// "p"的数量
allProv = new String[jsonProv.length()];//封装数据
for (int i = 0; i < jsonProv.length(); i++) {//遍历第一层数组,获取省
JSONObject jsonP = jsonProv.getJSONObject(i);//jsonArray转jsonObject
String provStr = jsonP.getString("p");//获取所有的省
Log.i("guu", "现在是:"+provStr);
allProv[i] = provStr;//封装所有的省
JSONArray jsonCity = null;
try {
jsonCity = jsonP.getJSONArray("c");//在所有的省中取出所有的市,转jsonArray
Log.i("guu", "现在取出"+provStr+"省的"+jsonCity);
} catch (Exception e) {
continue;
}
//所有的市
String[] allCity = new String[jsonCity.length()];//所有市的长度
for (int c = 0; c < jsonCity.length(); c++) {//遍历第二层数组,获得“c”对应的市或区
JSONObject jsonCy = jsonCity.getJSONObject(c);//转jsonObject
String cityStr = jsonCy.getString("n");//取出所有的市
allCity[c] = cityStr;//封装市集合
Log.i("guu", "allCity["+c+"]="+cityStr);
JSONArray jsonArea = null;
x++;//我想知道这个json文件中记录了多少个市
try {
jsonArea = jsonCy.getJSONArray("a");//在从所有的市里面取出所有的区,转jsonArray
} catch (Exception e) {
Log.i(TAG, cityStr+"没有下一级行政单位");
continue;//如果市或者区这一层不能取出键值"a"的数组,说明没有,那么下面的
//for循环不搞了,直接进行本本层次的for循环;
}
String[] allArea = new String[jsonArea.length()];//所有的区
for (int a = 0; a < jsonArea.length(); a++) {//遍历第三层数组,取出所有的区
JSONObject jsonAa = jsonArea.getJSONObject(a);
String areaStr = jsonAa.getString("s");//获取所有的区
allArea[a] = areaStr;//封装起来
q++;//我想知道这个json文件中记录了多少个省
}
areaMap.put(cityStr, allArea);//第三层数组被遍历结束后某个市取出所有的区集合
//假如现在是刚遍历完淄博市的所有区县,那么这个cityStr键值对应的名字就是“淄博市”
//allArea里面所存储的就是淄博市所有的区县的名称
}//这个第二层for循环跑起来时,每次都会执行 String[] allArea = new String[jsonArea.length()];
//全中国有多少市就会执行多少次,有多少县或者县级区就会创建多少个String型变量。
cityMap.put(provStr, allCity);//第二层数组被遍历结束后某个省取出所有的市,
Log.i(TAG, "中国有"+i+"个省");
}//这个第一层for循环跑起来时,每次都会执行 allProv = new String[jsonProv.length()];
//全中国有多少省加上直辖市就会执行多少次,有多少市或者市级区就会创建多少个String型变量。
//第一层数组遍历结束
} catch (JSONException e) {
e.printStackTrace();
}
Log.i("guu", "中国有"+x+"个市;有"+q+"个区县");
jsonObject = null;//清空所有的数据
}
private void initJsonData() {
try {
StringBuffer sb = new StringBuffer();
InputStream is = getAssets().open("city.json");//打开json数据
Log.i("guu", "打开city.json文件夹");
byte[] by = new byte[is.available()];//转字节
int len = -1;
while ((len = is.read(by)) != -1) {
sb.append(new String(by, 0, len, "gb2312"));//根据字节长度设置编码
}
is.close();//关闭流
jsonObject = new JSONObject(sb.toString());//创建json对象,内容为整个city.json的值
} catch (Exception e) {
e.printStackTrace();
}
}
private void initView() {
spinner_sheng = (Spinner) findViewById(R.id.sheng);
spinner_shi = (Spinner) findViewById(R.id.shi);
spinner_qu = (Spinner) findViewById(R.id.qu);
}
}
我把源码放在这里了:下载后需要解压这个文件然后导入city.json到assets文件里,怎么导入我就不说了;点击打开链接
导入后是这个样子的: