步骤
- 系列文章
- 前言
- 实现效果
- 项目结构
- 1.获取必应每日一图
- 1.1修改修改activity_weather.xml
- 1.2修改WeatherActivity
- 2.背景图和状态栏效果修改
- 2.1修改WeatherActivity
- 2.2修改activity_weather.xml
- 附录.参考资料
- 下载资源
- 总结
系列文章
提示:转到安卓学习专栏,观看更多内容!
点我直达–>安卓学习专栏 本项目注意包名前缀的修改,改成你自己的,我的包名是:
com.example.coolweather,不然会报错。
前言
本次主题:给天气页面加上背景图片
项目实战继承前面的文章
上一篇文章
安卓学习专栏——实战项目酷欧天气(3)显示天气信息
传送门:
实现效果
项目结构
在com.coolweather.android包下几个包
- db包用于存放数据库模型相关的代码
- gson包用于存放GSON模型相关的代码
- service包用于存放服务相关的代码
- util包用于存放工具相 关的代码。
1.获取必应每日一图
必应这个网站每日会更新一张好看的背景图片,我们可以用必应的图片装饰我们的应用。
郭霖老师专门准备了一个获取必应每日一图的接口:http://guolin.tech/api/bing_pic(接口仅供学习,大家不要恶意商用爆破)
思路过程:
- 访问接口
- 服务器会返回今日的必应背景图链接
- 依靠链接使用Glide加载图片
1.1修改修改activity_weather.xml
在FrameLayout中添加了一个ImageView,并且将它的宽和高都设置成match_parent。由于FrameLayout默认情况下会将控件都放置在左上角,因此ScrollView会完全覆盖住ImageView,从而ImageView也就成为背景图片了
原来的“<ScrollView”上添加代码
<ImageView
android:id="@+id/bing_pic_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
完整代码
<?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" />
<ScrollView
android:id="@+id/weather_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/title" />
<include layout="@layout/now" />
<include layout="@layout/forecast" />
<include layout="@layout/aqi" />
<include layout="@layout/suggestion" />
</LinearLayout>
</ScrollView>
</FrameLayout>
1.2修改WeatherActivity
首先在onCreate() 方法中获取了新增控件ImageView的实例,
然后尝试从SharedPreferences中读取缓存的背景图片。
如果有缓存 的话就直接使用Glide来加载这张图片,如果没有的话就调 用loadBingPic() 方法去请求今日的必应背景图。
loadBingPic() 方法中的逻辑就非常简单了,先是调用了 HttpUtil.sendOkHttpRequest() 方法获取到必应背景图的链接,然 后将这个链接缓存到SharedPreferences当中,再将当前线程切换到主线 程,最后使用Glide来加载这张图片就可以了。
在requestWeather() 方法的最后也需要调用一下loadBingPic() 方 法,这样在每次请求天气信息的时候同时也会刷新背景图片。
添加代码
private ImageView bingPicImg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
// 初始化各控件
weatherLayout = (ScrollView) findViewById(R.id.weather_layout);
titleCity = (TextView) findViewById(R.id.title_city);
titleUpdateTime = (TextView) findViewById(R.id.title_update_time);
degreeText = (TextView) findViewById(R.id.degree_text);
weatherInfoText = (TextView) findViewById(R.id.weather_info_text);
forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout);
aqiText = (TextView) findViewById(R.id.aqi_text);
pm25Text = (TextView) findViewById(R.id.pm25_text);
comfortText = (TextView) findViewById(R.id.comfort_text);
carWashText = (TextView) findViewById(R.id.car_wash_text);
sportText = (TextView) findViewById(R.id.sport_text);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences
(this);
String weatherString = prefs.getString("weather", null);
if (weatherString != null) {
// 有缓存时直接解析天气数据
Weather weather = Utility.handleWeatherResponse(weatherString);
showWeatherInfo(weather);
} else {
// 无缓存时去服务器查询天气
String weatherId = getIntent().getStringExtra("weather_id");
weatherLayout.setVisibility(View.INVISIBLE);
requestWeather(weatherId);
}
bingPicImg = (ImageView) findViewById(R.id.bing_pic_img);
String bingPic = prefs.getString("bing_pic", null);
if (bingPic != null) {
Glide.with(this).load(bingPic).into(bingPicImg);
} else {
loadBingPic();
}
}
/**
* 加载必应每日一图
*/
private void loadBingPic() {
String requestBingPic = "http://guolin.tech/api/bing_pic";
HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
final String bingPic = response.body().string();
SharedPreferences.Editor editor = PreferenceManager.
getDefaultSharedPreferences(WeatherActivity.this).edit();
editor.putString("bing_pic", bingPic);
editor.apply();
runOnUiThread(new Runnable() {
@Override
public void run() {
Glide.with(WeatherActivity.this).load(bingPic).into
(bingPicImg);
}
});
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
});
}
完整代码
package com.example.coolweather;
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.example.coolweather.gson.Forecast;
import com.example.coolweather.gson.Weather;
import com.example.coolweather.util.HttpUtil;
import com.example.coolweather.util.Utility;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class WeatherActivity extends AppCompatActivity {
private ScrollView weatherLayout;
private TextView titleCity;
private TextView titleUpdateTime;
private TextView degreeText;
private TextView weatherInfoText;
private LinearLayout forecastLayout;
private TextView aqiText;
private TextView pm25Text;
private TextView comfortText;
private TextView carWashText;
private TextView sportText;
private ImageView bingPicImg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
// 初始化各控件
weatherLayout = (ScrollView) findViewById(R.id.weather_layout);
titleCity = (TextView) findViewById(R.id.title_city);
titleUpdateTime = (TextView) findViewById(R.id.title_update_time);
degreeText = (TextView) findViewById(R.id.degree_text);
weatherInfoText = (TextView) findViewById(R.id.weather_info_text);
forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout);
aqiText = (TextView) findViewById(R.id.aqi_text);
pm25Text = (TextView) findViewById(R.id.pm25_text);
comfortText = (TextView) findViewById(R.id.comfort_text);
carWashText = (TextView) findViewById(R.id.car_wash_text);
sportText = (TextView) findViewById(R.id.sport_text);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences
(this);
String weatherString = prefs.getString("weather", null);
if (weatherString != null) {
// 有缓存时直接解析天气数据
Weather weather = Utility.handleWeatherResponse(weatherString);
showWeatherInfo(weather);
} else {
// 无缓存时去服务器查询天气
String weatherId = getIntent().getStringExtra("weather_id");
weatherLayout.setVisibility(View.INVISIBLE);
requestWeather(weatherId);
}
bingPicImg = (ImageView) findViewById(R.id.bing_pic_img);
String bingPic = prefs.getString("bing_pic", null);
if (bingPic != null) {
Glide.with(this).load(bingPic).into(bingPicImg);
} else {
loadBingPic();
}
}
/**
* 根据天气id请求城市天气信息
*/
public void requestWeather(final String weatherId) {
String weatherUrl = "http://guolin.tech/api/weather?cityid=" +
weatherId + "&key=bc0418b57b2d4918819d3974ac1285d9";
HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
final Weather weather = Utility.handleWeatherResponse(responseText);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (weather != null && "ok".equals(weather.status)) {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(WeatherActivity.this)
.edit();
editor.putString("weather", responseText);
editor.apply();
showWeatherInfo(weather);
} else {
Toast.makeText(WeatherActivity.this, "获取天气信息失败",
Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(WeatherActivity.this, "获取天气信息失败",
Toast.LENGTH_SHORT).show();
}
});
}
});
}
/**
* 加载必应每日一图
*/
private void loadBingPic() {
String requestBingPic = "http://guolin.tech/api/bing_pic";
HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
final String bingPic = response.body().string();
SharedPreferences.Editor editor = PreferenceManager.
getDefaultSharedPreferences(WeatherActivity.this).edit();
editor.putString("bing_pic", bingPic);
editor.apply();
runOnUiThread(new Runnable() {
@Override
public void run() {
Glide.with(WeatherActivity.this).load(bingPic).into
(bingPicImg);
}
});
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
});
}
/**
* 处理并展示Weather实体类中的数据
*/
private void showWeatherInfo(Weather weather) {
String cityName = weather.basic.cityName;
String updateTime = weather.basic.update.updateTime.split(" ")[1];
String degree = weather.now.temperature + "℃";
String weatherInfo = weather.now.more.info;
titleCity.setText(cityName);
titleUpdateTime.setText(updateTime);
degreeText.setText(degree);
weatherInfoText.setText(weatherInfo);
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);
dateText.setText(forecast.date);
infoText.setText(forecast.more.info);
maxText.setText(forecast.temperature.max);
minText.setText(forecast.temperature.min);
forecastLayout.addView(view);
}
if (weather.aqi != null) {
aqiText.setText(weather.aqi.city.aqi);
pm25Text.setText(weather.aqi.city.pm25);
}
String comfort = "舒适度:" + weather.suggestion.comfort.info;
String carWash = "洗车指数:" + weather.suggestion.carWash.info;
String sport = "运动建议:" + weather.suggestion.sport.info;
comfortText.setText(comfort);
carWashText.setText(carWash);
sportText.setText(sport);
weatherLayout.setVisibility(View.VISIBLE);
}
}
2.背景图和状态栏效果修改
目前的效果
2.1修改WeatherActivity
修改WeatherActivity,背景图和状态栏融合。
Android 5.0及以上的系统才支持的,因此我们先在代码中做了一个系统版本号的判断,只有当版本号大于或等于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);
完整代码
package com.example.coolweather;
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.example.coolweather.gson.Forecast;
import com.example.coolweather.gson.Weather;
import com.example.coolweather.util.HttpUtil;
import com.example.coolweather.util.Utility;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
public class WeatherActivity extends AppCompatActivity {
private ScrollView weatherLayout;
private TextView titleCity;
private TextView titleUpdateTime;
private TextView degreeText;
private TextView weatherInfoText;
private LinearLayout forecastLayout;
private TextView aqiText;
private TextView pm25Text;
private TextView comfortText;
private TextView carWashText;
private TextView sportText;
private ImageView bingPicImg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather);
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);
// 初始化各控件
weatherLayout = (ScrollView) findViewById(R.id.weather_layout);
titleCity = (TextView) findViewById(R.id.title_city);
titleUpdateTime = (TextView) findViewById(R.id.title_update_time);
degreeText = (TextView) findViewById(R.id.degree_text);
weatherInfoText = (TextView) findViewById(R.id.weather_info_text);
forecastLayout = (LinearLayout) findViewById(R.id.forecast_layout);
aqiText = (TextView) findViewById(R.id.aqi_text);
pm25Text = (TextView) findViewById(R.id.pm25_text);
comfortText = (TextView) findViewById(R.id.comfort_text);
carWashText = (TextView) findViewById(R.id.car_wash_text);
sportText = (TextView) findViewById(R.id.sport_text);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences
(this);
String weatherString = prefs.getString("weather", null);
if (weatherString != null) {
// 有缓存时直接解析天气数据
Weather weather = Utility.handleWeatherResponse(weatherString);
showWeatherInfo(weather);
} else {
// 无缓存时去服务器查询天气
String weatherId = getIntent().getStringExtra("weather_id");
weatherLayout.setVisibility(View.INVISIBLE);
requestWeather(weatherId);
}
bingPicImg = (ImageView) findViewById(R.id.bing_pic_img);
String bingPic = prefs.getString("bing_pic", null);
if (bingPic != null) {
Glide.with(this).load(bingPic).into(bingPicImg);
} else {
loadBingPic();
}
}
/**
* 根据天气id请求城市天气信息
*/
public void requestWeather(final String weatherId) {
String weatherUrl = "http://guolin.tech/api/weather?cityid=" +
weatherId + "&key=bc0418b57b2d4918819d3974ac1285d9";
HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
final Weather weather = Utility.handleWeatherResponse(responseText);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (weather != null && "ok".equals(weather.status)) {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(WeatherActivity.this)
.edit();
editor.putString("weather", responseText);
editor.apply();
showWeatherInfo(weather);
} else {
Toast.makeText(WeatherActivity.this, "获取天气信息失败",
Toast.LENGTH_SHORT).show();
}
}
});
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(WeatherActivity.this, "获取天气信息失败",
Toast.LENGTH_SHORT).show();
}
});
}
});
}
/**
* 加载必应每日一图
*/
private void loadBingPic() {
String requestBingPic = "http://guolin.tech/api/bing_pic";
HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
final String bingPic = response.body().string();
SharedPreferences.Editor editor = PreferenceManager.
getDefaultSharedPreferences(WeatherActivity.this).edit();
editor.putString("bing_pic", bingPic);
editor.apply();
runOnUiThread(new Runnable() {
@Override
public void run() {
Glide.with(WeatherActivity.this).load(bingPic).into
(bingPicImg);
}
});
}
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
});
}
/**
* 处理并展示Weather实体类中的数据
*/
private void showWeatherInfo(Weather weather) {
String cityName = weather.basic.cityName;
String updateTime = weather.basic.update.updateTime.split(" ")[1];
String degree = weather.now.temperature + "℃";
String weatherInfo = weather.now.more.info;
titleCity.setText(cityName);
titleUpdateTime.setText(updateTime);
degreeText.setText(degree);
weatherInfoText.setText(weatherInfo);
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);
dateText.setText(forecast.date);
infoText.setText(forecast.more.info);
maxText.setText(forecast.temperature.max);
minText.setText(forecast.temperature.min);
forecastLayout.addView(view);
}
if (weather.aqi != null) {
aqiText.setText(weather.aqi.city.aqi);
pm25Text.setText(weather.aqi.city.pm25);
}
String comfort = "舒适度:" + weather.suggestion.comfort.info;
String carWash = "洗车指数:" + weather.suggestion.carWash.info;
String sport = "运动建议:" + weather.suggestion.sport.info;
comfortText.setText(comfort);
carWashText.setText(carWash);
sportText.setText(sport);
weatherLayout.setVisibility(View.VISIBLE);
}
}
2.2修改activity_weather.xml
修改activity_weather.xml解决头布局和状态栏紧贴在一起的问题。
由于系统状态栏已经成为我们布局的一部分,因此没有单独为它留出空间。当然,这个问题也是非常好解决的,借助android:fitsSystemWindows 属性。
android:fitsSystemWindows 属性,设置成true 就表示会为系统状态栏留出空间。
添加代码
android:fitsSystemWindows="true"
完整代码
<?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" />
<ScrollView
android:id="@+id/weather_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
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>
</FrameLayout>
附录.参考资料
下载资源
gitee地址
https://gitee.com/miao-zehao/cool-weather
安卓学习者实战项目酷欧天气(4)给天气页面加上背景图片示例
总结
大家喜欢的话,给个👍,点个关注!继续跟大家分享敲代码过程中遇到的问题!