Android实现省市区三级联动的效果
效果图:

一、简介
二、实现步骤
/*** 将assets下的资源复制到应用程序的databases目录下* @param context 上下文* @param fileName assets下的资源的文件名*/public static void copyAssetsToDB(Context context, String fileName) throws IOException {//数据库的存储路径,该路径在:data/data/包名/databases目录下,String destPath = context.getDatabasePath("").getPath();Log.i("tag","path---->"+destPath);File file = new File(destPath);if (!file.exists()) {file.mkdirs(); //创建目录}else {return;}//打开assest文件,获得输入流InputStream is = context.getAssets().open(fileName);BufferedInputStream bis = new BufferedInputStream(is);//获得写入文件的输出流FileOutputStream fos = new FileOutputStream(destPath +File.separator + fileName);BufferedOutputStream bos = new BufferedOutputStream(fos);byte[] data = new byte[2 * 1024];int len;while ((len = bis.read(data)) != -1){bos.write(data, 0, len);}bos.flush();bis.close();bos.close();}
//打开数据库文件的完整路径,来获得sqlite数据库对象SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(dbPath, null);//数据库查询操作Cursor cursor = database.rawQuery(sql, null);if (cursor != null) {while (cursor.moveToNext()) {//......查询结果的操作}cursor.close();}
xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.zx.copydatabase.MainActivity"android:orientation="horizontal"android:gravity="center"android:padding="10dp">android:id="@+id/province_picker"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:focusable="true"android:focusableInTouchMode="true"/>android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="省"android:textColor="#000"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"/>android:id="@+id/city_picker"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:focusable="true"android:focusableInTouchMode="true"/>android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="市"android:textColor="#000"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"/>android:id="@+id/area_picker"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:focusable="true"android:focusableInTouchMode="true"/>android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="区"android:textColor="#000"android:layout_marginLeft="10dp"android:layout_marginRight="10dp"/>
实现这个功能,遇到了的2个问题和坑,文章最后给出详细解决
public class MainActivity extends AppCompatActivity {private static final String DB_NAME = "mydb.db"; //数据库名称private NumberPicker provincePicker, cityPicker, areaPicker; //省市区选择器private AddressManager manager; //自定义的地址管理器@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//拷贝assets下的文件"mydb.db"到 应用的databases目录下try {MyUtils.copyAssetsToDB(this, DB_NAME);} catch (IOException e) {e.printStackTrace();}//初始化“地址管理器”对象manager = new AddressManager(getDatabasePath(DB_NAME).getPath());initView();initProvince();}//初始化Viewprivate void initView() {provincePicker = (NumberPicker) findViewById(R.id.province_picker);cityPicker = (NumberPicker) findViewById(R.id.city_picker);areaPicker = (NumberPicker) findViewById(R.id.area_picker);}//设置“省份”选择器的数据private void initProvince() {ListprovinceList = manager.getProvinces(); //获取所有的省份 //设置省份的值final String[] provinces = provinceList.toArray(new String[provinceList.size()]);provincePicker.setDisplayedValues(provinces);provincePicker.setMinValue(0); //设置第一个值provincePicker.setMaxValue(provinces.length - 1); //设置最后一个值//默认的省份的位置int defaultProvince = provinces.length / 2 == 0 ? provinces.length / 2 : provinces.length / 2 + 1;provincePicker.setValue(defaultProvince); //设置当前值//根据当前默认的省份来设置对应的市showCityByProvince(provinces[provincePicker.getValue()]);//省份的选择事件provincePicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {@Overridepublic void onValueChange(NumberPicker picker, int oldVal, int newVal) {Log.i("tag", "newVal--->" + newVal);showCityByProvince(provinces[newVal]);}});}//根据选择的省份名来显示城市名private void showCityByProvince(String province) {ListcityList = manager.getCities(province); //设置城市的值final String[] cities = cityList.toArray(new String[cityList.size()]);cityPicker.setDisplayedValues(null); //清空之前的选择的数据cityPicker.setMinValue(0); //设置第一个值cityPicker.setMaxValue(cities.length - 1); //设置最后一个值cityPicker.setDisplayedValues(cities);//根据当前默认的城市来设置对应的区showAreaByCity(cities[cityPicker.getValue()]);//城市的选择事件cityPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {@Overridepublic void onValueChange(NumberPicker picker, int oldVal, int newVal) {showAreaByCity(cities[newVal]);}});}//根据选择的城市名来显示区名private void showAreaByCity(String city) {ListareaList = manager.getAreas(city); //设置区的值String[] areas = areaList.toArray(new String[areaList.size()]);areaPicker.setDisplayedValues(null);areaPicker.setMinValue(0); //设置第一个值areaPicker.setMaxValue(areas.length - 1); //设置最后一个值areaPicker.setDisplayedValues(areas);}}
/*** "省市区"数据库操作类*/public class AddressManager {//ssq表的字段的名字private static final String TABLE_NAME = "ssq"; //表名private static final String PROVINCE = "province"; //省份名称private static final String CITY = "city"; //市的名称private static final String AREA = "area"; //区的名称private SQLiteDatabase database; //sqlite数据库对象/*** @param dbPath 数据库中“省市区”表的路径*/public AddressManager(String dbPath) {database = SQLiteDatabase.openOrCreateDatabase(dbPath, null);}/*** 查询所有省份** @return 所有的省份名称*/public ListgetProvinces() { String sql = "select distinct " + PROVINCE + " from " + TABLE_NAME;Cursor cursor = database.rawQuery(sql, null);ListprovinceList = new ArrayList<>(); if (cursor != null) {while (cursor.moveToNext()) {String province = cursor.getString(cursor.getColumnIndex("province"));Log.i("tag", "province----->" + province);provinceList.add(province);}cursor.close();}return provinceList;}/*** 查询指定省份或直辖市的所有市的集合** @param province 省份名称* @return 查询到的所有城市*/public ListgetCities(String province) { String sql = "select distinct " + CITY + " from " + TABLE_NAME + " where " + PROVINCE + " = ?";Cursor cursor = database.rawQuery(sql, new String[]{province});ListaddressList = new ArrayList<>(); while (cursor.moveToNext()) {String city = cursor.getString(cursor.getColumnIndex(CITY));Log.i("tag", "city----->" + city);addressList.add(city);}//关闭游标cursor.close();return addressList;}/*** 查询指定市的所有区的列表的List集合** @param city 市的名称* @return 查询到的所有区*/public ListgetAreas(String city) { //获取指定市的所有区的列表的sql语句String sql = "select distinct " + AREA + " from " + TABLE_NAME + " where " + CITY + " = ?";Cursor cursor = database.rawQuery(sql, new String[]{city});ListaddressList = new ArrayList<>(); while (cursor.moveToNext()) {//获取区的名称String area = cursor.getString(cursor.getColumnIndex(AREA));Log.i("tag", "area----->" + area);//把所有的区添加到List集合addressList.add(area);}//关闭游标cursor.close();return addressList;}}
遇到的问题和坑
np = (NumberPicker) findViewById(R.id.numberPicker1);String[] city = {"北京","上海","广州","深圳","成都","天津"};np.setDisplayedValues(city);np.setMinValue(0); //设置显示的第一个数据np.setMaxValue(city.length - 1); //设置显示的最后一个数据
1、当前NumberPicker的最大值大于数组大小时,先setMaxValue再setDisplayedValues。
2、当前NumberPicker的最大值小于数组大小时,先setDisplayedValues再setMaxValue。
private void updateCitySelector() {int oldMax = cityPicker.getMaxValue();int newMax = mCitys.length - 1;if(newMax > oldMax) {cityPicker.setDisplayedValues(mCitys);cityPicker.setMaxValue(newMax);} else {cityPicker.setMaxValue(newMax);cityPicker.setDisplayedValues(mCitys);}}
解决方案二(推荐):
在设置最大值和最新数组数据前,先将之前设置过的数据设为null。
private void updateCitySelector() {cityPicker.setDisplayedValues(null);cityPicker.setMaxValue(mCitys.length - 1);cityPicker.setDisplayedValues(mCitys);}
评论
