Android实战:CoolWeather酷欧天气(加强版数据接口)代码详解(下)

本文主要是介绍Android实战:CoolWeather酷欧天气(加强版数据接口)代码详解(下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

-----------------------------------该文章代码已停更,可参考浩比天气(更新于2019/6/25)-----------------------------------
接上篇文章(http://blog.csdn.net/MaybeForever/article/details/78497806)继续,完整文件请从我的GitHub中下载。

目录(下)

文章目录

      • 目录(下)

##四、显示天气信息

1、定义GSON实体类

首先详细分析一下和风天气返回的数据,如下图所示:

这里写图片描述
除了status之外,其余的五个内部又包含具体的内容,因此将basic、aqi、now、suggestion、daily_forecast这五个部分定义成5个实体类。在gson包下建立Basic类、AQI类、Now类、Suggestion类、Forecast类。

Basic类:

public class Basic {@SerializedName("city")public String cityName;//城市名@SerializedName("id")public String weatherId;//城市对应的天气的id@SerializedName("lat")public String cityLat;//城市的经度@SerializedName("lon")public String cityLon;//城市的纬度public Update update;public class Update{@SerializedName("loc")public String updateTime;//接口更新时间}}

AQI类:

public class AQI {public AQICITY city;public class AQICITY{public String aqi;//空气质量指数public String co;//一氧化碳指数public String no2;//二氧化氮指数public String o3;//臭氧指数public String pm10;//PM10指数public String pm25;//PM2.5指数public String qlty;//空气质量(优/良/轻度污染/中度污染/重度污染/严重污染)public String so2;//二氧化硫指数}
}

Forecast类:

public class Forecast {public Astro astro;public class Astro{public String mr;//月升时间public String ms;//月落时间public String sr;//日升时间public String ss;//日落时间}@SerializedName("cond")public More more;public class More{@SerializedName("txt_d")public String info;//白天天气状况描述@SerializedName("txt_n")public String night_info;//晚间天气状况描述}public String date;//预报日期public String pcpn;//降水量public String pop;//降水概率public String pres;//大气压强public String uv;//紫外线强度指数public String vis;//能见度public String hum;//相对湿度@SerializedName("tmp")public Temperature temperature;public class Temperature{public String max;//最高温度public String min;//最低温度}public Wind wind;public class Wind{public String dir;//风向public String sc;//风力public String spd;//风速}}

Now类:

public class Now {@SerializedName("cond")public More more;public class More{@SerializedName("txt")public String info;//天气信息}public String fl;//体感温度public String hum;//相对湿度public String pcpn;//降水量public String pres;//大气压强@SerializedName("tmp")public String temperature;//温度public String vis;//能见度public WIND wind;public class WIND{public String dir;//风向public String sc;//风力public String spd;//风速}
}

Suggestion类:

public Air air;//空气质量指数public class Air{@SerializedName("txt")public String info;}@SerializedName("comf")public Comfort comfort;//舒适度指数public class Comfort{@SerializedName("txt")public String info;}@SerializedName("cw")public CarWash carWash;//洗车指数public class CarWash{@SerializedName("txt")public String info;}public Drsg drsg;//穿衣指数public class Drsg{@SerializedName("txt")public String info;}public Flu flu;//感冒指数public class Flu{@SerializedName("txt")public String info;}public Sport sport;//运动指数public class Sport{@SerializedName("txt")public String info;}public Trav trav;//旅游指数public class Trav{@SerializedName("txt")public String info;}public Uv uv;//紫外线指数public class Uv{@SerializedName("txt")public String info;}
}

在创建五个实体类的过程中,我们使用了@SerializedName() 来命名JSON中的一些字段,由于JSON中的一些字段不适合直接用来使用,因为不好理解,所以可以使用@SerializedName()的方式 ,将JSON字段写在里面,然后在下面一行写上自己需要用的命名(可随意写,只要自己理解就可以)。至此,basic、aqi、now、suggestion、daily_forecast对应的实体类已经创建好,接下来还需要再创建一个总的实体类来引用刚刚创建的各个实体类。在gson包下新建一个Weather类,代码如下:

public class Weather {/*** Weather类作为总的实例类来引用以上各个实体类*/public String status;public Basic basic;public AQI aqi;public Now now;public Suggestion suggestion;@SerializedName("daily_forecast")public List<Forecast> forecastList;
}

在Weather类中,对Basic、AQI、Now、Suggestion、Forecast类进行了引用,由于daily_forecast包含的是一个数组,因此使用List集合来引用Forecast类。此外,除了天气信息数据还会包含一项status数据,成功获取天气数据的时候会返回ok,失败时会返回具体原因。

2、编写天气界面

首先创建一个用于显示天气信息的活动。右击com.coolweather.android包->New->Activity->Empty Activity,创建一个Weather Activity,并将布局名指定成activity_weather.xml。由于所有的天气信息在同一界面上显示会让代码很混乱,所以将界面的不同部分写在不同的布局文件里,再通过引入布局的方式集成到activity_weather.xml中。右击res/layout->New->Layout resource file,新建一个title.xml作为头布局,代码如下所示:

<android.support.percent.PercentFrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="100dp"><Buttonandroid:id="@+id/nav_button"android:layout_width="30dp"android:layout_height="30dp"android:layout_gravity="left|top"app:layout_widthPercent="20%"app:layout_heightPercent="80%"android:background="@drawable/ic_home"/><TextViewandroid:id="@+id/title_city"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center|top"android:gravity="center"app:layout_widthPercent="60%"app:layout_heightPercent="80%"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:id="@+id/title_update_time"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginRight="10dp"android:layout_marginTop="10dp"android:layout_gravity="right|top"app:layout_widthPercent="20%"app:layout_heightPercent="80%"android:textColor="#fff"android:textSize="16sp"/><TextViewandroid:id="@+id/lat_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:layout_gravity="left|bottom"app:layout_widthPercent="50%"app:layout_heightPercent="20%"android:textColor="#fff"android:textSize="16sp"/><TextViewandroid:id="@+id/lon_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right|bottom"app:layout_widthPercent="50%"app:layout_heightPercent="20%"android:textColor="#fff"android:textSize="16sp"/></android.support.percent.PercentFrameLayout>

title.xml使用了百分比布局的方法,然后新建now.xml作为当前天气信息的布局,代码如下所示:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"android:background="#8000"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_marginTop="15dp"android:text="实时天气信息"android:textColor="#fff"android:textSize="20sp"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"android:orientation="vertical"><TextViewandroid:id="@+id/degree_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="温度"android:textColor="#fff" /></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"android:orientation="vertical"><TextViewandroid:id="@+id/fl_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="体感温度"android:textColor="#fff" /></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/weather_info_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="天气"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/hum_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="相对湿度"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/dir_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="35sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="风向"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/sc_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="35sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="风力"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/spd_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="风速"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/pcpn_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="降水量"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/pres_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="大气压强"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/vis_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="能见度"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout></LinearLayout>

now.xml使用了相对布局和线性布局的方法,然后新建forecast.xml作为未来几天天气信息的布局,代码如下所示:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"android:background="#8000"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_marginTop="15dp"android:text="预报"android:textColor="#fff"android:textSize="20sp"/><LinearLayoutandroid:id="@+id/forecast_layout"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"></LinearLayout></LinearLayout>

forecast.xml使用了线性布局,还再需要定义一个未来天气的子项布局,创建forecast_item.xml文件,代码如下所示:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><TextViewandroid:id="@+id/date_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="2"android:textColor="#fff"/><TextViewandroid:id="@+id/info_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:gravity="center"android:textColor="#fff" /><TextViewandroid:id="@+id/max_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:gravity="right"android:textColor="#fff"/><TextViewandroid:id="@+id/min_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:gravity="right"android:textColor="#fff"/><TextViewandroid:id="@+id/dir_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:gravity="right"android:textColor="#fff"/><TextViewandroid:id="@+id/sc_text"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_weight="1"android:gravity="right"android:textColor="#fff"/>
</LinearLayout>

forecast_item.xml使用线性布局,然后新建aqi.xml作为空气质量信息的布局,代码如下所示:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"android:background="#8000"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_marginTop="15dp"android:text="空气质量"android:textColor="#fff"android:textSize="20sp"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"android:orientation="vertical"><TextViewandroid:id="@+id/aqi_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="空气质量指数"android:textColor="#fff" /></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"android:orientation="vertical"><TextViewandroid:id="@+id/qlty_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="35sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="空气质量"android:textColor="#fff" /></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/pm25_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="PM2.5指数"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/pm10_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="PM10指数"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/co_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="CO指数"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/no2_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="NO2指数"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/o3_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="O3指数"android:textColor="#fff"/></LinearLayout></RelativeLayout><RelativeLayoutandroid:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_centerInParent="true"><TextViewandroid:id="@+id/so2_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:textColor="#fff"android:textSize="40sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:text="SO2指数"android:textColor="#fff"/></LinearLayout></RelativeLayout></LinearLayout></LinearLayout>

aqi.xml使用了相对布局和线性布局,然后新建suggestion.xml作为生活建议信息的布局,代码如下所示:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="15dp"android:background="#8000"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginLeft="15dp"android:layout_marginTop="15dp"android:text="生活建议"android:textColor="#fff"android:textSize="20sp"/><TextViewandroid:id="@+id/air_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/comfort_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/car_wash_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/drsg_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/flu_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/sport_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/trav_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/><TextViewandroid:id="@+id/uv_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="15dp"android:textColor="#fff"/></LinearLayout>

suggestion.xml使用了线性布局,接下来将已编好的每部分布局文件引入到activity_weather.xml当中,代码如下所示:

<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/colorPrimary"><ImageViewandroid:id="@+id/bing_pic_img"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"/><android.support.v4.widget.DrawerLayoutandroid:id="@+id/drawer_layout"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v4.widget.SwipeRefreshLayoutandroid:id="@+id/swipe_refresh"android:layout_width="match_parent"android:layout_height="match_parent"><ScrollViewandroid:id="@+id/weather_layout"android:layout_width="match_parent"android:layout_height="match_parent"android:scrollbars="none"android:overScrollMode="never"><LinearLayoutandroid:orientation="vertical"android:layout_width="match_parent"android:layout_height="wrap_content"android:fitsSystemWindows="true"><include layout="@layout/title" /><include layout="@layout/now" /><include layout="@layout/forecast" /><include layout="@layout/aqi" /><include layout="@layout/suggestion" /></LinearLayout></ScrollView></android.support.v4.widget.SwipeRefreshLayout><fragmentandroid:id="@+id/choose_area_fragment"android:name="com.coolweather.android.ChooseAreaFragment"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="start"/></android.support.v4.widget.DrawerLayout></FrameLayout>

antivity_weather.xml使用帧布局的布局方式。至此,天气界面就编写完成了。

3、将天气显示到界面上

首先在Utility类中添加一个用于解析天气JSON数据的方法,如下图所示:

这里写图片描述

接下来的工作是再活动中请求天气数据,以及将数据展示到界面上。修改WeatherActivity代码(之前创建了WeatherActivity,会生成activity_weatehr.xml布局文件和WeatherActivity.java文件)。如下所示:

public class WeatherActivity extends AppCompatActivity {public DrawerLayout drawerLayout;private Button navButton;public SwipeRefreshLayout swipeRefresh;private String mWeatherId;private ScrollView weatherLayout;//滚动视图对象private TextView titleCity;//基本信息--城市名private TextView titleUpdateTime;//基本信息--更新时间private TextView titleLat;//基本信息--经度private TextView titleLon;//基本信息--纬度private TextView weatherInfoText;//实时天气信息--天气信息private TextView flText;//实时天气信息--体感温度private TextView humText;//实时天气信息--相对湿度private TextView pcpnText;//实时天气信息--降水量private TextView presText;//实时天气信息--大气压强private TextView degreeText;//实时天气信息--温度private TextView visText;//实时天气信息--能见度private TextView dirText;//实时天气信息--风向private TextView scText;//实时天气信息--风力private TextView spdText;//实时天气信息--风速private LinearLayout forecastLayout;//线性布局对象--预报天气private TextView aqiText;//空气质量--空气质量指数private TextView coText;//空气质量--一氧化碳指数private TextView no2Text;//空气质量--二氧化氮指数private TextView o3Text;//空气质量--臭氧指数private TextView pm10Text;//空气质量--PM10指数private TextView pm25Text;//空气质量--PM2.5指数private TextView qltyText;//空气质量--空气质量水平private TextView so2Text;//空气质量--二氧化硫指数private TextView airText;//生活建议--空气质量指数private TextView comfortText;//生活建议--舒适度指数private TextView carWashText;//生活建议--洗车指数private TextView drsgText;//生活建议--穿衣指数private TextView fluText;//生活建议--感冒指数private TextView sportText;//生活建议--运动指数private TextView travText;//生活建议--旅游指数private TextView uvText;//生活建议--紫外线指数private ImageView bingPicImg;//背景图片@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//版本控制(当系统版本大于等于21,也就是5.0以上系统时才会执行后面的代码)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);}setContentView(R.layout.activity_weather);//初始化各控件bingPicImg = (ImageView)findViewById(R.id.bing_pic_img);//背景图片weatherLayout = (ScrollView) findViewById(R.id.weather_layout);//滚动视图对象titleCity = (TextView) findViewById(R.id.title_city);//基本信息--城市titleUpdateTime = (TextView) findViewById(R.id.title_update_time);//基本信息--更新时间titleLat = (TextView) findViewById(R.id.lat_text);//基本信息--经度titleLon = (TextView) findViewById(R.id.lon_text);//基本信息--纬度weatherInfoText = (TextView) findViewById(R.id.weather_info_text);//实时天气信息--天气信息flText = (TextView) findViewById(R.id.fl_text) ;//实时天气信息--体感温度humText = (TextView) findViewById(R.id.hum_text);//实时天气信息--相对湿度pcpnText = (TextView) findViewById(R.id.pcpn_text);//实时天气信息--降水量presText = (TextView) findViewById(R.id.pres_text); //实时天气信息--大气压强degreeText = (TextView) findViewById(R.id.degree_text);//实时天气信息--温度visText = (TextView) findViewById(R.id.vis_text);//实时天气信息--能见度dirText = (TextView) findViewById(R.id.dir_text);//实时天气信息--风向scText = (TextView) findViewById(R.id.sc_text);//实时天气信息--风力spdText = (TextView) findViewById(R.id.spd_text); //实时天气信息--风速forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout);//线性布局对象--预报天气aqiText = (TextView) findViewById(R.id.aqi_text);//空气质量--空气质量指数coText = (TextView) findViewById(R.id.co_text);//空气质量--一氧化碳指数no2Text = (TextView) findViewById(R.id.no2_text);//空气质量--二氧化氮指数o3Text = (TextView) findViewById(R.id.o3_text);//空气质量--臭氧指数pm10Text = (TextView) findViewById(R.id.pm10_text); //空气质量--PM10指数pm25Text = (TextView) findViewById(R.id.pm25_text);//空气质量--PM2.5指数qltyText = (TextView) findViewById(R.id.qlty_text);//空气质量--空气质量水平so2Text = (TextView) findViewById(R.id.so2_text); //空气质量--二氧化硫指数airText = (TextView) findViewById(R.id.air_text); //生活建议--空气质量指数comfortText = (TextView) findViewById(R.id.comfort_text);//生活建议--舒适度指数carWashText = (TextView) findViewById(R.id.car_wash_text);//生活建议--洗车指数drsgText = (TextView) findViewById(R.id.drsg_text);//生活建议--穿衣指数fluText = (TextView) findViewById(R.id.flu_text); //生活建议--感冒指数sportText = (TextView) findViewById(R.id.sport_text);//生活建议--运动指数travText = (TextView) findViewById(R.id.trav_text);//生活建议--旅游指数uvText = (TextView) findViewById(R.id.uv_text);//生活建议--紫外线指数drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);//DrawerLayout实例navButton = (Button) findViewById(R.id.nav_button);//Button实例swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);//获取SwipeRefreshLayout的实例swipeRefresh.setColorSchemeResources(R.color.colorPrimary);//调用setColorSchemeResources()方法来设置下拉刷新进度条的颜色SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);String weatherString = prefs.getString("weather", null);if(weatherString != null){//有缓存时直接解析天气数据Weather weather = Utility.handleWeatherResponse(weatherString);mWeatherId = weather.basic.weatherId;showWeatherInfo(weather);}else{//无缓存时去服务器查询天气mWeatherId = getIntent().getStringExtra("weather_id");//在请求数据的时候将ScrollView()隐藏weatherLayout.setVisibility(View.INVISIBLE);//从服务器请求天气数据requestWeather(mWeatherId);}//设置下拉刷新监听器swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener(){@Overridepublic void onRefresh() {requestWeather(mWeatherId);//请求天气信息}});String bingPic = prefs.getString("bing_pic",null);if(bingPic != null){//如果有缓存数据就直接使用Glide来加载这张图片Glide.with(this).load(bingPic).into(bingPicImg);}else{//如果没有缓存数据就调用loadBingPic()方法去请求今日的必应背景图loadBingPic();//加载每日一图}//请求新选择城市的天气信息navButton.setOnClickListener(new View.OnClickListener(){@Overridepublic void onClick(View v) {drawerLayout.openDrawer(GravityCompat.START);}});}/*** 根据天气ID请求城市天气信息*/public void requestWeather(final String weatherId){//组装接口地址String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId + "&key=8518f3bef50144e39994370699b08d5e";//向组装好的地址发送请求,服务器会将相应城市的天气信息以JSON()格式返回HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {@Overridepublic void onResponse(Call call, Response response) throws IOException {final String responseText = response.body().string();//将返回的JSON数据转换成Weather对象final Weather weather = Utility.handleWeatherResponse(responseText);//将当前线程切换到主线程runOnUiThread(new Runnable() {@Overridepublic void run() {if(weather != null && "ok".equals(weather.status)){//请求天气成功//将返回的数据缓存到SharedPreferences当中SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();editor.putString("weather", responseText);editor.apply();mWeatherId = weather.basic.weatherId;//调用showWeatherInfo()方法进行内容显示showWeatherInfo(weather);}else{Toast.makeText(WeatherActivity.this,"获取天气信息失败",Toast.LENGTH_SHORT).show();}swipeRefresh.setRefreshing(false);//刷新事件结束,将进度条隐藏起来}});//每次请求天气信息的时候也会刷新背景图片loadBingPic();}@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(WeatherActivity.this,"获取天气信息失败",Toast.LENGTH_SHORT).show();swipeRefresh.setRefreshing(false);//刷新事件结束,将进度条隐藏起来}});}});loadBingPic();//加载每日一图}/*** 加载必应每日一图*/private void loadBingPic(){String requestBingPic = "http://guolin.tech/api/bing_pic";//调用HttpUtil.sendOkHttpRequest()方法获取必应背景图的链接HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {final String bingPic = response.body().string();//将链接缓存到SharedPreferences当中SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();editor.putString("bing_pic",bingPic);editor.apply();//将线程切换到主线程runOnUiThread(new Runnable() {@Overridepublic void run() {//使用Glide来加载图片Glide.with(WeatherActivity.this).load(bingPic).into(bingPicImg);}});}});}/*** 处理并展示Weather实体类中的数据*/private void showWeatherInfo(Weather weather){String cityName = weather.basic.cityName;String updateTime = "更新时间:"+weather.basic.update.updateTime.split(" ")[1];String lat = "经度:"+weather.basic.cityLat;String lon = "纬度:"+weather.basic.cityLon;titleCity.setText(cityName);titleUpdateTime.setText(updateTime);titleLat.setText(lat);titleLon.setText(lon);String weatherInfo = weather.now.more.info;String flInfo = weather.now.fl + "℃";String humInfo = weather.now.hum;String pcpnInfo = weather.now.pcpn + "mm";String presInfo = weather.now.pres + "Pa";String degree = weather.now.temperature + "℃";String visInfo = weather.now.vis;String dirInfo = weather.now.wind.dir;String scInfo = weather.now.wind.sc;String spdInfo = weather.now.wind.spd + "m/s";weatherInfoText.setText(weatherInfo);flText.setText(flInfo);humText.setText(humInfo);pcpnText.setText(pcpnInfo);presText.setText(presInfo);degreeText.setText(degree);visText.setText(visInfo);dirText.setText(dirInfo);scText.setText(scInfo);spdText.setText(spdInfo);forecastLayout.removeAllViews();for(Forecast forecast : weather.forecastList){View view = LayoutInflater.from(this).inflate(R.layout.forecast_item,forecastLayout,false);TextView dateText = (TextView)view.findViewById(R.id.date_text);TextView infoText = (TextView)view.findViewById(R.id.info_text);TextView maxText = (TextView)view.findViewById(R.id.max_text);TextView minText = (TextView)view.findViewById(R.id.min_text);TextView dirText = (TextView)view.findViewById(R.id.dir_text);TextView scText = (TextView)view.findViewById(R.id.sc_text);dateText.setText(forecast.date);infoText.setText(forecast.more.info);maxText.setText(forecast.temperature.max);minText.setText(forecast.temperature.min);dirText.setText(forecast.wind.dir);scText.setText(forecast.wind.sc);forecastLayout.addView(view);}if(weather.aqi != null){aqiText.setText(weather.aqi.city.aqi);coText.setText(weather.aqi.city.co);no2Text.setText(weather.aqi.city.no2);o3Text.setText(weather.aqi.city.o3);pm10Text.setText(weather.aqi.city.pm10);pm25Text.setText(weather.aqi.city.pm25);qltyText.setText(weather.aqi.city.qlty);so2Text.setText(weather.aqi.city.so2);}String air = "空气质量:"+weather.suggestion.air.info;String comfort = "舒适度:"+weather.suggestion.comfort.info;String carWash = "洗车指数:"+weather.suggestion.carWash.info;String drsg = "穿衣指数:"+weather.suggestion.drsg.info;String flu = "感冒指数:"+weather.suggestion.flu.info;String sport = "运动建议:"+weather.suggestion.sport.info;String trav = "旅游指数:"+weather.suggestion.trav.info;String uv = "紫外线指数:"+weather.suggestion.uv.info;airText.setText(air);comfortText.setText(comfort);carWashText.setText(carWash);drsgText.setText(drsg);fluText.setText(flu);sportText.setText(sport);travText.setText(trav);uvText.setText(uv);//在设置完所有数据后,再将ScrollView设为可见weatherLayout.setVisibility(View.VISIBLE);//激活AutoUpdateService这个服务,只要选中了某个城市并成功更新天气之后,// AutoUpdateService就会一直在后台运行,并保证每8个小时更新一次天气Intent intent = new Intent(this, AutoUpdateService.class);startService(intent);}}

这个活动的详细分析在代码注释里已给出,接下来就是从省市县列表的界面跳转到天气界面,修改ChooseAreaFragment中的代码,如下所示:

这里写图片描述
另外,还需要在MainActivity中加入一个缓存数据判断,修改MainActivity中的代码,如下所示:

这里写图片描述

4、获取必应每日一图

使用郭霖大神准备的每日一图接口:http://guolin.tech/api/bing_pic ,访问这个接口,服务器就会返回今日的必应背景图链接,然后我们再使用Glide去加载这张图片。首先修改activity_weather.xml中的代码。如下所示:

这里写图片描述

接着修改WeatherActivity中的代码,如下所示:

这里写图片描述

这里写图片描述

 /*** 加载必应每日一图*/private void loadBingPic(){String requestBingPic = "http://guolin.tech/api/bing_pic";//调用HttpUtil.sendOkHttpRequest()方法获取必应背景图的链接HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {final String bingPic = response.body().string();//将链接缓存到SharedPreferences当中SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this).edit();editor.putString("bing_pic",bingPic);editor.apply();//将线程切换到主线程runOnUiThread(new Runnable() {@Overridepublic void run() {//使用Glide来加载图片Glide.with(WeatherActivity.this).load(bingPic).into(bingPicImg);}});}});}

再添加版本控制的代码,如下图所示:

这里写图片描述

##五、手动更新天气和切换城市

1、手动更新天气

采用下拉刷新的方式更新事件,首先修改activity_weather.xml中的代码,如下图:

这里写图片描述

然后修改WeatherActivity中的代码,加入更新天气的处理逻辑:

这里写图片描述

这里写图片描述

2、切换城市
将天气的布局引入碎片,再将碎片放入滑动菜单,在正常情况下,它不占据主界面的任何空间,想要切换城市的时候只需要通过滑动的方式将菜单显示出来就可以了。此外,我们在头布局添加一个切换城市的按钮。修改title.xml中的代码,如下所示:

这里写图片描述

接着修改activity_weather.xml布局来加入滑动菜单功能,如下所示:

这里写图片描述

接下来需要在WeatherActivity中加入滑动菜单的逻辑处理,修改WeatherActivity中的代码。如下所示:

这里写图片描述

打开滑动菜单后需要根据选择的不同状态来进行不同的逻辑,修改ChooseAreaFragment中的代码,如下所示:

这里写图片描述

这样就把切换城市的功能全部完成了。

##六、后台自动更新天气

首先在service包下新建一个服务,右击com.coolweather.android.service->New->Service->Service,创建一个AutoUpdateService,并将Exported和Enable这两个属性都选中,然后修改AutoUpdateService中的代码,如下所示:

public class AutoUpdateService extends Service {@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {updateWeather();updateBingPic();AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);int anHour = 8 * 60 * 60 * 1000;//8小时的毫秒数long triggerAtTime = SystemClock.elapsedRealtime() + anHour;Intent i = new Intent(this, AutoUpdateService.class);PendingIntent pi = PendingIntent.getService(this,0,i,0);manager.cancel(pi);manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pi);return super.onStartCommand(intent,flags,startId);}/*** 更新天气信息*/private void updateWeather(){//将更新后的数据存储在SharedPreferences文件中,打开WeatherActivity的时候会优先从SharedPreferences缓存中读取数据SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);String weatherString = prefs.getString("weather",null);if(weatherString != null){//有缓存时直接解析天气数据Weather weather = Utility.handleWeatherResponse(weatherString);String weatherId = weather.basic.weatherId;String weatherUrl = "http://guolin.tech/api/weather?cityid=?"+weatherId+"&key=8518f3bef50144e39994370699b08d5e";HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {String responseText = response.body().string();Weather weather = Utility.handleWeatherResponse(responseText);if(weather != null && "ok".equals(weather.status)){SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(AutoUpdateService.this).edit();editor.putString("weather",responseText);editor.apply();}}});}}/*** 更新必应每日一图*/private void updateBingPic(){String requestBingPic = "Http://guolin.tech/api/bing_pic";HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {String bingPic = response.body().string();SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(AutoUpdateService.this).edit();editor.putString("bing_pic",bingPic);editor.apply();}});}
}

然后激活这段代码,修改WeatherActivity中的代码,如下所示:

这里写图片描述

##七、修改图标和名称

自己准备好一张图片来作为软件图标,将这张图片命名为logo.png,放入所有以mipmap开头的目录下,然后修改AndroidManifest.xml中的代码,如下所示:

这里写图片描述

接下来修改程序名称。打开res/values/sring.xml文件,如下:

这里写图片描述

至此,软件就基本完成了。界面效果如下:
这里写图片描述

这篇关于Android实战:CoolWeather酷欧天气(加强版数据接口)代码详解(下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/249448

相关文章

Python正则表达式语法及re模块中的常用函数详解

《Python正则表达式语法及re模块中的常用函数详解》这篇文章主要给大家介绍了关于Python正则表达式语法及re模块中常用函数的相关资料,正则表达式是一种强大的字符串处理工具,可以用于匹配、切分、... 目录概念、作用和步骤语法re模块中的常用函数总结 概念、作用和步骤概念: 本身也是一个字符串,其中

Nginx location匹配模式与规则详解

《Nginxlocation匹配模式与规则详解》:本文主要介绍Nginxlocation匹配模式与规则,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、环境二、匹配模式1. 精准模式2. 前缀模式(不继续匹配正则)3. 前缀模式(继续匹配正则)4. 正则模式(大

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Java中的JSONObject详解

《Java中的JSONObject详解》:本文主要介绍Java中的JSONObject详解,需要的朋友可以参考下... Java中的jsONObject详解一、引言在Java开发中,处理JSON数据是一种常见的需求。JSONObject是处理JSON对象的一个非常有用的类,它提供了一系列的API来操作J

SpringBoot使用GZIP压缩反回数据问题

《SpringBoot使用GZIP压缩反回数据问题》:本文主要介绍SpringBoot使用GZIP压缩反回数据问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot使用GZIP压缩反回数据1、初识gzip2、gzip是什么,可以干什么?3、Spr