首先城市接口地址为:http://guolin.tech/api/china/1/1 ,china后面的是省份的ID,再接着是城市的ID.分析API,也就是china的时候会返回所有省份ID,返回格式是JSON格式,然后选择了省份ID以后会展示当前省份所有城市的ID,选择了城市以后会展示当前城市所有的县市ID和WeatherID。
整个应用程序的一个过程就是选择城市>展示天气
所以第一步,
选择城市
选择城市的思路应该是首先从缓存中读取,缓存中没有的话再去网络上读取,读取到以后,将读取到的JSON数据转换为相应的javaBean并同时缓存到数据库中,然后展示出来。有了这么一个思路以后,我们可以先做第一步工作,就是新建新建一个实体类,用来对应所选择的城市
新建实体类
分别为省份,城市,县,此处省略了get/set方法
public class Province extends DataSupport {
private int id;
private String ProvinceName;
private int ProvinceCode;
....
}
public class City extends DataSupport {
private int id;
private String cityName;
private int cityCode;
private int provinceID;
...
}
public class County extends DataSupport {
private int id;
private String countyName;
private String weatherId;
private int cityId;
....
}
因为要用到litepal,所以所有的类都继承了DataSupport。
新建assets文件夹,并创建litepal.xml文件,内容如下:
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="weather" />
<version value="1" />
<list>
<mapping class="com.example.weather.db.City"/>
<mapping class="com.example.weather.db.County" />
<mapping class="com.example.weather.db.Province"/>
</list>
</litepal>
这样在创建的实体类中会有save方法,可以自动保存到数据库中。这里,新建一个工具类httputill用于访问网络(使用的是okhttp3):
public class HttpUtil {
public static void sendOkhttpRequest(String address, Callback callback){
OkHttpClient.Builder builder=new OkHttpClient.Builder();
OkHttpClient client= builder.build();
Request request=new Request.Builder().url(address).build();
client.newCall(request).enqueue(callback);
}
}
当从网络中返回数据后,我们新建工具类将它解析并且存储到数据库中,这里只展示一个省份的,其他也一样:
public static boolean SelectProvinceResponse(String response){
if(!TextUtils.isEmpty(response)) {
try {
JSONArray jsonArray = new JSONArray(response);
for (int i = 0; i < jsonArray.length(); i++) {
Province p = new Province();
JSONObject projson = jsonArray.getJSONObject(i);
p.setProvinceCode(projson.getInt("id"));
p.setProvinceName(projson.getString("name"));
p.save();
}
return true;
} catch (JSONException e) {
e.printStackTrace();
}
}
return false;
}
这段代码,首先我们将返回来的字符串解析为一个json数组,然后循环遍历取出其中的json对象,再通过新建实体类并且将json对象中的数据取出并且赋值以后,使用save方法保存到数据库中。因为返回的json对象格式就是一系列数组,因为比较简单就不使用Gson了。这样就完成了访问网络,并将网络中的数据解析为实体类,然后存储到数据库这一过程。
2.
读取城市信息
因为随后要涉及到切换城市的功能,我们现在使用fragment来显示页面,并且在fragment中处理关于城市的逻辑。
首先在fragment中放置了一个listview和一个用于返回的按钮,这里说一下处理的逻辑:
1 从数据库中查询数据,如果没有就从网络中读取数据并缓存,然后再从数据库中查询数据并更新listview。
2 设置了三个等级,currentLevel,Level_Province = 0,Level_City = 1,Level_County = 2。这里代表的是当前listview显示的是哪一级的数据,当刚刚进入界面的时候,currentLevel为Level_Province,当前级别为省一级,然后点击后,当前级别更新为市一级,以此类推。同时,还设置了Province selectProvince,City selectCity,代表当前选中的省份和城市,用于在后面进行查询。
3 在listview中监听了点击事件,首先判断当前级别是哪一个级别,如果是省一级别,就查询点击的省份的ID,并且设置选中省份为点击的省份,然后调用显示城市的方法。
4 在显示省份或者城市,县的方法中,都是依据一个原则,首先从数据库中读取数据。如果有,则更新listview,如果没有,则根据选择的省份ID/城市ID/县ID拼接字符串从服务器中查询,并且使用工具类中的方法保存到数据库中。
5 在这些显示方法中都会自动的更改当前选中的级别和更新listview。在调用查询服务器的方法的时候为了友好的用户体验会设置开启一个进度对话框,并在查询得到数据后关闭对话框。在从服务器查询的方法中,会根据所传入的级别进行相应的数据库的缓存,并同时调用相应级别的展示省份/城市/省份的方法。
这就是全部的逻辑,下面是展示城市的方法:
private void queryCitys() {
textView.setText(selectProvince.getProvinceName());
backButton.setVisibility(View.VISIBLE);
cityList = DataSupport.where("provinceID=?", String.valueOf(selectProvince.getId())).find(City.class);
if (cityList.size() > 0) {
mlist.clear();
for (City city :
cityList) {
mlist.add(city.getCityName());
}
listView.setSelection(0);
currentLevel = Level_City;
adapter.notifyDataSetChanged();
} else {
String address = "http:guolin.tech/api/china" + "/" + selectProvince.getProvinceCode();
QueryFromServce(address, "city");
}
}
下面是从服务器查询数据的方法:
private void QueryFromServce(String address, final String type) {
showProdialog();
HttpUtil.sendOkhttpRequest(address, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
String responsebody = response.body().string();
boolean result = false;
if (type.equals("province")) {
result = Util.SelectProvinceResponse(responsebody);
} else if (type.equals("city")) {
result = Util.SelectCityResponse(responsebody, selectProvince.getId());
} else if (type.equals("county")) {
result = Util.SelectCountyResponse(responsebody, selectCity.getId());
}
if (result) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
closedialog();
if (type.equals("province")) {
queryProvince();
} else if (type.equals("city")) {
queryCitys();
} else if (type.equals("county")) {
queryCounty();
}
}
});
}
}
@Override
public void onFailure(Call call, IOException e) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), "加载失败,请检查网络设置", Toast.LENGTH_SHORT).show();
}
});
}
});
}
如果查询到数据就更新数据,如果没有查询到就从服务器中查询并缓存到数据库中,然后在回调该方法,此时数据库中已经有数据,所以能够查询到数据并且更新。
因为在listview的item点击事件中有判断当前级别,所以当当前级别为县一级的时候,会将当前县的weatherID通过SharedPreferences存储到手机中,用于启动的时候判断是否已经选择了城市。并且将weatherID通过intent传递给天气界面。这里,还有一个判断,如果当前的这个fragment属于主界面,则intent到天气界面,如果属于天气界面,则通过天气界面的下拉刷新控件刷新数据:
if (getActivity() instanceof mainAcvitity) {
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.putExtra("weatherID", weatherid);
startActivity(intent);
getActivity().finish();
} else if (getActivity() instanceof MainActivity) {
MainActivity activity= (MainActivity) getActivity();
activity.drawer.closeDrawers();
activity.swipe.setRefreshing(true);
activity.requestWeather(weatherid);
}
2.
展示天气数据
天气活动的这个界面和控件实例化暂时不说了,说查询数据方面
同样的,展示天气数据也是先从缓存中读取天气API返回的json格式字符串,这里字符串使用SharedPreferences来保存,如果有的话就进行解析并且映射为实体类,然后保存到数据库中,如果没有就根据fragment中传来的weatherID从服务器中查询。
在从服务器查询数据的方法中,如果返回了数据,那么就先解析并映射为实体类,然后在用SharedPreferences来保存字符串信息,然后调用展示数据的方法,为每个控件依次赋值。这里就没什么其他东西了。
- 关于json格式映射实体类,这里推荐使用GsonFormat控件,ALT+S可以输入json字符串,然后自动映射为javaBean对象,其中的@SerializedName(“xxx”),xxx代表json中的某一个键名,使用这个以后,会自动的将json中xxx的键名的值赋值到@SerializedName下面所对应的变量中
- 关于判断时间的,获得当前时间的方法是:
public static boolean isNight(){
Calendar calendar=Calendar.getInstance();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
Log.i("MainActivity", "isNight: "+hour);
if(hour<=19&&hour>=7){
return false;
}else{
return true;
}
}
其中的hour of day代表的是24小时制
- 关于沉浸式标题栏的方式是:
if (Build.VERSION.SDK_INT >= 21) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}else{
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
总结:
总的来说不太好的就是程序的架构设计,还有json字符串解析,以及网络访问这三个部分。关于架构设计还需要多多参与实战开发才能有所提高,json字符串解析有个Gsonformat工具以后很好的帮我解析json数据,无需在担心,网络访问和异步这个需要随后学习一些框架来提高。希望2017年我能有更高的发挥和更好地APP。