WheelView地區選擇三級聯動詳解
1. 效果
最近需要做一個地區選擇的功能,但是在網上和github上找了很久都沒找到滿意的,然後朋友推薦了一個給我,我花了點時間把程式碼大致看懂並改成我想要的,並寫上我的理解。效果如圖:
2. 注意
a. 首先我們要明白,網上這寫三級聯動的demo,不管是把資料庫檔案放在raw還是assets中,我們都要進行復制,將這個檔案複製到app目錄下,即
/data/data/"+context.getPackageName()+"/databases/
至於到底放在raw還是assets中哪裡好些,我也在網上查了下,見這篇部落格這裡點選 ,但是按照這裡面說的好像.db檔案最好放在assets中,但是這個demo中拿過來,原來的.db檔案就是放在raw中,所以我也沒改了。
b.
我的資料庫中省市區只有一個表,而那份卻有三個表,省 市 區三個表。並且編碼也不一樣,可以明顯看出來。
3. 乾貨
a. 佈局就直接貼程式碼了,很簡單,mainactivity中就一個按鈕,而popupwindow中是三個wheelview,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background ="#00000000"
android:gravity="bottom"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/ly_myinfo_changeaddress_child"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#ffffff"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="44dp"
>
<View
android:background="@color/silver"
android:layout_width="match_parent"
android:layout_height="0.5dp" />
<TextView
android:id="@+id/btn_myinfo_cancel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="18dp"
android:text="取消"
android:gravity="center"
android:layout_alignParentLeft="true"
android:layout_marginRight="15dip"
android:textColor="#e84515"
android:textSize="14sp" />
<TextView
android:id="@+id/btn_myinfo_sure"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="center"
android:text="完成"
android:textColor="#e84515"
android:paddingRight="18dp"
android:textSize="14sp" />
</RelativeLayout>
<View android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#d8d8d8"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="190dip"
android:orientation="horizontal"
android:gravity="center_vertical">
<guozhaohui.com.wlylocationchoose.locationchoose.WheelView
android:id="@+id/provinceView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<guozhaohui.com.wlylocationchoose.locationchoose.WheelView
android:id="@+id/cityView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<guozhaohui.com.wlylocationchoose.locationchoose.WheelView
android:id="@+id/districtView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
b. 因為上面說了,需要將檔案copy到app目錄下,所以直接最好這程式碼在application中寫,
package guozhaohui.com.wlylocationchoose;
import android.app.Application;
import java.io.InputStream;
import guozhaohui.com.wlylocationchoose.locationchoose.CityDataHelper;
/**
* Created by ${GuoZhaoHui} on 2017/2/13.
* Abstract:
*/
public class MyApplication extends Application {
private CityDataHelper dataHelper;
@Override
public void onCreate() {
super.onCreate();
/**
* 放在application中,讓app一啟動就把raw中檔案copy到 "/data/data/"+context.getPackageName()+"/databases/"
* 這是app讀取資料的方法,不管是將資料庫檔案放在raw或者assets中都是一樣
*/
dataHelper=CityDataHelper.getInstance(this);
InputStream in = this.getResources().openRawResource(R.raw.city);
dataHelper.copyFile(in,CityDataHelper.DATABASE_NAME,CityDataHelper.DATABASES_DIR);
}
}
c. popupwindow不是本次的重點也直接貼程式碼,
View popupView = LayoutInflater.from(this).inflate(R.layout.popup_locationchoose, null);
mPopupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
mPopupWindow.setTouchable(true);
mPopupWindow.setFocusable(true);
mPopupWindow.setOutsideTouchable(true);
mPopupWindow.setAnimationStyle(R.style.popup_locationchoose_bottom);
// pickText = (TextView)popupView.findViewById(R.id.tv_pickText);
provinceView = (WheelView)popupView.findViewById(R.id.provinceView);
cityView = (WheelView)popupView.findViewById(R.id.cityView);
districtView = (WheelView)popupView.findViewById(R.id.districtView);
//確定或者取消
btn_myinfo_sure = (TextView)popupView.findViewById(R.id.btn_myinfo_sure);
btn_myinfo_cancel = (TextView) popupView.findViewById(R.id.btn_myinfo_cancel);
btn_myinfo_cancel.setOnClickListener(this);
btn_myinfo_sure.setOnClickListener(this);
設定三個wheelview的可見條目數
provinceView.setVisibleItems(7);
cityView.setVisibleItems(7);
districtView.setVisibleItems(7);
為三個 wheelview新增滑動事件
// 新增change事件
provinceView.addChangingListener(this);
// 新增change事件
cityView.addChangingListener(this);
// 新增change事件
districtView.addChangingListener(this);
c. 拿到操作資料的物件SQLiteDatabase
db = dataHelper.openDataBase();
觀察資料庫檔案可知這表中是根據欄位level來判斷省市區的,如圖
同時我們也可知這省市區三個模型中的欄位都是一樣的,都是
private int cityID;
private int parentId;
private int level;
private String name;
private String pinyin;
不清楚的可以自己查下表,如圖,我查一個省
所以我們建立一樣的模型,雖然三個欄位是一樣的,建一個就可以了,但是為了標準最好還是建三個。
package guozhaohui.com.wlylocationchoose.locationchoose.model;
public class ProvinceModel {
private int cityID;
private int parentId;
private int level;
private String name;
private String pinyin;
public int getCityID() {
return cityID;
}
public void setCityID(int cityID) {
this.cityID = cityID;
}
public int getParentId() {
return parentId;
}
public void setParentId(int parentId) {
this.parentId = parentId;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPinyin() {
return pinyin;
}
public void setPinyin(String pinyin) {
this.pinyin = pinyin;
}
}
進行sql查詢,將查詢到的結果儲存在cursor中,然後進行一行行的迴圈遍歷,然後將遍歷一行的物件新增到這個物件的集合中。這裡得到省的集合。
public List<ProvinceModel> getProvice(SQLiteDatabase db){
String sql="SELECT * FROM ChooseCityModel where level = 1 ORDER BY cityID";
Cursor cursor = db.rawQuery(sql,null);
List<ProvinceModel> list=new ArrayList<ProvinceModel>();
if (cursor!=null&&cursor.getCount() > 0) {
while (cursor.moveToNext()){
ProvinceModel provinceModel=new ProvinceModel();
provinceModel.setCityID(cursor.getInt(cursor.getColumnIndex("cityID")));
provinceModel.setParentId(cursor.getInt(cursor.getColumnIndex("parentId")));
provinceModel.setLevel(cursor.getInt(cursor.getColumnIndex("level")));
provinceModel.setName(cursor.getString(cursor.getColumnIndex("name")));
provinceModel.setPinyin(cursor.getString(cursor.getColumnIndex("pinyin")));
list.add(provinceModel);
}
}
return list;
}
根據表的結構,得到相應的sql語句,希望根據上一級省的cityId得到下面的市,其實換句話說本級市的parentId就是上一級的cityid,不清楚的可以將sql語句查一遍,驗證下對不對,如圖
得到相應省下面市的集合
public List<CityModel> getCityByParentId(SQLiteDatabase db, String code){
String sql="SELECT * FROM ChooseCityModel WHERE level = 2 and parentId = ? ORDER BY cityID";
Cursor cursor = db.rawQuery(sql,new String[]{code});
List<CityModel> list=new ArrayList<CityModel>();
if (cursor!=null&&cursor.getCount() > 0) {
while (cursor.moveToNext()){
CityModel cityModel=new CityModel();
cityModel.setCityID(cursor.getInt(cursor.getColumnIndex("cityID")));
cityModel.setParentId(cursor.getInt(cursor.getColumnIndex("parentId")));
cityModel.setLevel(cursor.getInt(cursor.getColumnIndex("level")));
cityModel.setName(cursor.getString(cursor.getColumnIndex("name")));
cityModel.setPinyin(cursor.getString(cursor.getColumnIndex("pinyin")));
list.add(cityModel);
}
}
return list;
}
區也是一樣的,直接貼程式碼了
public List<DistrictModel> getDistrictById(SQLiteDatabase db, String code){
//注意這裡的parentId其實就是上一級的cityID
String sql="SELECT * FROM ChooseCityModel WHERE level = 3 and parentId = ? ORDER BY cityID";
Cursor cursor = db.rawQuery(sql,new String[]{code});
List<DistrictModel> list=new ArrayList<DistrictModel>();
if (cursor!=null&&cursor.getCount() > 0) {
while (cursor.moveToNext()){
DistrictModel districtModel=new DistrictModel();
districtModel.setCityID(cursor.getInt(cursor.getColumnIndex("cityID")));
districtModel.setParentId(cursor.getInt(cursor.getColumnIndex("parentId")));
districtModel.setLevel(cursor.getInt(cursor.getColumnIndex("level")));
districtModel.setName(cursor.getString(cursor.getColumnIndex("name")));
districtModel.setPinyin(cursor.getString(cursor.getColumnIndex("pinyin")));
list.add(districtModel);
}
}
return list;
}
d. 對彈出popuwindow顯示的wheelview進行初始化,註釋都寫在程式碼裡,
private void initpopData() {
//初始化資料
dataHelper = CityDataHelper.getInstance(this);
db = dataHelper.openDataBase();
provinceDatas = dataHelper.getProvice(db);
if (provinceDatas.size() > 0) {
//彈出popup時,省wheelview中當前的省其實就是省集合的第一個
mCurrentProvince = provinceDatas.get(0).getName();
//根據省cityid查詢到第一個省下面市的集合
cityDatas = dataHelper.getCityByParentId(db, provinceDatas.get(0).getCityID()+"");
}
if (cityDatas.size() > 0) {
//根據市cityid查詢到第一個市集合下面區的集合
districtDatas = dataHelper.getDistrictById(db, cityDatas.get(0).getCityID()+"");
}
//wheelview的介面卡程式碼
provinceAdapter = new ProvinceAdapter(this, provinceDatas);
provinceAdapter.setTextSize(TEXTSIZE);//設定字型大小
provinceView.setViewAdapter(provinceAdapter);
updateCitys();
updateAreas();
}
更新省下面市的wheelview內容,註釋很清楚,直接上程式碼
private void updateCitys() {
int pCurrent = provinceView.getCurrentItem();
if (provinceDatas.size() > 0) {
//這裡是必須的的,上面得到的集合只是第一個省下面所有市的集合及第一個市下面所有區的集合
//這裡得到的是相應省下面對應市的集合
cityDatas = dataHelper.getCityByParentId(db, provinceDatas.get(pCurrent).getCityID()+"");
} else {
cityDatas.clear();
}
citysAdapter = new CitysAdapter(this, cityDatas);
citysAdapter.setTextSize(TEXTSIZE);
cityView.setViewAdapter(citysAdapter);
if (cityDatas.size() > 0) {
//預設省下面 市wheelview滑動第一個,顯示第一個市
cityView.setCurrentItem(0);
mCurrentCity = cityDatas.get(0).getName();
} else {
mCurrentCity = "";
}
updateAreas();
}
第三個wheelview和第二個一樣的,程式碼直接上
private void updateAreas() {
int cCurrent = cityView.getCurrentItem();
if (cityDatas.size() > 0) {
districtDatas = dataHelper.getDistrictById(db, cityDatas.get(cCurrent).getCityID()+"");
} else {
districtDatas.clear();
}
areaAdapter = new AreaAdapter(this, districtDatas);
areaAdapter.setTextSize(TEXTSIZE);
districtView.setViewAdapter(areaAdapter);
if (districtDatas.size() > 0) {
mCurrentDistrict = districtDatas.get(0).getName();
districtView.setCurrentItem(0);
} else {
mCurrentDistrict = "";
}
}
4. 道友留步
1.因為我朋友也是在網上哪裡找到的demo,所以這原版是誰的也不知道了。
2. 原始碼地址這裡點選