导图社区 coolweather_app_开发思路
仿《第一行代码》中的酷欧天气app的开发思路
编辑于2019-07-09 09:58:36coolweather app
src/main
assets
litepal.xml
将db中3个实体类添加到映射列表
java/com.example.coolweather
db
City
County
Province
数据库表结构,市、县、省 声明字段,生成getter和setter
子主题
gson
AQI
package com.example.coolweather.gson;import com.google.gson.annotations.SerializedName;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-03 21:28 */public class AQI { public AQICity city; public class AQICity{ @SerializedName("aqi") public String aqi; @SerializedName("pm25") public String pm25; }}
Basic
package com.example.coolweather.gson;import com.google.gson.annotations.SerializedName;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-03 21:27 */public class Basic { /** * 由于JSON中的一些字段不太适合直接作为Java字段命名,这里使用@SerializedName朱姐的方式让JSON字段和java字段建立映射关系 */ @SerializedName("city") public String cityName; @SerializedName("id") public String weatherId; public Update update; public class Update{ @SerializedName("loc") public String updateTime; }}
Forecast
package com.example.coolweather.gson;import com.google.gson.annotations.SerializedName;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-03 21:28 */public class Forecast { @SerializedName("date") public String date; @SerializedName("tmp") public Temperature temperature; @SerializedName("cond") public More more; public class Temperature{ @SerializedName("max") public String max; @SerializedName("min") public String min; } public class More{ @SerializedName("txt_d") public String info; }}
Now
package com.example.coolweather.gson;import com.google.gson.annotations.SerializedName;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-03 21:28 */public class Now { @SerializedName("tmp") public String temperature; @SerializedName("cond") public More more; public class More { @SerializedName("txt") public String info; }}
Suggestion
package com.example.coolweather.gson;import com.google.gson.annotations.SerializedName;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-03 21:28 */public class Suggestion { @SerializedName("comf") public Comfort comfort; @SerializedName("cw") public CarWash carWash; @SerializedName("sport") public Sport sport; public class Comfort{ @SerializedName("txt") public String info; } public class CarWash{ @SerializedName("txt") public String info; } public class Sport{ @SerializedName("txt") public String info; }}
返回天气信息的格式
Weather
package com.example.coolweather.gson;import com.google.gson.annotations.SerializedName;import java.util.List;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-03 21:40 */public class Weather { public String status; public Basic basic; public AQI aqi; public Now now; public Suggestion suggestion; @SerializedName("daily_forecast") public List<Forecast> forecastList;}
创建一个总的实体类来引用以上的各个实体类。
service
AutoUpdateService
package com.example.coolweather.service;import android.app.AlarmManager;import android.app.PendingIntent;import android.app.Service;import android.content.Intent;import android.content.SharedPreferences;import android.os.IBinder;import android.os.SystemClock;import android.preference.PreferenceManager;import com.example.coolweather.gson.Weather;import com.example.coolweather.util.HttpUtil;import com.example.coolweather.util.Utility;import org.jetbrains.annotations.NotNull;import java.io.IOException;import okhttp3.Call;import okhttp3.Callback;import okhttp3.Response;public class AutoUpdateService extends Service { public AutoUpdateService() { } @Override public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. // throw new UnsupportedOperationException("Not yet implemented"); return null; } @Override public 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 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=ca5202ab136049029e17824f7357f163"; HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() { @Override public void onResponse(@NotNull Call call, @NotNull 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(); } } @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { e.printStackTrace(); } }); } } /** * 更新必应每日一图 */ private void updateBingPic() { String requestBingPic = "http://guolin.tech/api/bing_pic"; HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { e.printStackTrace(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { String bingPic = response.body().string(); SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(AutoUpdateService.this).edit(); editor.putString("bing_pic", bingPic); editor.apply(); } }); }}
后台自动更新天气和图片服务
util
HttpUtil
Utility
package com.example.coolweather.util;import android.text.TextUtils;import com.example.coolweather.db.City;import com.example.coolweather.db.County;import com.example.coolweather.db.Province;import com.example.coolweather.gson.Weather;import com.google.gson.Gson;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;public class Utility { /** * 解析和处理服务器返回省级数据 * @param response * @return */ public static boolean handleProvinceResponse(String response) { if (!TextUtils.isEmpty(response)) { try { //json的对象的数组,用来接收传回的多个省份的数据 JSONArray allProvince = new JSONArray(response); for (int i = 0; i < allProvince.length(); i++) { //取出每一个省份 JSONObject provinceObject = allProvince.getJSONObject(i); Province province = new Province(); //解析出省份的name并将其赋值给province对象 province.setProvinceName(provinceObject.getString("name")); //解析出省份的id并将其赋值给province对象 province.setProvinceCode(provinceObject.getInt("id")); //将这一个省份保存到表中 province.save(); } //处理成功返回真 return true; } catch (JSONException e) { e.printStackTrace(); } } //处理成功返回假 return false; } /** * 解析和处理服务器返回市级数据 * @param response * @param provinceId * @return */ public static boolean handleCityResponse(String response,int provinceId) { if (!TextUtils.isEmpty(response)) { try { JSONArray allCities = new JSONArray(response); for (int i = 0; i < allCities.length(); i++) { JSONObject cityObject = allCities.getJSONObject(i); City city = new City(); city.setCityName(cityObject.getString("name")); city.setCityCode(cityObject.getInt("id")); city.setProvinceId(provinceId); city.save(); } return true; } catch (JSONException e) { e.printStackTrace(); } } return false; } /** * 解析和处理服务器返回县级数据 * @param response * @param cityId * @return */ public static boolean handleCuntyoResponse(String response, int cityId) { if (!TextUtils.isEmpty(response)) { try { JSONArray allCountys = new JSONArray(response); for (int i = 0; i < allCountys.length(); i++) { JSONObject countyObject = allCountys.getJSONObject(i); County county = new County(); county.setCountyName(countyObject.getString("name")); county.setWeatherId(countyObject.getString("weather_id")); county.setCityId(cityId); county.save(); } return true; } catch (JSONException e) { e.printStackTrace(); } } return false; } /** * 将返回的JSON数据解析成Weather实体类 * @param response * @return */ public static Weather handleWeatherResponse(String response){ try { JSONObject jsonObject = new JSONObject(response); JSONArray jsonArray = jsonObject.getJSONArray("HeWeather"); String weatherContent = jsonArray.getJSONObject(0).toString(); //调用fromJson()方法直接将JSON数据转成Weather对象 return new Gson().fromJson(weatherContent, Weather.class); }catch (Exception e){ e.printStackTrace(); } return null; }}
和服务器进行交互
ChooseAreFragment
package com.example.coolweather;import android.app.ProgressDialog;import android.content.Intent;import android.os.Bundle;import android.support.v4.app.Fragment;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;import com.example.coolweather.db.City;import com.example.coolweather.db.County;import com.example.coolweather.db.Province;import com.example.coolweather.util.HttpUtil;import com.example.coolweather.util.Utility;import org.litepal.LitePal;import java.io.IOException;import java.util.ArrayList;import java.util.List;import okhttp3.Call;import okhttp3.Callback;import okhttp3.Response;public class ChooseAreFragment extends Fragment { public static final int LEVEL_PROVINCE = 0; public static final int LEVEL_CITY = 1; public static final int LEVEL_COUNTY = 2; private ProgressDialog progressDialog; private TextView titleText; private Button backButton; private ListView listView; private ArrayAdapter<String> adapter;//适配器,与ListView配合使用 private List<String> dataList = new ArrayList<>();//动态数组 /** * 省列表 */ private List<Province> provinceList; /** * 市列表 */ private List<City> cityList; /** * 县列表 */ private List<County> countyList; /** * 选中的省份 */ private Province selectedProvince; /** * 选中的城市 */ private City selectedCity; /** * 当前选中的级别 */ private int currentLevel; /** * onCreateView()方法中获取到一些控件的实例, * 初始化了ArrayAdapter,将其设置为ListView的适配器。 * @param inflater * @param countainer * @param savedInstanceState * @return */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup countainer, Bundle savedInstanceState) { /** * 【LayoutInflater】其实是在res/layout/下找到xml布局文件,并且将其实例化, * 对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入; */ View view = inflater.inflate(R.layout.choose_area, countainer, false); titleText = (TextView) view.findViewById(R.id.title_text); backButton = (Button) view.findViewById(R.id.back_button); listView = (ListView) view.findViewById(R.id.list_view); adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, dataList); //载入listView listView.setAdapter(adapter); return view; } /** * onActivityCreated()方法中设置ListView和Button的点击事件, * 完成了基本的初始化操作,调用queryProvinces()方法,加载省级数据。 * @param savedInstanceState */ @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); //对列表设置监听事件 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (currentLevel == LEVEL_PROVINCE) { //记住选中的省份 selectedProvince = provinceList.get(position); //显示出省份对应下city的界面 queryCities(); } else if (currentLevel == LEVEL_CITY) { //记住选中的City selectedCity = cityList.get(position); //切换到相应的county界面 queryCounties(); } else if (currentLevel == LEVEL_COUNTY) { String weatherId = countyList.get(position).getWeatherId(); if (getActivity() instanceof MainActivity){ Intent intent = new Intent(getActivity(),WeatherActivity.class); intent.putExtra("weather_id",weatherId); Log.d(weatherId, "weatherId: "); startActivity(intent); getActivity().finish(); }else if (getActivity() instanceof WeatherActivity){ WeatherActivity activity = (WeatherActivity) getActivity(); activity.drawerLayout.closeDrawers(); activity.swipeRefresh.setRefreshing(true); activity.requestWeather(weatherId); } } } }); //为返回按钮注册监听事件 backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //若在county切换到City if (currentLevel == LEVEL_COUNTY) { queryCities(); } else if (currentLevel == LEVEL_CITY) { //若在City切换到province queryProvinces(); } } }); //初始状态下显示province queryProvinces(); } /** * 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器上查询 */ private void queryProvinces() { //设置标题栏 titleText.setText("中国"); //隐藏返回按钮 backButton.setVisibility(View.GONE); //在数据库中查询所有省份 provinceList = LitePal.findAll(Province.class); if (provinceList.size() > 0) { dataList.clear(); for (Province province : provinceList) { dataList.add(province.getProvinceName()); } //更新适配器中的内容,变为省份数据 adapter.notifyDataSetChanged(); listView.setSelection(0); currentLevel = LEVEL_PROVINCE; } else { //从服务器中查询 String address = "http://guolin.tech/api/china"; queryFromServer(address, "province"); } } private void queryCities() { //设置标题栏 titleText.setText(selectedProvince.getProvinceName()); //设置返回按钮可见 backButton.setVisibility(View.VISIBLE); //在数据库中查询对应的City数据 cityList = LitePal.where("provinceid=?", String.valueOf(selectedProvince.getId())).find(City.class); if (cityList.size() > 0) { dataList.clear(); for (City city : cityList) { dataList.add(city.getCityName()); } adapter.notifyDataSetChanged(); listView.setSelection(0); currentLevel = LEVEL_CITY; } else { int provinceCode = selectedProvince.getProvinceCode(); String address = "http://guolin.tech/api/china/" + provinceCode; queryFromServer(address, "city"); } } private void queryCounties() { titleText.setText(selectedCity.getCityName()); backButton.setVisibility(View.VISIBLE); countyList = LitePal.where("cityid = ?", String.valueOf(selectedCity.getId())).find(County.class); if (countyList.size() > 0) { dataList.clear(); for (County county : countyList) { dataList.add(county.getCountyName()); } adapter.notifyDataSetChanged(); listView.setSelection(0); currentLevel = LEVEL_COUNTY; } else { int provinceCode = selectedProvince.getProvinceCode(); int cityCode = selectedCity.getCityCode(); String address = "http://guolin.tech/api/china/" + provinceCode + "/" + cityCode; queryFromServer(address, "county"); } } /** * 根据传入的地址和类型从服务器上查询省市县数据 * 调用HttpUtil中的sendOkHttpRequest()方法向服务器发送请求,响应的数据回调到onResponse()方法中, * 然后调用Utility中的handleProvincesResponse()方法解析和处理服务器返回的数据,然后将其存储到数据库中。 * 最后再次调用queryProvinces()方法重新加载省级数据。 * 因为queryProvinces()涉及UI操作,则须在主线程中调用,因此借助runOnUiThread()方法从子线程切换到主线程。 * @param address * @param type */ private void queryFromServer(String address, final String type) { //未查出之前显示出进度条框 showProgressDialog(); HttpUtil.sendOkHttpRequest(address, new Callback() { @Override public void onFailure(Call call, IOException e) { //通过runOnUiThread回到主线程处理逻辑 getActivity().runOnUiThread(new Runnable() { @Override public void run() { closeProgressDialog(); Toast.makeText(getContext(), "加载失败", Toast.LENGTH_SHORT).show(); } }); } @Override public void onResponse(Call call, Response response) throws IOException { String responseText = response.body().string(); boolean result = false; if ("province".equals(type)) { result = Utility.handleProvinceResponse(responseText); } else if ("city".equals(type)) { result = Utility.handleCityResponse(responseText, selectedProvince.getId()); } else if ("county".equals(type)) { result = Utility.handleCuntyoResponse(responseText, selectedCity.getId()); } if (result) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { closeProgressDialog(); if ("province".equals(type)) { queryProvinces(); } else if ("city".equals(type)) { queryCities(); } else if ("county".equals(type)) { queryCounties(); } } }); } } }); } /** * 显示进度对话框 */ private void showProgressDialog() { if (progressDialog == null) { progressDialog = new ProgressDialog(getActivity()); progressDialog.setMessage("正在加载"); progressDialog.setCanceledOnTouchOutside(false); } progressDialog.show(); } /** * 关闭进度对话框 */ private void closeProgressDialog() { if (progressDialog != null) { progressDialog.dismiss(); } }}
遍历省市县数据碎片内有跳转到天气界面
MainActivity
package com.example.coolweather;import android.content.Intent;import android.content.SharedPreferences;import android.preference.PreferenceManager;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); if (prefs.getString("weather",null) != null){ Intent intent = new Intent(this,WeatherActivity.class); startActivity(intent); finish(); } }}
WeatherActivity
package com.example.coolweather;import android.content.Intent;import android.content.SharedPreferences;import android.graphics.Color;import android.os.Build;import android.os.Bundle;import android.preference.PreferenceManager;import android.support.v4.view.GravityCompat;import android.support.v4.widget.DrawerLayout;import android.support.v4.widget.SwipeRefreshLayout;import android.support.v7.app.AppCompatActivity;import android.view.LayoutInflater;import android.view.View;import android.widget.Button;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.service.AutoUpdateService;import com.example.coolweather.util.HttpUtil;import com.example.coolweather.util.Utility;import org.jetbrains.annotations.NotNull;import java.io.IOException;import okhttp3.Call;import okhttp3.Callback;import okhttp3.Response;/** * 项目名称:CoolWeather * 创建人:lita * 创建时间:2019-07-04 11:35 */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; public SwipeRefreshLayout swipeRefresh; public DrawerLayout drawerLayout; private Button navButton; @Override protected void onCreate(Bundle saveInstanceState) { super.onCreate(saveInstanceState); if (Build.VERSION.SDK_INT >= 21) { //调用getWindow().getDecorView()拿到当前活动的DecorView View decorView = getWindow().getDecorView(); //改变系统的UI显示,传入的两个值表示活动的布局会显示在状态栏上面 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); bingPicImg = (ImageView) findViewById(R.id.bing_pic_img); swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); navButton = (Button) findViewById(R.id.nav_button); swipeRefresh.setColorSchemeResources(R.color.colorPrimary); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String weatherString = prefs.getString("weather", null); final String weatherId; if (weatherString != null) { // 有缓存时直接解析天气数据 Weather weather = Utility.handleWeatherResponse(weatherString); weatherId = weather.basic.weatherId; showWeatherInfo(weather); } else { // 无缓冲时去服务器查询天气 weatherId = getIntent().getStringExtra("weather_id"); weatherLayout.setVisibility(View.INVISIBLE); requestWeather(weatherId); } swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { requestWeather(weatherId); } }); String bingPic = prefs.getString("bing_pic", null); if (bingPic != null) { Glide.with(this).load(bingPic).into(bingPicImg); } else { loadBingPic(); } navButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { drawerLayout.openDrawer(GravityCompat.START); } }); } /** * 加载必应每日一图 */ private void loadBingPic() { String requestBingPic = "http://guolin.tech/api/bing_pic"; HttpUtil.sendOkHttpRequest(requestBingPic, new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { e.printStackTrace(); } @Override public void onResponse(@NotNull Call call, @NotNull 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); } }); } }); } /** * 根据天气id请求城市天气信息 * * @param weatherId */ public void requestWeather(final String weatherId) { String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId + "&key=ca5202ab136049029e17824f7357f163"; // 发送请求 HttpUtil.sendOkHttpRequest(weatherUrl, new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { final String responseText = response.body().string(); // 调用Utility.handleWeatherResponse()将返回的JSON数据转换成Weather对象 final Weather weather = Utility.handleWeatherResponse(responseText); // 将线程切换到主线程 runOnUiThread(new Runnable() { @Override public void run() { if (weather != null && "ok".equals(weather.status)) { // 返回数据缓存到SharedPreferences中,并进行显示 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(); } swipeRefresh.setRefreshing(false); } }); } @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(); swipeRefresh.setRefreshing(false); } }); } }); loadBingPic(); } /** * 处理并展示Weather实体类中的数据 * 从Weather对象获取数据,然后显示到相应的空间上。 * @param weather */ public 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); Intent intent = new Intent(this, AutoUpdateService.class); startService(intent); } 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); }}
请求天气数据,将数据展示到界面上手动更新
res
drawable
layout
activity_main.xml
<?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"> <fragment android:id="@+id/choose_area_fragment" android:name="com.example.coolweather.ChooseAreFragment" android:layout_width="match_parent" android:layout_height="match_parent"/></FrameLayout>
activity_weather.xml
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_weather" 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" /> <!--滑动菜单DrawerLayout中的第一个子控件用于作为主屏幕中显示的内容;--> <!--第二个子控件用于作为滑动菜单中显示的内容,添加了用于遍历省市县数据的碎片。--> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <!--下拉刷新--> <android.support.v4.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="wrap_content" android:overScrollMode="never" android:scrollbars="none"> <!--fitsSystemWindows为系统状态栏留出空间--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:fitsSystemWindows="true" 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> </android.support.v4.widget.SwipeRefreshLayout> <fragment android:id="@+id/choose_area_fragment" android:name="com.example.coolweather.ChooseAreFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" /> </android.support.v4.widget.DrawerLayout></FrameLayout>
天气界面总布局
choose_area.xml
<?xml version="1.0" encoding="utf-8"?><!--头布局作为标题栏--><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#fff"> <!--布局高度为actionBar高度,背景色为colorPrimary--> <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/ic_back"/> </RelativeLayout> <!--省市县的数据显示在这里--> <!--listView会自动给每个子项之间增加一条分割线--> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView></LinearLayout>
遍历省市县功能布局 需复用,采用碎片式
aqi.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="wrap_content" android:layout_margin="15dp" android:background="#8000"> <!--标题--> <TextView android: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"/> <!--LinearLayout和RelativeLayout嵌套实现左右平分且居中对齐布局--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dp"> <RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true"> <!--AQI指数--> <TextView android:id="@+id/aqi_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#fff" android:textSize="40sp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#fff" android:text="AQI指数"/> </LinearLayout> </RelativeLayout> <RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true"> <!--PM2.5指数--> <TextView android:id="@+id/pm25_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#fff" android:textSize="40sp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="PM2.5指数" android:textColor="#fff"/> </LinearLayout> </RelativeLayout> </LinearLayout></LinearLayout>
forecast.xml
<?xml version="1.0" encoding="utf-8"?><!--半透明布局--><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dp" android:background="#8000" android:orientation="vertical"> <!--未来几天天气信息布局--> <!--标题--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="15dp" android:text="预报" android:textColor="#fff" android:textSize="20sp"/> <!--未来几天天气信息布局,根据服务器返回的数据在代码中动态添加--> <LinearLayout android:id="@+id/forecast_layout" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> </LinearLayout></LinearLayout>
forecast_item.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dp"> <!--未来天气信息子项布局--> <!--天气预报日期--> <TextView android: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" /> <!--天气概况--> <TextView android: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"/> <!--当天最高气温--> <TextView android:id="@+id/max_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:gravity="right" android:textColor="#fff"/> <!--当天最低气温--> <TextView android:id="@+id/min_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:gravity="right" android:textColor="#fff"/></LinearLayout>
now.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_margin="15dp"> <!--当天天气信息布局--> <!--当前气温--> <TextView android:id="@+id/degree_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:textColor="#fff" android:textSize="60sp" /> <!--天气概况--> <TextView android:id="@+id/weather_info_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" android:textColor="#fff" android:textSize="20sp"/></LinearLayout>
suggestion.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="wrap_content" android:layout_margin="15sp" android:background="#8000"> <!--生活建议信息布局--> <!--标题--> <TextView android:layout_marginLeft="15dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="15dp" android:text="生活建议" android:textColor="#fff" android:textSize="20sp"/> <!--舒适度--> <TextView android:id="@+id/comfort_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="15dp" android:textColor="#fff"/> <!--洗车指数--> <TextView android:id="@+id/car_wash_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="15dp" android:textColor="#fff"/> <!--运动建议--> <TextView android:id="@+id/sport_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="15dp" android:textColor="#fff"/></LinearLayout>
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/ic_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>
引用布局技术,将界面的不同部分写在不同的文件中,再通过引入布局的方式集成到activity_weather.xml中。
mipmap
values
strings.xml
styles.xml
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.coolweather"> <uses-permission android:name="android.permission.INTERNET" /> //网络许可 <application android:name="org.litepal.LitePalApplication" //数据库的配置,数据库和表会在首次执行任意数据库操作自动创建 android:allowBackup="true" android:icon="@mipmap/weather" android:label="@string/app_name" android:networkSecurityConfig="@xml/network_security_config" android:roundIcon="@mipmap/weather" android:supportsRtl="true" android:theme="@style/AppTheme"> <service android:name=".service.AutoUpdateService" android:enabled="true" android:exported="true"></service> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".WeatherActivity" /> //添加页面 </application></manifest>
配置文件
build.gradle
apply plugin: 'com.android.application'android { compileSdkVersion 28 defaultConfig { applicationId "com.example.coolweather" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } }}dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation 'org.litepal.android:java:3.0.0' implementation 'com.squareup.okhttp3:okhttp:4.0.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.github.bumptech.glide:glide:4.9.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'}
子主题
添加依赖库
litepal 数据库
okhttp3 网络请求
gson 解析json数据
glide 加载和展示图片
id 市id cityName 市名字 cityCode 市代号 provinceId 市所属省id
id:县id countyName:县名字 weatherId:县天气id cityId:县所属市id
id 省id provinceName 省名字 provinceCode 省代号
<litepal> <dbname value="coolweather" /> <version value="1" /> <list> <mapping class="com.example.coolweather.db.Province" /> <mapping class="com.example.coolweather.db.City" /> <mapping class="com.example.coolweather.db.County" /> </list> </litepal>
coolweather 库名1 库版本
客户端与服务器的交互发起http请求只需调用sendOkHttprequest()这个方法,传入一个地址,并且注册一个回调来处理服务器的响应。
处理服务器返回的Json格式的数据handleProvinceResponse()、handleCityResponse()、handleCuntyoResponse(),分别解析省,市,县.使用JSONArray和JSONObject进行解析,然后组装成实体类对象,再调用save()方法存储到数据库中handleWeatherResponse(),解析天气JSONObject和JSONArray将天气中的主体内容解析出来,之后可通过调用fromJson()方法将JSON转换成Weather对象。
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">