之前实现了能够罗列可以罗列出全国所有的省、市、县,然后就是查询全国任意城市的天气信息。查询天气信息使用的是和风天气的api,这个api获得的天气信息是JSON格式的。
使用GSON库解析JSON数据的第一步要先定义用于映照的类。
我对官方实例做了一下删减,然后可以看到这次我想要展示在这次这个项目中的信息:
{ "HeWeather5": [ { "aqi": { "city": { "aqi": "46", "pm25": "26", } }, "basic": { "city": "秦皇岛", "id": "CN101091101", "update": { "loc": "2017-09-10 14:46", } }, "daily_forecast": [ { "cond": { "txt_d": "小雨", }, "date": "2017-09-10", "tmp": { "max": "26", "min": "17" }, }, { "cond": { "txt_d": "晴", }, "date": "2017-09-11", , "tmp": { "max": "25", "min": "12" }, }, { "cond": { "txt_d": "晴", }, "date": "2017-09-12", "tmp": { "max": "26", "min": "15" }, } ], "status": "ok", "suggestion": { "comf": { "brf": "较舒适", "txt": "白天有雨,从而使空气湿度加大,会使人们感觉有点儿闷热,但早晚的天气很凉爽、舒适。" }, "cw": { "brf": "不宜", "txt": "不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。" }, "sport": { "brf": "较不宜", "txt": "有降水,推荐您在室内进行健身休闲运动;若坚持户外运动,须注意携带雨具并注意避雨防滑。" }, } } ] }
其中天气情况AQI:
{ "aqi": { "city": { "aqi": "46", "pm25": "26", } }
}
定义的AQI类代码为:
public class AQI { public AQIcity city; public class AQIcity{ public String aqi; public String pm25; } }
这里要说的就是“{}”包括一个类要用一个类去映照。
然后是城市的信息Basic:
{ "basic": { "city": "秦皇岛", "id": "CN101091101", "update": { "loc": "2017-09-10 14:46", } },
对应应该定义映照的java类:
public class Basic { @SerializedName("city") public String cityName; @SerializedName("id") public String weatherId; public Update update; public class Update{ @SerializedName("loc") public String updateTime; } }
如果获得的数据的变量名设置自己不喜欢,可以用@SerializedName("loc")去映照,
然后是天气预测情况daily_forecast:
"daily_forecast": [ { "cond": { "txt_d": "小雨", }, "date": "2017-09-10", "tmp": { "max": "26", "min": "17" }, }, { "cond": { "txt_d": "晴", }, "date": "2017-09-11", , "tmp": { "max": "25", "min": "12" }, }, { "cond": { "txt_d": "晴", }, "date": "2017-09-12", "tmp": { "max": "26", "min": "15" }, } ],
用“[]”包括的是数组,也就是说这个daily_forecast在建立映照的时候,应该也建立一个数组。不过这里先定义数组的成员:
public class Forecast { public String date; @SerializedName("tmp") public Temperature temperature; @SerializedName("cond") public More more; public class Temperature{ public String max; public String min; } public class More{ @SerializedName("txt_d") public String info; } }
然后是其他就没什么特殊的了:
public class Now { @SerializedName("tmp") public String temperature; @SerializedName("cond") public More more; public class More{ @SerializedName("txt") public String info; } } public class Suggestion { @SerializedName("comf") public Comfort comfort; @SerializedName("cw") public CarWash carWash; 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; } }
最后要合在一起,因为对于整个天气信息来说,上面的都是用“{}”包括的一个类,所以:
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; }
对于[]数组就是在这里定义。
然后就是具体的解析代码:
public class Utility { /** * 将返回的JSON数据解析成Weather类 */ 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; } }
这样就解析完毕了。
然后是界面的布局代码编写了,
<RelativeLayout 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: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> </RelativeLayout>
这是整个活动布局,其中正文部分使用的是子项布局,因为内容比较多,这样显得条理一些。
layout/title:
<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="15dp"
android:layout_centerVertical="true"
android:layout_alignParentLeft="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="15dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:textColor="#fff"
android:textSize="16sp"/>
</RelativeLayout>
layout/now:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginLeft="15dp" android:layout_marginRight="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:textAlignment="textEnd" android:textColor="#fff" android:textSize="20sp" /> </LinearLayout>
layout/forecast:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="15dp" android:background="#8000"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="预报" android:layout_marginLeft="15dp" android:layout_marginTop="15dp" android:textColor="#fff" android:textSize="20sp"/> <LinearLayout android:id="@+id/forecast_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> </LinearLayout> </LinearLayout>
layout/forecast_item:
<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/data_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: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>
layout/aqi:
<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:text="空气质量" android:textColor="#fff" android:textSize="20sp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="15dp"> <RelativeLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_centerVertical="true"> <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:text="AQI指数" android:textColor="#fff"/> </LinearLayout> </RelativeLayout> <RelativeLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_centerVertical="true"> <TextView android:id="@+id/pm25_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:text="PM2.5指数" android:textColor="#fff"/> </LinearLayout> </RelativeLayout> </LinearLayout> </LinearLayout>
layout/suggestion:
<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:text="生活建议" android:layout_marginLeft="15dp" android:layout_marginTop="15dp" android:textColor="#fff" android:textSize="20sp"/> <TextView android:id="@+id/comfort_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#fff" android:layout_margin="15dp"/> <TextView android:id="@+id/car_wash_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:layout_margin="15dp"/> <TextView android:id="@+id/sport_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#fff" android:layout_margin="15dp" /> </LinearLayout>
然后是活动的java代码:
public class WeatherActivity extends AppCompatActivity { public SwipeRefreshLayout swipeRefreshLayout; private ImageView bingPicImg; 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; public DrawerLayout drawerLayout; public Button navbutton; String weatherId; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //使状态栏和背景图融合在一块 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); drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); navbutton = (Button) findViewById(R.id.nav_button); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String weatherString = prefs.getString("weather",null); 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); } //向下滑动刷新天气 swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener(){ @Override public void onRefresh() { 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(); } //顶部按钮打开侧边栏 navbutton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { drawerLayout.openDrawer(GravityCompat.START); } }); } /** * 根据天气ID请求城市天气信息 */ public void requestWeather(final String weatherId){ String weatherUrl = "http://guolin.tech/api/weather?cityid=" + weatherId +"&key=32d1c829ed7d483086f4f5b4d5947cef"; 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); Toast.makeText(WeatherActivity.this, "天气获取成功", Toast.LENGTH_SHORT).show(); }else { Toast.makeText(WeatherActivity.this, "天气获取失败", Toast.LENGTH_SHORT).show(); } swipeRefreshLayout.setRefreshing(false);//传入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(); swipeRefreshLayout.setRefreshing(false);//传入false表示刷新事件结束 } }); } }); } /** * 处理展示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 dataText = (TextView) view.findViewById(R.id.data_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); dataText.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); } private void loadBingPic(){ String Url = "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=en-US"; HttpUtil.sendOkHttpRequest(Url, new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { String responseText = response.body().string(); final Images images = Utility.handleImagesrResponse(responseText); final String url = "http://s.cn.bing.net"+ images.url; try { runOnUiThread(new Runnable() { @Override public void run() { Glide.with(WeatherActivity.this).load(url).into(bingPicImg); } }); }catch (Exception e){ e.printStackTrace(); } } @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(); } }); } }); } }
这个活动实现的功能大概有:
1、向下滑动刷新:
布局代码这样编写。
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> </ScrollView...> </android.support.v4.widget.SwipeRefreshLayout>
活动中的JAVA代码:
public SwipeRefreshLayout swipeRefreshLayout; //向下滑动刷新天气 swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent); swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener(){ @Override public void onRefresh() { requestWeather(weatherId);//从服务器获取天气信息 } });
public class Images { public String url; }
然后解析java代码:
public class Utility { public static Images handleImagesrResponse(String response){ try { JSONObject jsonObject = new JSONObject(response); JSONArray jsonArray = jsonObject.getJSONArray("images"); String weatherContent = jsonArray.getJSONObject(0).toString(); return new Gson().fromJson(weatherContent,Images.class); }catch (Exception e){ e.printStackTrace(); } return null; } }
活动中调用的代码:
private void loadBingPic(){ String Url = "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=en-US"; HttpUtil.sendOkHttpRequest(Url, new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { String responseText = response.body().string(); final Images images = Utility.handleImagesrResponse(responseText); final String url = "http://s.cn.bing.net"+ images.url; try { runOnUiThread(new Runnable() { @Override public void run() { Glide.with(WeatherActivity.this).load(url).into(bingPicImg); } }); }catch (Exception e){ e.printStackTrace(); } } @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(); } }); } }); }
3、侧边栏切换城市:
布局:
<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...> <fragment android:id="@+id/choose_area_fragment" android:name="xbt.exp20.ChooseAreaFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" /> </android.support.v4.widget.DrawerLayout>
之前的省市县列表作为侧边栏,
然后有一个按钮打开侧边栏:
//顶部按钮打开侧边栏 navbutton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { drawerLayout.openDrawer(GravityCompat.START); } });
这里打开侧边栏选中某个城市之后要自动把侧边栏关闭,以及刷新天气信息,所以需要修改碎片的列表点击响应:
//列表的点击事件响应 listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { //如果当前级别是省,那点击选取的就是某个省,而想要查询的是选中省有那些城市,市同理 ,想要查询这个城市有那些县 if(currentLevel == LEVEL_PROVINCE){ selectedProvince = provinceList.get(position); queryCities();//查询全国所有的省,优先从数据库查询,如果没有查到再去服务器上查询 }else if(currentLevel == LEVEL_CITY){ selectedCity = cityList.get(position); 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); startActivity(intent); getActivity().finish(); }else if(getActivity() instanceof WeatherActivity){ WeatherActivity activity = (WeatherActivity) getActivity(); activity.drawerLayout.closeDrawers(); activity.swipeRefreshLayout.setRefreshing(true); activity.requestWeather(weatherId); } } } });
使用instanceof关键字判断一个对象是否属于某个类的实例,判断当时碎片处于那个活动中,并设置相应的响应。
基础的根据城市ID获取天气信息,并展示在活动上,以及将获取过的天气缓存下来就不在赘述了
项目截图: