安卓天气预报的设计与实现
一、项目概述
本系统是采用面向对象的软件开发方法,基于Android studio开发平台,以Android作为本系统的开发语言实现天气预报的功能。
1,总体设计
本软件是一个App Widget应用程序,启动程序后可以进行城市天气的查询、可以通过图片和文字显示当前和未来的天气状况,包括温度、湿度、风向和雨雪情况等。这些天气数据是通过开源API提供的服务获取天气预报信息,并将天气信息展示在界面中。该软件的基本功能需求有:
(1) 启动App应用程序;
(2) 设置界面:默认显示天气预报的城市(北京);
(3) 显示界面:通过文字和图片显示当前的天气情况,包括日期、时间、城市、最高温度、最低温度、当前温度,空气质量指数,天气类型,PM25,日出时间,日落时间,感冒提示等
(4)界面设计了三个城市按钮,点击任意一个就可以获得相应城市的天气。
2,UI展示
二、开发及运行环境、
'androidx.appcompat:appcompat:1.2.0'
'com.google.android.material:material:1.3.0'
'androidx.constraintlayout:constraintlayout:2.0.4'
**'com.squareup.okhttp:okhttp:2.7.5'
'com.google.code.gson:gson:2.8.6'**
**'com.squareup.okhttp3:okhttp:4.10.0-RC1'**
'junit:junit:4.+'
'androidx.test.ext:junit:1.1.2'
'androidx.test.espresso:espresso-core:3.3.0'
三、系统分析与设计
- 功能分析与设计
建立了两个数据实体类,第一个数据实体是来存储JSON文件中的城市编码,城市名等信息。第二个实体是来存储所请求的天气预报的数据实体类。
通过预先准备JSON文件,加载主界面时,从city.json中读取全部内容(读取assets目录下的文件),将json字符串转为城市信息数组(如何用gson解析json数组,根据默认城市名称从城市信息数组中查出对应的城市编码,使用okhttp库向接口发请求,返回数据后使用handler机制通知主界面更新显示。界面需要显示的天气信息在返回的数据中都可以找到。同时为大家提供了一些不同天气类型的图片,可以根据类型加载图片,比如类型中包含“晴”字就加载晴天的图片。
从功能需求上分析可以看出,整个应用程序应划分为4个模块,分别是程序启动、用户界面、网络请求,界面数据渲染
四、系统实现
首先创建数据实体类,
public class cityjson {
private int id;
private int pid;
private String city_code;
private String city_name;
private String post_code;
private String area_code;
private String ctime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getCity_code() {
return city_code;
}
public void setCity_code(String city_code) {
this.city_code = city_code;
}
public String getCity_name() {
return city_name;
}
public void setCity_name(String city_name) {
this.city_name = city_name;
}
public String getPost_code() {
return post_code;
}
public void setPost_code(String post_code) {
this.post_code = post_code;
}
public String getArea_code() {
return area_code;
}
public void setArea_code(String area_code) {
this.area_code = area_code;
}
public String getCtime() {
return ctime;
}
public void setCtime(String ctime) {
this.ctime = ctime;
}
@Override
public String toString() {
return "cityjson{" +
"id=" + id +
", pid=" + pid +
", city_code='" + city_code + '\'' +
", city_name='" + city_name + '\'' +
", post_code='" + post_code + '\'' +
", area_code='" + area_code + '\'' +
", ctime='" + ctime + '\'' +
'}';
}
}
用来存放json解析出来的数据。接着写一个获取JSON文件数据的方法
private void getData(String city) throws IOException {
InputStream is =getResources().getAssets().open("city.json");
StringBuilder builder=new StringBuilder();
String line;
BufferedReader br=new BufferedReader(new InputStreamReader(is));
while ((line=br.readLine())!=null){
builder.append(line);
}
String str=builder.toString();
Gson gson=new Gson();
List<cityjson> con= gson.fromJson(str,new TypeToken<List<cityjson>>() {}.getType());
String [][]cha=new String[3][2];
for(int i=0;i<3;i++){
for(int j=0;j<2;){
cha[i][j]=con.get(i).getCity_code(); j++;
cha[i][j]=con.get(i).getCity_name();j++;
}
}
for(int i=0;i<3;i++){
if(cha[i][1].equals(city)){
System.out.println(cha[i][1]);
loadData(cha[i][0]);
}
}
}
建立接口请求返回来的JSON数据数据实体类
public class Weather {
private String message;
private int status;
private String time;
private WeathercityInfo cityInfo;
private Weatherdata data;
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public WeathercityInfo getCityInfo() {
return cityInfo;
}
public void setCityInfo(WeathercityInfo cityInfo) {
this.cityInfo = cityInfo;
}
public Weatherdata getData() {
return data;
}
public void setData(Weatherdata data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public class WeathercityInfo {
private String city;
private String citykey;
private String parent;
private String updateTime;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCitykey() {
return citykey;
}
public void setCitykey(String citykey) {
this.citykey = citykey;
}
public String getParent() {
return parent;
}
public void setParent(String parent) {
this.parent = parent;
}
public String getUpdateTime() {
return updateTime;
}
public void setUpdateTime(String updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "WeathercityInfo{" +
"city='" + city + '\'' +
", citykey='" + citykey + '\'' +
", parent='" + parent + '\'' +
", updateTime='" + updateTime + '\'' +
'}';
}
}
public class Weatherdata {
private String shidu;
private String pm25;
private String pm10;
private String quality;
private String wendu;
private String ganmao;
private List<Weatherforecast> forecast;
public String getShidu() {
return shidu;
}
public void setShidu(String shidu) {
this.shidu = shidu;
}
public String getPm25() {
return pm25;
}
public void setPm25(String pm25) {
this.pm25 = pm25;
}
public String getPm10() {
return pm10;
}
public void setPm10(String pm10) {
this.pm10 = pm10;
}
public String getQuality() {
return quality;
}
public void setQuality(String quality) {
this.quality = quality;
}
public String getWendu() {
return wendu;
}
public void setWendu(String wendu) {
this.wendu = wendu;
}
public String getGanmao() {
return ganmao;
}
public void setGanmao(String ganmao) {
this.ganmao = ganmao;
}
public List<Weatherforecast> getForecast() {
return forecast;
}
public void setForecast(List<Weatherforecast> forecast) {
this.forecast = forecast;
}
@Override
public String toString() {
return "Weatherdata{" +
"shidu='" + shidu + '\'' +
", pm25='" + pm25 + '\'' +
", pm10='" + pm10 + '\'' +
", quality='" + quality + '\'' +
", wendu='" + wendu + '\'' +
", ganmao='" + ganmao + '\'' +
", forecast=" + forecast +
'}';
}
}
class Weatherforecast {
private String date;
private String high;
private String low;
private String ymd;
private String week;
private String sunrise;
private String sunset;
private int aqi;
private String fx;
private String fl;
private String type;
private String notice;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getHigh() {
return high;
}
public void setHigh(String high) {
this.high = high;
}
public String getLow() {
return low;
}
public void setLow(String low) {
this.low = low;
}
public String getYmd() {
return ymd;
}
public void setYmd(String ymd) {
this.ymd = ymd;
}
public String getWeek() {
return week;
}
public void setWeek(String week) {
this.week = week;
}
public String getSunrise() {
return sunrise;
}
public void setSunrise(String sunrise) {
this.sunrise = sunrise;
}
public String getSunset() {
return sunset;
}
public void setSunset(String sunset) {
this.sunset = sunset;
}
public int getAqi() {
return aqi;
}
public void setAqi(int aqi) {
this.aqi = aqi;
}
public String getFx() {
return fx;
}
public void setFx(String fx) {
this.fx = fx;
}
public String getFl() {
return fl;
}
public void setFl(String fl) {
this.fl = fl;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNotice() {
return notice;
}
public void setNotice(String notice) {
this.notice = notice;
}
@Override
public String toString() {
return "Weatherforecast{" +
"date='" + date + '\'' +
", high='" + high + '\'' +
", low='" + low + '\'' +
", ymd='" + ymd + '\'' +
", week='" + week + '\'' +
", sunrise='" + sunrise + '\'' +
", sunset='" + sunset + '\'' +
", aqi=" + aqi +
", fx='" + fx + '\'' +
", fl='" + fl + '\'' +
", type='" + type + '\'' +
", notice='" + notice + '\'' +
'}';
}
}
}
接下来就这对以数据进行解析了
首先通过天气接口进行通过OKHTTP请求返回来的数据进行解析,
String path = "http://t.weather.itboy.net/api/weather/city/" + city_code;
HttpUtil.sendOkHttpRequest(path, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final String weatherData=response.body().string();
打一个LOG语句查看一下返回的数据成不成功,如果看到一下证明你就成功了,有数据返回了
拿到返回的数据后就可以通过我们定义的实体类进行存储了
接下来就是通过拿到的数据对界面的UI进行改变了,我用的是Activity类的runOnUiThread方法
其实还有另一种方法进行更新UI就是使用handler机制,但是我的安卓编辑器打进去是加了一杠,可能是我SDK版本的问题,所以我们了RUNONUITHREAD方法,代码如下
runOnUiThread(new Runnable() {
@Override
public void run() {
Gson gson =new Gson();
Weather weatherdata=gson.fromJson(weatherData,Weather.class);
Weather.WeathercityInfo cityin=weatherdata.getCityInfo();
System.out.println(cityin);
tvCity.setText(cityin.getCity());//城市
Weather.Weatherdata datain=weatherdata.getData();
tvPm.setText("PM25:"+datain.getPm25()); //pm25
tvqu.setText("空气质量指数:"+datain.getQuality());
tvTemp.setText("温度:"+datain.getWendu()+"℃");
String [][] fore=new String[4][5];
for(int i=0;i<4;i++){
for(int j=0;j<5;){
fore[i][j]=weatherdata.getData().getForecast().get(i).getWeek();j++;
fore[i][j]=weatherdata.getData().getForecast().get(i).getHigh();j++;
fore[i][j]=weatherdata.getData().getForecast().get(i).getLow();j++;
fore[i][j]=weatherdata.getData().getForecast().get(i).getType();j++;
fore[i][j]=weatherdata.getData().getForecast().get(i).getFl();j++;
}
}
tvWeather.setText(fore[0][3]);//天气类型
tvWind.setText("风力:"+fore[0][4]+" "+weatherdata.getData().getForecast().get(0).getFx());
w2.setText(fore[0][0]);
h2.setText(fore[0][1]);
l2.setText(fore[0][2]);
t2.setText(fore[0][3]);
w3.setText(fore[1][0]);
h3.setText(fore[1][1]);
l3.setText(fore[1][2]);
t3.setText(fore[1][3]);
w4.setText(fore[2][0]);
h4.setText(fore[2][1]);
l4.setText(fore[2][2]);
t4.setText(fore[2][3]);
w5.setText(fore[3][0]);
h5.setText(fore[3][1]);
l5.setText(fore[3][2]);
t5.setText(fore[3][3]);
if(fore[0][3].equals("晴")){
tvicon.setImageResource(R.drawable.day_clear);
}else if(fore[0][3].equals("多云")){
tvicon.setImageResource(R.drawable.day_cloudy);
}else if(fore[0][3].equals("中雨")){
tvicon.setImageResource(R.drawable.rain_shower);
}else if(fore[0][3].equals("阴")){
tvicon.setImageResource(R.drawable.day_cloudy);
}else if(fore[0][3].equals("小雨")){
tvicon.setImageResource(R.drawable.rain);
}else if(fore[0][3].equals("雷阵雨")){
tvicon.setImageResource(R.drawable.rain_shower);
}else {
tvicon.setImageResource(R.drawable.none);
}
tip.setText("天气便利贴:"+"\n"+"日出时间来了:"+weatherdata.getData().getForecast().get(0).getSunrise()+"\n"+"日落时间来了:"+weatherdata.getData().getForecast().get(0).getSunset()+"\n"+"温馨提示:"+weatherdata.getData().getForecast().get(0).getNotice());
}
完成了主要代码的书写,还有一个和重要的就是开权限,不然你是请求不成功的,需要在AndroidManifest.xml加入下面代码
<uses-permission android:name="android.permission.INTERNET"/>
完成上面代码后,你有可能还是请求不到数据的话,可能是你的安卓版本太高,为保证用户数据和设备的安全,Google针对下一代 Android 系统(Android P) 的应用程序,将要求默认使用加密连接,这意味着 Android P 将禁止 App 使用所有未加密的连接,因此运行 Android P 系统的安卓设备无论是接收或者发送流量,未来都不能明码传输,需要使用下一代(Transport Layer Security)传输层安全协议。解决方法我用的是更改网络安全配置,解决方法如下:
1.在res文件夹下创建一个xml文件夹,然后创建一个network_security_config.xml文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
2.接着,在AndroidManifest.xml文件下的application标签增加以下属性:
android:networkSecurityConfig = "@xml/network_security_config">
以上就是我的系统实现
五、总结
设计过程中遇到哪些问题:
问题一:我在解析返回来的JSON数据是,一直跟我建立的实体类对应不上去后来我重新检查了我的实体类发现我建立的实体类和返回来的数据名字有几个不一样,改正后就可以正常使用了。
问题二:联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法,解决方法在Android P系统的设备上,如果应用使用的是非加密的明文流量的http网络请求,则会导致该应用无法进行网络请求,更改网络安全配置就可以了
总结:本次课题设计让我对安坐网络编程都有了很深的理解,都是在程序的每一个报错中不断的去百度查资料,顺利完成了设计