Android第一行代码 CoolWeather综合练习
一:概述
项目思路基本参照guolin大神《Android第一行代码》第二版的CoolWeather源码。不过对部分内容进行了改动,并且引入了定位之类的功能。
二:主要改动部分的图览
1项目第一次安装运行的时候会请求获取位置信息,并且根据当前的省份,直接给出所在省会城市的天气。见下图
ps:
(1):本想直接具体到区县的天气。但是因为guolin大神给出的服务器反馈的数据不全,作者所在的市区就不在服务器返回的数据之列,只能直接具体到省会城市了。
(2):因为项目使用BD(百度地图)的定位功能,所以无法在模拟器上成功运行,均用手机截图
2 选择城市列表进行了改动。见下图
透明背景更加美观,给出了各级城市范围和列出用户之前的选项
三:源码改动部分
1,统一接口封装。 对JSON,OKHTTP3,NETWORK封装处理
在第一行代码中,guolin大神在几个handle方法中用的JSONArray JSONObject的方法解析,这里作者统一的用封装GSON来处理JSON数据。
在第一行代码中,没有对NetWork进行判断,因为用户可能需要访问服务器,需要网络支持,这里作者将网络的状态进行了封装和调用处理
public class Utils { /* 通用的网络访问回馈接口 */ public static void sendOkHttpRequest(String address , okhttp3.Callback callback) { OkHttpClient client = new OkHttpClient() ; Request request = new Request.Builder() .url(address) .build() ; client.newCall(request).enqueue(callback); } /* 通用的JSONOBJECT解析接口 */ public static <T> T parseJsonObjectWithGson(String jsonData , Class<T> type) { Gson gson = new Gson() ; T result = gson.fromJson(jsonData , type) ; return result ; } /* 通用的JSONARRAY解析接口 */ public static <T>List<T> parseJsonArrayWithGson (String jsonData , Class<T[]> type) { Gson gson = new Gson() ; T[] result = gson.fromJson(jsonData , type) ; return(List<T>) Arrays.asList(result) ; } /* 判断网络是否可用 */ public static boolean JudgeNetState(Context mContext){ if(mContext != null) { //获取ConnectivityManager对象 Context context = mContext ; ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE) ; //获取NetwORKInfo状态 NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo() ; if (networkInfo!=null){ return networkInfo.isAvailable() ; } return false ; } return false ; } /* 获取网络连接的类型:wifi mobile之类 */ public static int JudgeNetType(Context context){ if (context != null){ //虎丘手机的连接管理对象 ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE); //获取NetInfo的信息 NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo() ; //判断NetWorkInfo对象是否为null 并且判断状态是否为wifi状态 if (networkInfo != null && networkInfo.isAvailable()) { return networkInfo.getType() ; } return -1 ; } return -1 ; } }
2,多级城市分隔
其实也很简单,就是在XML中添加两个横向的LinearLayout,在其中分别添加TextView和View,用weight字段来分段。
在处理的时候需要定义三个ArrayAdapter ,listview ,list<String>来分别处理省会,市,区县的数据,而不是像第一行代码中通过一个ArrayAdapter,listView,list<String>就实现。
详情可以参见代码
3,定位处理
/* 根据传入的地址和类型从服务器上查询省市县的数据 */ private void queryFromServer(String address , final String type) { // Log.i(TAG , "QUERY FROM SERVICE") ; showProgressDialog(); Utils.sendOkHttpRequest(address, new Callback() { @Override public void onFailure(Call call, IOException e) { runOnUiThread(new Runnable() { @Override public void run() { closeProgressDialog(); Toast.makeText(MainActivity.this , "加载失败",Toast.LENGTH_SHORT).show(); } }); } @Override public void onResponse(Call call, Response response) throws IOException { String responseText = response.body().string() ; boolean result = false ; if ("province".equals(type)){ // result = JsonUtils.handleProvinceResponse(responseText) ; Log.i(TAG , "on main response") ; currentProvince = LitePal.where("provinceName = ? " , LOCATION).find(Province.class).get(0) ; String address = "http://guolin.tech/api/china/" + currentProvince.getId() ; //从服务器查询数据 queryFromServer(address , "city"); return ; }else if ("city".equals(type)){ result = JsonUtils.handleCityResponse(responseText , currentProvince.getId()) ; cityList = LitePal.where("provinceid = ? " , String.valueOf(currentProvince.getId())).find(City.class) ; if (cityList.size() > 0 ) { currentCity = cityList.get(0); int provinceCode = currentProvince.getId() ; int cityCode = currentCity.getCityCode() ; String address = "http://guolin.tech/api/china/" + provinceCode +"/" +cityCode ; //从服务器查询数据 queryFromServer(address , "county"); closeProgressDialog(); return ; }else { Toast.makeText(MainActivity.this , "获取位置出现错误" , Toast.LENGTH_SHORT).show(); closeProgressDialog(); return ; } }else if ("county".equals(type)){ result = JsonUtils.handleCountyResponse(responseText , currentCity.getId()) ; countyList = LitePal.where("cityid = ?" ,String.valueOf(currentCity.getId())).find(County.class) ; if (countyList.size() > 0 ) { weatherId = countyList.get(0).getWeatherId() ; requestWeather(weatherId); Log.i(TAG , "获取位置成功 " + LOCATION) ; } } } }); }
获取地理位置的部分很简单,完全用第一行代码中LOCATION部分的操作实现,但主要的部分是对获取到城市的处理
简单写一下部分坑:
(1)通过百度获取的省会,夹带了“省”,“市”这个字段,比如“重庆市”。但是服务器中反馈的数据没有“省”,“市”字段,substr来截取。
(2)从服务器中获取当前省中的所有市级城市,所以需要访问服务器,再把所有省加载到数据库,然后查询匹配当前省。依次重复,直到区县选取得到weatherId。最后根据weatherId再去访问服务器,得到城市的天气数据并且接收后完成布局。
这里最大的坑在于:
// result = JsonUtils.handleProvinceResponse(responseText) ;
这行代码的意思是:向数据库添加从服务器接收到的省级城市列表。
为什么这里注释掉了这行代码?如果不注释掉,作者发现数据库Province中所有省级城市出现了两次。然后又只有
JsonUtils.handleProvinceResponse(responseText) ;
这段代码会向数据库添加省级城市。后来作者在ChooserAreaFragment中发现了:
在OnActivityCreated方法中有个:queryProvince();
queryProvince()中又有:
String address = "http://guolin.tech/api/china" ; //从服务器查询数据 queryFromServer(address , "province");
。也就意味着这里也调用了handleProvinceResponse方法。并且先于Activity之中的方法调用,所以避免重复和冲突,注释掉了Activity中的方法。
三:源码地址:
https://github.com/547291213/CoolWeather