简介
完整的模拟了一个天气软件
功能列表
- 罗列出全国所有的省、市、县
- 查看全国任意城市的天气信息
- 自由的切换城市,查看其他城市的天气
- 能手动更新以及后台自动更新天气
获取全国的省市县信息
利用 作者的服务器 去获取数据信息
通过数据信息中的 weather_id 去访问和风天气的接口,即可得到该地区的天气
注册 和风天气获取 API Key。
最后利用 weather_id 和 Key 便可拿到天气相关的数据。
创建数据库和表
新建 db 目录存放数据库的表,利用 LitePal 管理数据库;
在 build.gradle 添加依赖 implementation 'org.litepal.android:core:1.3.2'
新建类 Province,City 和 County 继承 DataSupport,分别保存省市县的数据信息。
新建 assets 目录,新建文件 litepal.xml,内容如下:
<litepal>
<dbname value="weather"/>
<version value="1"/>
<list>
<mapping class="com.example.weather.db.Province"/>
<mapping class="com.example.weather.db.City"/>
<mapping class="com.example.weather.db.County"/>
</list>
</litepal>
配置 LitePalApplication,在 MyApplication 中初始化
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
LitePalApplication.initialize(mContext);
}
遍历全国省市县数据
获取数据
数据最开始去需要从服务器获取的,所以需要和服务器交互,利用 OkHttp 开源库
在 build.gradle 添加依赖 implementation 'com.squareup.okhttp3:okhttp:3.10.0'
用法:
public class HttpUtil {
private static final String TAG = "ZLTEST";
public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address)
.build();
LogUtil.d(TAG, "request success");
client.newCall(request).enqueue(callback);
}
}
解析数据
由于服务器返回的数据是 json 格式的,所以需要利用 gson 去解析处理
在 build.gradle 添加依赖 implementation 'com.google.code.gson:gson:2.8.2'
用法:
public class Utility {
public static Weather handleWeatherResponse(String response) {
try {
JSONObject jsonObject = new JSONObject(response);
JSONArray jsonArray = jsonObject.getJSONArray("HeWeather");
String weatherContent = jsonArray.getJSONObject(0).toString();
return new Gson().fromJson(weatherContent, Weather.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
展示数据
利用 Fragment 去显示界面,用 fragment 是因为这个界面后面需要复用
新建布局文件,在 res/layout 新建文件 choose_area.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary">
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#fff"
android:textSize="20sp"/>
<Button
android:id="@+id/back_button"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginLeft="10dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@drawable/back"/>
</RelativeLayout>
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
新建 ChooseAreaFragment 类去处理相关逻辑,可以参考 源码文件
显示天气信息
定义 GSON 实体类
天气返回的数据格式:
{
"HeWeather": [
{
"status" : "ok",
"basic" : {},
"aqi" : {},
"now" : {},
"suggestion" : {},
"daily_forecast" : []
}
]
}
其中 basic,aqi,now,suggestion 和 daily_forecast 的内部又有具体的内容,所以可以将这个五个部分定义成五个类
新建 Weather 类, 根据每个部分不同的属性分别建类,这里不再赘述,可以参考源码
public class Weather {
public String status;
public AQI aqi;
public Basic basic;
public Now now;
public Suggestion suggestion;
@SerializedName("daily_forecast")
public List<Forecast> forecastList;
}
编写天气界面
新建一个 title.xml 作为头布局,用来显示城市名以及时间
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
<Button
android:id="@+id/nav_button"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginLeft="10dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:background="@drawable/home"/>
<TextView
android:id="@+id/title_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="#fff"
android:textSize="20sp"/>
<TextView
android:id="@+id/title_update_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textColor="#fff"
android:textSize="16sp"/>
</RelativeLayout>
新建天气主界面布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<ImageView
android:id="@+id/bing_pic_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<androidx.drawerlayout.widget.DrawerLayout
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/weather_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<LinearLayout
android: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>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<fragment
android:id="@+id/choose_area_fragment"
android:name="com.example.weather.ChooseAreaFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start">
</fragment>
</androidx.drawerlayout.widget.DrawerLayout>
</FrameLayout>
新建 WeatherActivity 类去处理相关逻辑,可以参考 源码文件
后台自动更新天气
利用 service 和 AlarmManager 实现定时更新
public class AutoUpdateService extends Service {
private static final String TAG = "ZLTEST";
public AutoUpdateService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
updateWeather();
updateBingPic();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int anHour = 8*60*1000;
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent intent1 = new Intent(this, AutoUpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(this,
0, intent1, 0);
manager.cancel(pendingIntent);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
return super.onStartCommand(intent, flags, startId);
}
private void updateBingPic() {
LogUtil.d(TAG,"updateBingPic");
String requestBingPic = "http://guolin.tech/api/bing_pic";
HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public 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();
}
});
}
private void updateWeather() {
LogUtil.d(TAG,"updateWeather");
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
String weatherString = preferences.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=a6ddec4e22e145eabb7e2ccafb24e0bd";
HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public 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();
}
}
});
}
}
}
修改图标和名称
修改文件 AndroidManifest.xml
android:icon 对应图标,android:label 对应名称
<application
android:name="com.example.weather.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/we"
android:label="@string/app_name"
android:roundIcon="@mipmap/we"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
</application>
项目源码下载
Github
|