Android中省市區的三級聯動Android-wheel
阿新 • • 發佈:2019-01-04
在專案開發中或多或少都會遇到地址的選擇如淘寶,美團等。在ios中有PickerView可以很輕鬆的實現地區之間的輪滑和級聯,在Android中,並沒有可以直接實現此功能的控制元件,為了實現功能和效果的美觀,只有通過自定義控制元件。在GitHub上有一個叫做Android-wheel的開源控制元件:https://github.com/maarek/android-wheel。也可以使用TimePickerDialog:https://github.com/JZXiang/TimePickerDialogTimePickerDialog集成了Android-wheel。Android-wheel是一個非常好用的元件,對於資料適配介面的抽取和事件的回撥都做了抽取,程式碼的耦合度低,唯一不足就是在介面的定製這塊,通過原始碼來實現的三級聯動開起來比較醜如果想要體現出更好的效果,則需要自己手動的修改原始碼。下面是實際專案中的一個專案:
1、下載Android-wheel專案,並匯入wheel依賴
2、講資料檔案本專案中為json格式的如下:
[ { "parent_id": "1", "region_id": "2", "region_name": "北京", "sub": [ { "parent_id": "2", "region_id": "52", "region_name": "北京", "sub": [ { "parent_id": "52","region_id": "500", "region_name": "東城區" }, { "parent_id": "52", "region_id": "501", "region_name": "西城區" },
3、在XML佈局中設定WheelView的控制元件,如下圖:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent"4、主函式(在這裡城市的選擇是在PopupWindow中通過點選彈出來的)android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="請選擇城市" android:gravity="center"/> <LinearLayout android:id="@+id/line" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <kankan.wheel.widget.WheelView android:id = "@+id/province" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> <kankan.wheel.widget.WheelView android:id = "@+id/city" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> <kankan.wheel.widget.WheelView android:id = "@+id/area" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> </LinearLayout> <Button android:id="@+id/btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="確定" android:layout_gravity="right"/> </LinearLayout>
public class MainActivity extends AppCompatActivity implements OnWheelChangedListener { /** * 所有省 */ private String[] mProvinceDatas; /** * key - 省 value - 市s */ private Map<String, String[]> mCitisDatasMap = new HashMap<String, String[]>(); /** * key - 市 values - 區s */ private Map<String, String[]> mAreaDatasMap = new HashMap<String, String[]>(); /** * 省的名稱和id * key--name values--ID */ private Map<String,String> mProvinceName_Id = new HashMap<>(); /** * 市的名稱和id * key--name values--ID */ private Map<String,String> mCityName_Id = new HashMap<>(); /** * 區的名稱和id * key--name values--ID */ private Map<String,String> mAreaName_Id = new HashMap<>(); /** * 當前省的名稱 */ private String mCurrentProviceName; /** * 當前市的名稱 */ private String mCurrentCityName; /** * 當前區的名稱 */ private String mCurrentAreaName =""; private String mJson; private WheelView mMProvince; private WheelView mCity; private WheelView mMArea; private Button mMButton; private TextView mAddressText; private TextView mAddressIdText; private LinearLayout mMLine; private PopupWindow mMPopupWindow; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAddressText = (TextView) findViewById(R.id.address); mAddressIdText = (TextView)findViewById(R.id.address_id); init(); } public void btnClick(View view){ getSelect(); showData(); } //顯示PopupWindow視窗 private void showData() { //獲取父佈局 View rootView = LayoutInflater.from(this).inflate(R.layout.activity_main,null); //相對父佈局的位置在底部彈出 mMPopupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, 0); } //初始化PopupWindow private void init(){ View v = LayoutInflater.from(this).inflate(R.layout.popwindow_layout,null); //設定彈窗的寬和高 mMPopupWindow = new PopupWindow(v, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); //點選外部彈窗消失 mMPopupWindow.setBackgroundDrawable(new BitmapDrawable()); mMPopupWindow.setOutsideTouchable(true); mMLine = (LinearLayout)v. findViewById(R.id.line); //省的控制元件 mMProvince = (WheelView)v. findViewById(R.id.province); //市的控制元件 mCity = (WheelView)v. findViewById(R.id.city); //區的控制元件 mMArea = (WheelView)v. findViewById(area); mMButton = (Button)v. findViewById(R.id.btn); //點選確定按鈕,把選擇的地址返回,同事彈窗消失 mMButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showChoose(v); getValue(); //把當前所選地賦值給mAddressText mAddressText.setText(mCurrentProviceName+"--"+mCurrentCityName+"--"+mCurrentAreaName); mMPopupWindow.dismiss(); } }); } //PopupWindow介面的顯示效果 public void getSelect(){ String json = initJsonData(); parseJson(json); mMProvince.setViewAdapter(new ArrayWheelAdapter<String>(this,mProvinceDatas)); //新增change事件 mMProvince.addChangingListener(this); //新增change事件 mCity.addChangingListener(this); //新增change事件 mMArea.addChangingListener(this); //同一時間可見的item個數 mMProvince.setVisibleItems(6); mCity.setVisibleItems(6); mMArea.setVisibleItems(6); updateCities(); updateAreas(); } /** * 根據當前的市,更新區 WheelView的資訊 */ private void updateAreas(){ int pCurrent = mCity.getCurrentItem(); //當前市的名稱 mCurrentCityName = mCitisDatasMap.get(mCurrentProviceName)[pCurrent]; String[] areas = mAreaDatasMap.get(mCurrentCityName); if(areas == null){ areas = new String[]{""}; } mMArea.setViewAdapter(new ArrayWheelAdapter<String>(this,areas)); mMArea.setCurrentItem(0); //得到區名字的初始值為第0個位置的值 mCurrentAreaName = mAreaDatasMap.get(mCurrentCityName)[0]; } /** * 根據當前的省,更新市WheelView的資訊 */ private void updateCities() { int pCurrent = mMProvince.getCurrentItem(); //當前省的名稱 mCurrentProviceName = mProvinceDatas[pCurrent]; String[] cities = mCitisDatasMap.get(mCurrentProviceName); if (cities == null) { cities = new String[] { "" }; } mCity.setViewAdapter(new ArrayWheelAdapter<String>(this, cities)); mCity.setCurrentItem(0); updateAreas(); } //讀取本地json生成json字串 private String initJsonData(){ try { InputStream is = getResources().getAssets().open("地區json.json"); byte[] buffer = new byte[is.available()]; is.read(buffer); mJson = new String(buffer,"UTF-8"); } catch (IOException e) { e.printStackTrace(); } return mJson; } //解析json,並把解析出來的資料存放到不同的集合和Map中 private void parseJson(String str){ try { JSONArray jsonArray = new JSONArray(str); mProvinceDatas = new String[jsonArray.length()]; for(int i = 0;i<jsonArray.length();i++){ JSONObject jsonp = jsonArray.getJSONObject(i); String provinceName = jsonp.getString("region_name"); String provinceId = jsonp.getString("region_id"); mProvinceName_Id.put(provinceName,provinceId); mProvinceDatas[i] = provinceName; JSONArray jsonc = jsonp.getJSONArray("sub"); String [] citiesData = new String[jsonc.length()]; for(int j = 0;j<jsonc.length();j++){ JSONObject jsoncity = jsonc.getJSONObject(j); String cityName = jsoncity.getString("region_name"); String cityId = jsoncity.getString("region_id"); mCityName_Id.put(cityName,cityId); citiesData[j] = cityName; JSONArray jsona = jsoncity.getJSONArray("sub"); String [] areaData = new String[jsona.length()]; for(int k = 0;k<jsona.length();k++){ String areaName = jsona.getJSONObject(k).getString("region_name"); String areaId = jsona.getJSONObject(k).getString("region_id"); mAreaName_Id.put(areaName,areaId); areaData[k] = areaName; } mAreaDatasMap.put(cityName,areaData); } mCitisDatasMap.put(provinceName,citiesData); } } catch (JSONException e) { e.printStackTrace(); } } //活動省或市或區的時候觸發 @Override public void onChanged(WheelView wheel, int oldValue, int newValue) { if(wheel == mMProvince){ updateCities(); }else if(wheel == mCity){ updateAreas(); }else if(wheel == mMArea){ mCurrentAreaName = mAreaDatasMap.get(mCurrentCityName)[newValue]; } } //獲得選擇地址的id private void getValue(){ //獲取省的ID Object provinceId = new Object(); provinceId = mProvinceName_Id.get(mCurrentProviceName); //獲取市的ID Object cityId = new Object(); cityId = mCityName_Id.get(mCurrentCityName); //獲取區的ID Object areaId = new Object(); areaId = mAreaName_Id.get(mCurrentAreaName); //將ID賦值給TextView(傳伺服器) mAddressIdText.setText(provinceId+"----"+cityId+"----"+areaId); } public void showChoose(View view) { Toast.makeText(this, mCurrentProviceName + mCurrentCityName + mCurrentAreaName, Toast.LENGTH_LONG).show(); } }由於程式碼中都有詳細的註釋,這裡不再贅述,源生的程式碼所取得的結果如下:
通過更改原始碼可以使得選擇器更簡潔漂亮一些,我做出的效果如下: