一 使用平台:
使用和風天氣提供的接口來查詢天氣。
全國的省市數據:會返回一段json格式的數據。
http://guolin.tech/api/china
二 創建數據庫和表
先在com.Mask weather.android包下新建幾個包,用來進行存儲。
其中db用來存放數據庫模型相關的代碼,gson用於存放GSON模型相關的代碼,service包用於存放服務相關diamante,util用於存放工具相關的代碼。
1.添加依賴庫的聲明
dependencies { compile 'org.litepal.android:core:1.4.1' compile 'com.squareup.okhttp3:okhttp:3.10.0' compile 'com.google.code.gson:gson:2.7' compile 'com.github.bumptech.glide:glide:3.7.0' }
其中包括litepal來對數據庫進行操作,okhttp對網絡請求進行操作,gson對json數據進行解析,glide用於加載展示圖片。
2.添加表對應的三個類:
在db下簡歷三個類,對應數據庫中的三張表。而litepal中的每一個映射的實體類都要繼承自DataSupport類。
省表:對應province類。
1 public class Province extends DataSupport { 2 3 private int id; 4 private String provinceName; 5 private int provinceCode; 6 7 public int getId() { 8 return id; 9 } 10 11 public void setId(int id) { 12 this.id = id; 13 } 14 15 public String getProvinceName() { 16 return provinceName; 17 } 18 19 public void setProvinceName(String provinceName) { 20 this.provinceName = provinceName; 21 } 22 23 public int getProvinceCode() { 24 return provinceCode; 25 } 26 27 public void setProvinceCode(int provinceCode) { 28 this.provinceCode = provinceCode; 29 } 30 }
市表:city類
1 public class City extends DataSupport { 2 private int id; 3 private String cityName; 4 private int cityCode; 5 private int provinceId; 6 7 public int getId() { 8 return id; 9 } 10 11 public void setId(int id) { 12 this.id = id; 13 } 14 15 public String getCityName() { 16 return cityName; 17 } 18 19 public void setCityName(String cityName) { 20 this.cityName = cityName; 21 } 22 23 public int getCityCode() { 24 return cityCode; 25 } 26 27 public void setCityCode(int cityCode) { 28 this.cityCode = cityCode; 29 } 30 31 public int getProvinceId() { 32 return provinceId; 33 } 34 35 public void setProvinceId(int provinceId) { 36 this.provinceId = provinceId; 37 } 38 }
縣表:county類
1 public class County extends DataSupport { 2 3 private int id; 4 private String countyName; 5 private String weatherId; 6 private int cityId; 7 8 public int getId() { 9 return id; 10 } 11 12 public void setId(int id) { 13 this.id = id; 14 } 15 16 public String getCountyName() { 17 return countyName; 18 } 19 20 public void setCountyName(String countyName) { 21 this.countyName = countyName; 22 } 23 24 public String getWeatherId() { 25 return weatherId; 26 } 27 28 public void setWeatherId(String weatherId) { 29 this.weatherId = weatherId; 30 } 31 32 public int getCityId() { 33 return cityId; 34 } 35 36 public void setCityId(int cityId) { 37 this.cityId = cityId; 38 } 39 }
3.配置litepal.xml文件
在app/src/main中new一個Directory,創建一個assets目錄,新建一個litepal.xml文件。
1 <litepal> 2 <!--數據庫名稱和版本--> 3 <dbname value="Mask_weather"/> 4 <version value="1"/> 5 <!--將三個實體類添加到映射的列表中--> 6 <list> 7 <mapping class="com.example.maskweather.db.City"/> 8 <mapping class="com.example.maskweather.db.County"/> 9 <mapping class="com.example.maskweather.db.Province"/> 10 </list> 11 12 </litepal>
配置LitePalApplication。修改AndroidManifest文件如下:
<application android:name="org.litepal.LitePalApplication"
這樣數據庫就建立完成了。
三 遍歷全國省市縣數據
因為在服務器中可以獲取這些數據。服務器:
http://guolin.tech/api/china
1.發送獲取服務器數據的網絡請求
所以和服務器的交互必不可少,所以在util包中增加一個HttpUtil類用來發送網絡請求。
1 /*用來發送http請求*/ 2 public class HttpUtil { 3 public static void sendOkHttpRequest(String address,okhttp3.Callback callback){ 4 OkHttpClient client = new OkHttpClient(); 5 Request request = new Request.Builder().url(address).build(); 6 client.newCall(request).enqueue(callback); 7 } 8 }
2.解析json格式的工具類Utility
解析數據的工具類中存放了三種方法,分別用來解析省級數據、市級數據和縣級數據。都是布爾型的方法,如果解析成功,返回true。解析成功數據使用save方法保存在數據庫中。
1 public class Utility { 2 /* 3 * 解析和處理服務器返回的省級數據 4 * */ 5 public static boolean handleProvinceResponse(String response) { 6 if (!TextUtils.isEmpty(response)) { 7 try { 8 /*使用JSONArray和JSONObject來解析json數據 9 * 傳入的是一個json數組,存放在JSONArray中,依次遍歷放在JSONObject中 10 * 創建一個province實例,取出JSONObject中數據存放*/ 11 JSONArray allProvinces = new JSONArray(response); 12 for (int i = 0; i < allProvinces.length(); i++) { 13 JSONObject provinceObject = allProvinces.getJSONObject(i); 14 Province province = new Province(); 15 province.setProvinceName(provinceObject.getString("name")); 16 province.setProvinceCode(provinceObject.getInt("id")); 17 province.save(); 18 } 19 return true; 20 } catch (JSONException e) { 21 e.printStackTrace(); 22 } 23 } 24 return false; 25 } 26 27 /* 28 * 解析和處理服務器返回的市級數據 29 * */ 30 public static boolean handleCityResponse(String response, int provinceId) { 31 if (!TextUtils.isEmpty(response)) { 32 try { 33 JSONArray allCities = new JSONArray(response); 34 for (int i = 0; i < allCities.length(); i++) { 35 JSONObject cityObject = allCities.getJSONObject(i); 36 City city = new City(); 37 city.setCityName(cityObject.getString("name")); 38 city.setCityCode(cityObject.getInt("id")); 39 city.setProvinceId(provinceId); 40 city.save(); 41 } 42 return true; 43 } catch (JSONException e) { 44 e.printStackTrace(); 45 } 46 } 47 return false; 48 } 49 50 /* 51 * 解析和處理服務器返回的縣級數據 52 * */ 53 public static boolean handleCountyResponse(String response,int cityId){ 54 if(!TextUtils.isEmpty(response)){ 55 try { 56 JSONArray allCounties = new JSONArray(response); 57 for (int i=0;i<allCounties.length();i++){ 58 JSONObject countyObject = allCounties.getJSONObject(i); 59 County county = new County(); 60 county.setCountyName(countyObject.getString("name")); 61 county.setWeatherId(countyObject.getString("weather_id")); 62 county.setCityId(cityId); 63 county.save(); 64 } 65 return true; 66 } catch (JSONException e) { 67 e.printStackTrace(); 68 } 69 } 70 return false; 71 } 72 }
以上就完成了兩個工具類的編寫。
3.使用碎片寫choose_area,xml布局
在這個碎片布局中,定義了一個頭布局作為標題欄,將高度設為actionBar的高度,顏色設為colorPrimary。在頭布局中放了一個Testview和一個后退的button。
在標題欄下面放了一個listview用來存放
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:background="#ffffff" 5 android:orientation="vertical"> 6 7 <!--頭布局作為標題欄 包括一個文本和一個返回按鍵--> 8 <RelativeLayout 9 android:layout_width="match_parent" 10 android:layout_height="?attr/actionBarSize" 11 android:background="?attr/colorPrimary"> 12 13 <TextView 14 android:id="@+id/title_text" 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:layout_centerInParent="true" 18 android:textColor="#fff" 19 android:textSize="20sp" /> 20 21 <Button 22 android:id="@+id/back_button" 23 android:layout_width="25dp" 24 android:layout_height="25dp" 25 android:layout_alignParentLeft="true" 26 android:layout_centerVertical="true" 27 android:layout_marginLeft="10dp" 28 android:background="@drawable/ic_back" /> 29 30 </RelativeLayout> 31 32 <ListView 33 android:id="@+id/list_view" 34 android:layout_width="match_parent" 35 android:layout_height="match_parent" /> 36 </LinearLayout>
4.編寫遍歷省市縣數據的碎片
在編寫碎片時,首先聲明一系列元素。其中包括三個省市縣登記、進度框、Testview、button、和listview原件。列表適配器和數據列表。
省市縣列表以及選中的省市。以及當前選中的級別。
1 public class ChooseAreaFragment extends Fragment { 2 public static final int LEVEL_PROVINCE = 0; 3 public static final int LEVEL_CITY = 1; 4 public static final int LEVEL_COUNTY = 2; 5 6 private ProgressDialog progressDialog; 7 8 private TextView titleText; 9 private Button backButton; 10 private ListView listView; 11 12 private ArrayAdapter<String> adapter; 13 private List<String> dataList = new ArrayList<>(); 14 15 /*省列表*/ 16 private List<Province> provincesList; 17 18 /*市列表*/ 19 private List<City> cityList; 20 21 /*縣列表*/ 22 private List<County> countyList; 23 24 /*選中的省份*/ 25 private Province selectProvince; 26 27 /*選中的城市*/ 28 private City selectCity; 29 30 /*當前選中的級別*/ 31 private int currentLevel;
碎片初始化,並實例化各種控件。同時創造一個listview的適配器用來給listview傳送數據。
1 /*碎片初始化,將各種控件實例化 2 * 創建一個listView的適配器,將適配器與listview綁定*/ 3 @Override 4 public View onCreateView(LayoutInflater inflater, ViewGroup container, 5 Bundle savedInstanceState) { 6 View view = inflater.inflate(R.layout.choose_area, container, false); 7 titleText = (TextView) view.findViewById(R.id.title_text); 8 backButton = (Button) view.findViewById(R.id.back_button); 9 listView = (ListView) view.findViewById(R.id.list_view); 10 adapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, dataList); 11 listView.setAdapter(adapter); 12 return view; 13 }
為listview和button添加點擊事件,currentLevel表示現在的級別。通過點擊事件,調用相應的方法,來查詢相關的省市縣。
1 /*為listview和button設置了點擊事件,currentLevel表示現在處於的級別*/ 2 @Override 3 public void onActivityCreated(Bundle saveInstanceState) { 4 super.onActivityCreated(saveInstanceState); 5 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 6 @Override 7 public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { 8 if (currentLevel == LEVEL_PROVINCE) { 9 selectProvince = provincesList.get(position); 10 queryCities(); 11 } else if (currentLevel == LEVEL_CITY) { 12 selectCity = cityList.get(position); 13 queryCounties(); 14 } 15 } 16 }); 17 backButton.setOnClickListener(new View.OnClickListener() { 18 @Override 19 public void onClick(View view) { 20 if (currentLevel == LEVEL_COUNTY) { 21 queryCities(); 22 } else if (currentLevel == LEVEL_CITY) { 23 queryProvinces(); 24 } 25 } 26 }); 27 queryProvinces(); 28 }
然后是查詢省市縣的具體方法,三個大體流程差不多,都是先設置標題欄的文字和后退按鍵。
然后在省市縣列表先將數據庫中的相應數據加入到列表中。
判斷列表是否為空,如果不為空的話,說明在數據庫中有相關的數據,將適配器的數據datalist清空,然后遍歷這個列表,將列表中的數據都添加到datalist中。
遍歷結束后,更新listview的數據對其進行通知,並將選擇移動到首位。然后改相關的等級。
如果列表為空,調用queryFromService方法,傳入地址和類型,進行對服務器數據的訪問請求。
1 /* 2 * 查詢全國所有的省,先從數據庫查,沒有的話從服務器上查詢 3 * */ 4 private void queryProvinces() { 5 titleText.setText("中國"); 6 backButton.setVisibility(View.GONE);//在最初設為不可見 7 provincesList = DataSupport.findAll(Province.class); 8 if (provincesList.size() > 0) { 9 dataList.clear(); 10 for (Province province : provincesList) { 11 dataList.add(province.getProvinceName()); 12 } 13 adapter.notifyDataSetChanged(); //更新了listview中數據,對其進行通知 14 listView.setSelection(0); //將選擇移到列表開始 15 currentLevel = LEVEL_PROVINCE; 16 } else { 17 String address = "http://guolin.tech/api/china"; 18 queryFromServer(address, "province"); 19 } 20 } 21 22 /* 23 * 查詢選中省內全部的市 優先從數據庫查詢,沒有就從網上查詢 24 * */ 25 private void queryCities() { 26 titleText.setText(selectProvince.getProvinceName()); 27 backButton.setVisibility(View.VISIBLE); 28 cityList = DataSupport.where("provinceid=?", 29 String.valueOf(selectProvince.getId())).find(City.class); 30 if (cityList.size() > 0) { 31 dataList.clear(); 32 for (City city : cityList) { 33 dataList.add(city.getCityName()); 34 } 35 adapter.notifyDataSetChanged(); 36 listView.setSelection(0); 37 currentLevel = LEVEL_CITY; 38 } else { 39 int provinceCode = selectProvince.getProvinceCode(); 40 String address = "http://guolin.tech/api/china/" + provinceCode; 41 queryFromServer(address, "city"); 42 } 43 } 44 45 /* 46 * 查詢選中市內所有的縣 優先從數據庫查詢,其次從服務器查詢 47 * */ 48 private void queryCounties() { 49 titleText.setText(selectCity.getCityName()); 50 backButton.setVisibility(View.VISIBLE); 51 countyList = DataSupport.where("cityid=?", 52 String.valueOf(selectCity.getCityCode())).find(County.class); 53 if (countyList.size() > 0) { 54 dataList.clear(); 55 for (County county : countyList) { 56 dataList.add(county.getCountyName()); 57 } 58 adapter.notifyDataSetChanged(); 59 listView.setSelection(0); 60 currentLevel = LEVEL_COUNTY; 61 }else{ 62 int provinceCode = selectProvince.getProvinceCode(); 63 int cityCode = selectCity.getCityCode(); 64 String address = "http://guolin.tech/api/china/"+provinceCode+"/"+cityCode; 65 queryFromServer(address,"country"); 66 } 67 }
queryFromService()方法對服務器進行數據查詢,首先調出進度加載框。然后調用HttpUil的發送網絡請求,並傳入地址。
如果加載服務器數據失敗,則調用返回主線程處理邏輯,關閉進度框,並發送提示加載失敗。
如果加載服務器數據成功,先初始化返回數據,布爾型result,result用來判斷是否查詢成功。
判斷查詢數據類型,然后進行json數據解析(在utility中解析相關類型數據)解析出對應的數據。
如果查詢成功,返回主線程進行ui修改,關閉進度框,並再次執行相關等級的查詢(此時就在數據庫中查詢,然后加入到listview的datalist中顯示出來。)。
1 /* 2 * 顯示進度對話框 3 * */ 4 private void showProgressDialog() { 5 if (progressDialog == null) { 6 progressDialog = new ProgressDialog(getActivity()); 7 progressDialog.setMessage("正在加載..."); 8 progressDialog.setCanceledOnTouchOutside(false); 9 } 10 progressDialog.show(); 11 } 12 13 /* 14 * 關閉進度對話框 15 * */ 16 private void closeProgressDialog() { 17 if (progressDialog != null) { 18 progressDialog.dismiss(); 19 } 20 } 21 }
最后是兩個具體方法,顯示進度框和關閉進度框。