zoukankan      html  css  js  c++  java
  • android开发学习之路——天气预报之遍历省市县数据(二)

        我们已经知道省市县的数据都是从服务器端获取到的,因此与服务器的交互是必不可少的,我们再util包下增加一个HttpUtil类,代码如下所示:

    1 public class HttpUtil{
    2     public static void sendOkHttpRequest(String address,okttp3.Callback callback)
    3     {
    4         OkHttpClient client = new OkHttpClient();
    5         Request request= new Request.Builder().url(address).build();
    6         Client.newcall(request).enqueue(callback);
    7     }
    8 }

        由于Okhttp的出色封装,这里和服务器进行交互的代码非常简单,仅仅3行就完成了。现在我们发起一条HTTP请求只需要调用sendOkHttpRequest()方法,传入请求地址,并注册一个回调来处理服务器响应就可以了。

        由于服务器返回的省市县数据都是JSON格式的,所以我们再提供一个工具类来解析和处理这种数据。在util包下新建一个Utility类,代码如下:

     1 public class Utility {
     2 
     3     /**
     4      * 解析和处理服务器返回的省级数据
     5      */
     6     public static boolean handleProvinceResponse(String response) {
     7         if (!TextUtils.isEmpty(response)) {
     8             try {
     9                 JSONArray allProvinces = new JSONArray(response);
    10                 for (int i = 0; i < allProvinces.length(); i++) {
    11                     JSONObject provinceObject = allProvinces.getJSONObject(i);
    12                     Province province = new Province();
    13                     province.setProvinceName(provinceObject.getString("name"));
    14                     province.setProvinceCode(provinceObject.getInt("id"));
    15                     province.save();
    16                 }
    17                 return true;
    18             } catch (JSONException e) {
    19                 e.printStackTrace();
    20             }
    21         }
    22         return false;
    23     }
    24 
    25     /**
    26      * 解析和处理服务器返回的市级数据
    27      */
    28     public static boolean handleCityResponse(String response, int provinceId) {
    29         if (!TextUtils.isEmpty(response)) {
    30             try {
    31                 JSONArray allCities = new JSONArray(response);
    32                 for (int i = 0; i < allCities.length(); i++) {
    33                     JSONObject cityObject = allCities.getJSONObject(i);
    34                     City city = new City();
    35                     city.setCityName(cityObject.getString("name"));
    36                     city.setCityCode(cityObject.getInt("id"));
    37                     city.setProvinceId(provinceId);
    38                     city.save();
    39                 }
    40                 return true;
    41             } catch (JSONException e) {
    42                 e.printStackTrace();
    43             }
    44         }
    45         return false;
    46     }
    47 
    48     /**
    49      * 解析和处理服务器返回的县级数据
    50      */
    51     public static boolean handleCountyResponse(String response, int cityId) {
    52         if (!TextUtils.isEmpty(response)) {
    53             try {
    54                 JSONArray allCounties = new JSONArray(response);
    55                 for (int i = 0; i < allCounties.length(); i++) {
    56                     JSONObject countyObject = allCounties.getJSONObject(i);
    57                     County county = new County();
    58                     county.setCountyName(countyObject.getString("name"));
    59                     county.setWeatherId(countyObject.getString("weather_id"));
    60                     county.setCityId(cityId);
    61                     county.save();
    62                 }
    63                 return true;
    64             } catch (JSONException e) {
    65                 e.printStackTrace();
    66             }
    67         }
    68         return false;
    69     }
    70 
    71

        可以看到,我们提供了handleProvincesResponse()、handleCitiesResponse()、handleCountiesResponse()这3个方法,分别用于解析和处理服务器返回的省级、市级和县级数据。处理的方法是类似的,先使用JSONArray和JSONObject将数据解析出来,然后组装成实体类对象,再调用save()方法将数据存储到数据库当中。

        工具类准备好了,现在开始写界面。由于遍历全国省市县的功能我们后面还会复用,因此写在碎片里,这样复用的时候,直接在布局里引用碎片就可以了。

        在res/layout目录中新建choose_area.xml布局,代码如下所示:

     1 <LinearLayout
     2   xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:orientation="vertical"
     4     android:layout_height="match_parent"    
     5     android:layout_width="match_parent"
     6     android:background="#fff "  >
     7     <RelativeLayout  
     8         android:layout_height="?attr/actionBarSize"
     9         android:layout_width="match_parent
    10         android:background="?attr/colorPrimary""> 
    11         <TextView 
    12            android:id="@+id/title_text"
    13            android:layout_height="wrap_content"
    14            android:layout_width="wrap_content"
    15            android:textSize="20sp" 
    16            android:textColor="#fff"
    17            android:layout_centerInParent="true" /> 
    18         <Button 
    19            android:id="@+id/back_button" 
    20            android:layout_height="25dp" 
    21            android:layout_width="25dp"
    22            android:layout_centerVertical="true"
    23            android:layout_alignParentLeft="true"
    24            android:layout_marginLeft="10dp"
    25            android:background="@drawable/ic_back"/> 
    26     </RelativeLayout> 
    27     <ListView 
    28         android:id="@+id/list_view"
    29         android:layout_height="match_parent"
    30         android:layout_width="match_parent"/> 
    31 </LinearLayout>

        布局文件中的内容并不复杂,我们先定义了一个头布局作为标题栏,将布局高度设置为actionBar的高度,背景色设置为colorPrimary。然后在头布局中放置一个TextView用于显示标题内容,放置了一个Button用于执行返回操作,注意我已经提前准备好了一张ic_back.png图片作为按钮的背景图。这里之所以要自己定义标题栏,是因为碎片中最好不要直接使用ActionBar或Toolbar,不然在复用的时候可能出现一些你不想看到的效果。

        接下来在头布局的下面定义了一个ListView,省市县的数据就将显示在这里。之所以这次使用ListView,是因为它会自动给每个子项之间添加一条分割线,而如果使用RecyclerView想实现同样的功能则会比较麻烦,这里我们总是选择最优的实现方案。

        接下来我们需要编写用于遍历省市县数据的碎片了。新建ChooseAreaFragment继承自Fragment,代码如下:

      1 public class ChooseAreaFragment extends Fragment {
      2 
      3     private static final String TAG = "ChooseAreaFragment";
      4 
      5     public static final int LEVEL_PROVINCE = 0;
      6 
      7     public static final int LEVEL_CITY = 1;
      8 
      9     public static final int LEVEL_COUNTY = 2;
     10 
     11     private ProgressDialog progressDialog;
     12 
     13     private TextView titleText;
     14 
     15     private Button backButton;
     16 
     17     private ListView listView;
     18 
     19     private ArrayAdapter<String> adapter;
     20 
     21     private List<String> dataList = new ArrayList<>();
     22 
     23     /**
     24      * 省列表
     25      */
     26     private List<Province> provinceList;
     27 
     28     /**
     29      * 市列表
     30      */
     31     private List<City> cityList;
     32 
     33     /**
     34      * 县列表
     35      */
     36     private List<County> countyList;
     37 
     38     /**
     39      * 选中的省份
     40      */
     41     private Province selectedProvince;
     42 
     43     /**
     44      * 选中的城市
     45      */
     46     private City selectedCity;
     47 
     48     /**
     49      * 当前选中的级别
     50      */
     51     private int currentLevel;
     52 
     53 
     54     @Override
     55     public View onCreateView(LayoutInflater inflater, ViewGroup container,
     56                              Bundle savedInstanceState) {
     57         View view = inflater.inflate(R.layout.choose_area, container, false);
     58         titleText = (TextView) view.findViewById(R.id.title_text);
     59         backButton = (Button) view.findViewById(R.id.back_button);
     60         listView = (ListView) view.findViewById(R.id.list_view);
     61         adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, dataList);
     62         listView.setAdapter(adapter);
     63         return view;
     64     }
     65 
     66     @Override
     67     public void onActivityCreated(Bundle savedInstanceState) {
     68         super.onActivityCreated(savedInstanceState);
     69         listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
     70             @Override
     71             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
     72                 if (currentLevel == LEVEL_PROVINCE) {
     73                     selectedProvince = provinceList.get(position);
     74                     queryCities();
     75                 } else if (currentLevel == LEVEL_CITY) {
     76                     selectedCity = cityList.get(position);
     77                     queryCounties();
     78                 } else if (currentLevel == LEVEL_COUNTY) {
     79                     String weatherId = countyList.get(position).getWeatherId();
     80                     if (getActivity() instanceof MainActivity) {
     81                         Intent intent = new Intent(getActivity(), WeatherActivity.class);
     82                         intent.putExtra("weather_id", weatherId);
     83                         startActivity(intent);
     84                         getActivity().finish();
     85                     } else if (getActivity() instanceof WeatherActivity) {
     86                         WeatherActivity activity = (WeatherActivity) getActivity();
     87                         activity.drawerLayout.closeDrawers();
     88                         activity.swipeRefresh.setRefreshing(true);
     89                         activity.requestWeather(weatherId);
     90                     }
     91                 }
     92             }
     93         });
     94         backButton.setOnClickListener(new View.OnClickListener() {
     95             @Override
     96             public void onClick(View v) {
     97                 if (currentLevel == LEVEL_COUNTY) {
     98                     queryCities();
     99                 } else if (currentLevel == LEVEL_CITY) {
    100                     queryProvinces();
    101                 }
    102             }
    103         });
    104         queryProvinces();
    105     }
    106 
    107     /**
    108      * 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器上查询。
    109      */
    110     private void queryProvinces() {
    111         titleText.setText("中国");
    112         backButton.setVisibility(View.GONE);
    113         provinceList = DataSupport.findAll(Province.class);
    114         if (provinceList.size() > 0) {
    115             dataList.clear();
    116             for (Province province : provinceList) {
    117                 dataList.add(province.getProvinceName());
    118             }
    119             adapter.notifyDataSetChanged();
    120             listView.setSelection(0);
    121             currentLevel = LEVEL_PROVINCE;
    122         } else {
    123             String address = "http://guolin.tech/api/china";
    124             queryFromServer(address, "province");
    125         }
    126     }
    127 
    128     /**
    129      * 查询选中省内所有的市,优先从数据库查询,如果没有查询到再去服务器上查询。
    130      */
    131     private void queryCities() {
    132         titleText.setText(selectedProvince.getProvinceName());
    133         backButton.setVisibility(View.VISIBLE);
    134         cityList = DataSupport.where("provinceid = ?", String.valueOf(selectedProvince.getId())).find(City.class);
    135         if (cityList.size() > 0) {
    136             dataList.clear();
    137             for (City city : cityList) {
    138                 dataList.add(city.getCityName());
    139             }
    140             adapter.notifyDataSetChanged();
    141             listView.setSelection(0);
    142             currentLevel = LEVEL_CITY;
    143         } else {
    144             int provinceCode = selectedProvince.getProvinceCode();
    145             String address = "http://guolin.tech/api/china/" + provinceCode;
    146             queryFromServer(address, "city");
    147         }
    148     }
    149 
    150     /**
    151      * 查询选中市内所有的县,优先从数据库查询,如果没有查询到再去服务器上查询。
    152      */
    153     private void queryCounties() {
    154         titleText.setText(selectedCity.getCityName());
    155         backButton.setVisibility(View.VISIBLE);
    156         countyList = DataSupport.where("cityid = ?", String.valueOf(selectedCity.getId())).find(County.class);
    157         if (countyList.size() > 0) {
    158             dataList.clear();
    159             for (County county : countyList) {
    160                 dataList.add(county.getCountyName());
    161             }
    162             adapter.notifyDataSetChanged();
    163             listView.setSelection(0);
    164             currentLevel = LEVEL_COUNTY;
    165         } else {
    166             int provinceCode = selectedProvince.getProvinceCode();
    167             int cityCode = selectedCity.getCityCode();
    168             String address = "http://guolin.tech/api/china/" + provinceCode + "/" + cityCode;
    169             queryFromServer(address, "county");
    170         }
    171     }
    172 
    173     /**
    174      * 根据传入的地址和类型从服务器上查询省市县数据。
    175      */
    176     private void queryFromServer(String address, final String type) {
    177         showProgressDialog();
    178         HttpUtil.sendOkHttpRequest(address, new Callback() {
    179             @Override
    180             public void onResponse(Call call, Response response) throws IOException {
    181                 String responseText = response.body().string();
    182                     boolean result = false;
    183                 if ("province".equals(type)) {
    184                     result = Utility.handleProvinceResponse(responseText);
    185                 } else if ("city".equals(type)) {
    186                     result = Utility.handleCityResponse(responseText, selectedProvince.getId());
    187                 } else if ("county".equals(type)) {
    188                     result = Utility.handleCountyResponse(responseText, selectedCity.getId());
    189                 }
    190                 if (result) {
    191                     getActivity().runOnUiThread(new Runnable() {
    192                         @Override
    193                         public void run() {
    194                             closeProgressDialog();
    195                             if ("province".equals(type)) {
    196                                 queryProvinces();
    197                             } else if ("city".equals(type)) {
    198                                 queryCities();
    199                             } else if ("county".equals(type)) {
    200                                 queryCounties();
    201                             }
    202                         }
    203                     });
    204                 }
    205             }
    206 
    207             @Override
    208             public void onFailure(Call call, IOException e) {
    209                 // 通过runOnUiThread()方法回到主线程处理逻辑
    210                 getActivity().runOnUiThread(new Runnable() {
    211                     @Override
    212                     public void run() {
    213                         closeProgressDialog();
    214                         Toast.makeText(getContext(), "加载失败", Toast.LENGTH_SHORT).show();
    215                     }
    216                 });
    217             }
    218         });
    219     }
    220 
    221     /**
    222      * 显示进度对话框
    223      */
    224     private void showProgressDialog() {
    225         if (progressDialog == null) {
    226             progressDialog = new ProgressDialog(getActivity());
    227             progressDialog.setMessage("正在加载...");
    228             progressDialog.setCanceledOnTouchOutside(false);
    229         }
    230         progressDialog.show();
    231     }
    232 
    233     /**
    234      * 关闭进度对话框
    235      */
    236     private void closeProgressDialog() {
    237         if (progressDialog != null) {
    238             progressDialog.dismiss();
    239         }
    240     }
    241 
    242 }

       这个类里的代码非常多,但逻辑并不复杂。在onCreateView()方法中先是获取到了一些控件的实例,然后去初始化ArrayAdapter,并将它设置为ListView的适配器。接着在onActivityCreated()方法中给ListView和Button设置了点击事件,到这初始工作算是完成。

        在onAcivityCreated()方法的最后,调用了queryProvinces()方法,也就是从这里开始加载省级数据的。queryProvinces()方法中首先会将头布局的标题设置成中国,将返回按钮隐藏起来,因为省级列表已经不能再返回了。然后调用LitePal的查询接口来从数据库中读取省级数据,如果读取到了就直接将数据显示到界面上,如果没有,就组装出一个请求地址,然后调用queryFromServer()方法来从服务器上查询数据。

        queryFromServer()方法会调用HttpUtil的sendOkHttpRequest()方法来向服务器发送请求,响应的数据会回调到onResponse()方法中,然后我们在这里去调用Utility的handleProvincesResponse()方法来解析和处理服务器返回的数据,并存储到数据库中。在解析和处理完数据之后,我们再次调用了queryProvinces()方法来重新加载省级数据,由于queryProvinces()方法牵扯到了UI操作,因此必须要在主线程中调用,,这里借助了runOnUiThread()方法来实现从子线程切换到主线程。现在数据库中已经存在了数据,因此调用queryProvinces()就会直接将数据显示到界面上了。

        当你点击了某个省的时候会进入到ListView的onItemClick()方法中,这个时候会根据当前的级别来判断是去调用queryCities()方法还是queryCounties()方法,queryCities()方法是去查询市级数据,而queryCounties()方法是去查询县级数据。

        另外,在返回按钮的点击事件里,会对当前ListView的列表级别进行判断。如果当前是县级列表,那么就返回到市级列表,如果当前是市级列表,那么就返回到省级列表。当返回到省级列表时,返回按钮会自动隐藏。

        这样我们就把遍历省市县的功能完成了,但碎片不能直接显示在界面上的,因为我们需要将它添加到活动当中。修改activity_main.xml中的代码,如下所示:

     1 <FrameLayout
     2     xmlns:android="http://schemas.android.com/apk/res/android"
     3     android:layout_height="match_parent"
     4     android:layout_width="match_parent" > 
     5     <fragment 
     6         android:id="@+id/choose_area_fragment"
     7         android:name="com.coolweather.android.ChooseAreaFragment"
     8         android:layout_height="match_parent"
     9         android:layout_width="match_parent"  />
    10  </FrameLayout>

        定义了一个FrameLayout,然后将ChooseAreaFragment添加进来,并让它充满整个布局。

        另外,我们刚才在碎片的布局里面已经自定义了一个标题栏,因此就不再需要原生的AcitonBar了,修改res/values/styles.xml中的代码,如下所示:

    1 <resources> 
    2     <!-- Base application theme. -->
    3     <style parent="Theme.AppCompat.Light.NoActionBar">
    4          .... 
    5      </style>
    6 </resources>

         接下来声明程序所需要的权限。修改AndroidManifest.xml中的代码,如下所示:

    1 <manifest 
    2     package="com.coolweather.android"
    3     xmlns:android="http://schemas.android.com/apk/res/android">
    4     <uses-permission android:name="android.permission.INTERNET"/>
    5     .... 
    6 </manifest>

        由于我们是通过网络接口来获取全国省市县数据的,因此必须要添加访问网络的权限才行。

        下一章节设计并编写显示天气信息的布局和功能。

    具体实现步骤连接:

    android开发学习之路——天气预报之技术分析与数据库(一)

    android开发学习之路——天气预报之遍历省市县数据(二)

    android开发学习之路——天气预报之显示天气信息(三)

    android开发学习之路——天气预报之手动更新天气和切换城市(四)

    android开发学习之路——天气预报之后台自动更新天气(五)

  • 相关阅读:
    数据结构排序算法之堆排序
    整数划分
    二叉树的递归与非递归
    螺旋数组
    【图的DFS】图的DFS非递归算法
    Docker实战部署应用——Tomcat
    Docker实战部署应用——Redis
    Docker实战部署应用——MySQL5.7
    Docker其他操作:查看内部细节、IP、删除容器
    Docker数据目录相关操作
  • 原文地址:https://www.cnblogs.com/weilongfu/p/7407293.html
Copyright © 2011-2022 走看看