1. 程式人生 > >WheelView地區選擇三級聯動詳解

WheelView地區選擇三級聯動詳解

1. 效果

最近需要做一個地區選擇的功能,但是在網上和github上找了很久都沒找到滿意的,然後朋友推薦了一個給我,我花了點時間把程式碼大致看懂並改成我想要的,並寫上我的理解。效果如圖:
這裡寫圖片描述

2. 注意

a. 首先我們要明白,網上這寫三級聯動的demo,不管是把資料庫檔案放在raw還是assets中,我們都要進行復制,將這個檔案複製到app目錄下,即

/data/data/"+context.getPackageName()+"/databases/

至於到底放在raw還是assets中哪裡好些,我也在網上查了下,見這篇部落格這裡點選 ,但是按照這裡面說的好像.db檔案最好放在assets中,但是這個demo中拿過來,原來的.db檔案就是放在raw中,所以我也沒改了。
b.

最重要的一點,因為我們一般都是將選擇完以後的資料都要上傳到伺服器中,但是因為每個後臺要求的不一樣,所以對於這個地區的.db檔案也要求不一樣,所以我們最主要的是學會讀取資料庫檔案然後通過程式碼形成聯動,舉個例子,比如美團有自己的一個數據庫,我們定位湖南長沙芙蓉區,然後進行地區選擇,將嶽麓區上傳伺服器並且請求資料下來,但是我們一般不會直接上傳文字,一個是怕重名,還有一個就是文字不會絕對的準確,比如兩個字的地名黃山 和 黃 山,看起來都對,但是你們的後臺伺服器接受的引數是黃山,而且使用別人的資料庫中使黃 山,那就出事了,所以我們一般都是傳編碼,但是每個資料庫中城市編碼一般都是不一樣,所以我們需要找後臺拿匹配的資料庫檔案或者js檔案。如圖所示,兩份資料庫,一份是朋友推薦自帶的,例外一份是我們的。
這裡寫圖片描述

這裡寫圖片描述
我的資料庫中省市區只有一個表,而那份卻有三個表,省 市 區三個表。並且編碼也不一樣,可以明顯看出來。

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. 原始碼地址這裡點選