在某些应用程序中,经常有天气预报的功能,通过当地天气情况给予用户合适的建议。比如团购网站根据定位,天气推送用户需要的信息等。本篇博客会使用公共api当中天气预报接口信息,编写一个简易天气预报的界面。
获取天气信息接口如下:
http://api.map.baidu.com/telematics/v3/weather?location=九江&output=json&ak=FkPhtMBK0HTIQNh7gG4cNUttSTyr0nzo
可替换location对应的地址,比如改变location=九江为location=北京,及可以获取北京的天气情况与指数建议。
最终显示界面如下:
这里我针对已有接口中提供的信息,简易布局内容,查看上图,包括控件有:Toolbar,ListView,Spinner以及一些简单组件。接下来,我们就根据这个布局完成相关功能:
首先完成activity对应的布局界面编写:activity_forecast.xml
观察发现,这里涉及到listview和spinner,所以需要编写每一个item的布局,我先编写listview对应的布局:item_forecast_lv.xml
接下来编写spinner下拉列表对应的item的布局内容,在下拉列表的item中主要用于显示温度,风向等信息。item_forecast_sp.xml内容为:
至此,界面对应的所有布局都完成了。在开始编写代码前,我们需要首先明确需要用到哪些包或者依赖,倒入到当前app的build.gradle文件当中。此处我在libs文件夹中添加了gson用于解析数据,picasso用于加载网络图片,当然也可通过compile直接倒入,我在build.gradle用添加了这两个包对应的添加文件信息:
然后我们就开始正式分析需求,编写逻辑代码了:
1.获取接口内容,分析json数据格式,编写对应的java bean类,用于方便存储和获取其对应的信息。
2.编写listview和spinner对应的适配器信息,将布局加载为view对象,显示在每一个item格式当中。继承BaseAdapter类。
3.在主页面中为复杂视图设置起对应的适配器。并且进行网络加载,将获取并解析后的数据显示在适配器视图中。
根据以上布局进行编写,相信功能就能很快完成了~我们试一下!
分析接口数据,并生成bean类。
整体看为一个对象,包括4个属性,其中需要重点为results对应的数组容器,在向下分析,这个容器中只有一个对象,而这个对象中有包括4个属性,我们需要重点注意的包括数据index为天气指数信息,weather_data为每天的天气情况信息,currentcity为当前查询城市,pm25为空气质量情况。故生成bean类如下:
import java.util.List;
public class ForecastBean {
private int error;
private String status;
private String date;
private List results;
public int getError() {
return error;
}
public void setError(int error) {
this.error = error;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public List getResults() {
return results;
}
public void setResults(List results) {
this.results = results;
}
public static class ResultsBean {
/**
* currentCity : 九江
* pm25 : 105
* index : [{"des":"天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。","zs":"炎热","tipt":"穿衣指数","title":"穿衣"},{"des":"较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。","zs":"较适宜","tipt":"洗车指数","title":"洗车"},{"des":"各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。","zs":"少发","tipt":"感冒指数","title":"感冒"},{"des":"天气较好,户外运动请注意防晒,推荐您在室内进行低强度运动。","zs":"较适宜","tipt":"运动指数","title":"运动"},{"des":"紫外线辐射极强,建议涂擦SPF20以上、PA++的防晒护肤品,尽量避免暴露于日光下。","zs":"很强","tipt":"紫外线强度指数","title":"紫外线强度"}]
* weather_data : [{"date":"周一 06月11日 (实时:32℃)","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/qing.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/qing.png","weather":"晴","wind":"南风微风","temperature":"32 ~ 21℃"},{"date":"周二","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"南风微风","temperature":"29 ~ 21℃"},{"date":"周三","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/duoyun.png","weather":"多云","wind":"南风微风","temperature":"29 ~ 21℃"},{"date":"周四","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/duoyun.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/qing.png","weather":"多云转晴","wind":"东北风微风","temperature":"30 ~ 21℃"}]
*/
private String currentCity;
private String pm25;
private List index;
private List weather_data;
public String getCurrentCity() {
return currentCity;
}
public void setCurrentCity(String currentCity) {
this.currentCity = currentCity;
}
public String getPm25() {
return pm25;
}
public void setPm25(String pm25) {
this.pm25 = pm25;
}
public List getIndex() {
return index;
}
public void setIndex(List index) {
this.index = index;
}
public List getWeather_data() {
return weather_data;
}
public void setWeather_data(List weather_data) {
this.weather_data = weather_data;
}
public static class IndexBean {
/**
* des : 天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。
* zs : 炎热
* tipt : 穿衣指数
* title : 穿衣
*/
private String des;
private String zs;
private String tipt;
private String title;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public String getZs() {
return zs;
}
public void setZs(String zs) {
this.zs = zs;
}
public String getTipt() {
return tipt;
}
public void setTipt(String tipt) {
this.tipt = tipt;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
public static class WeatherDataBean {
/**
* date : 周一 06月11日 (实时:32℃)
* dayPictureUrl : http://api.map.baidu.com/images/weather/day/qing.png
* nightPictureUrl : http://api.map.baidu.com/images/weather/night/qing.png
* weather : 晴
* wind : 南风微风
* temperature : 32 ~ 21℃
*/
private String date;
private String dayPictureUrl;
private String nightPictureUrl;
private String weather;
private String wind;
private String temperature;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getDayPictureUrl() {
return dayPictureUrl;
}
public void setDayPictureUrl(String dayPictureUrl) {
this.dayPictureUrl = dayPictureUrl;
}
public String getNightPictureUrl() {
return nightPictureUrl;
}
public void setNightPictureUrl(String nightPictureUrl) {
this.nightPictureUrl = nightPictureUrl;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
public String getWind() {
return wind;
}
public void setWind(String wind) {
this.wind = wind;
}
public String getTemperature() {
return temperature;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
}
}
}
继续根据需求可知,我们需要在ListView的每一个item当中展示指数信息,即bean类当中的result集合的对象包括的index集合的信息,了解了数据源类型,我们就可以非常容易完成适配器部分:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
import com.animee.testinfo.R;
/**
* 这是ListView的适配器
*/
public class IndexAdapter extends BaseAdapter{
Context context;
// listView的数据源
List mDatas;
public IndexAdapter(Context context, List mDatas) {
this.context = context;
this.mDatas = mDatas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int i) {
return mDatas.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
IndexViewHolder holder = null;
if (view==null) {
view = LayoutInflater.from(context).inflate(R.layout.item_forecast_lv,null);
holder = new IndexViewHolder();
holder.titleTv = (TextView) view.findViewById(R.id.item_lv_tv_title);
holder.zsTv = (TextView) view.findViewById(R.id.item_lv_tv_zs);
holder.tiptTv = (TextView) view.findViewById(R.id.item_lv_tv_tipt);
holder.desTv = (TextView) view.findViewById(R.id.item_lv_tv_des);
view.setTag(holder);
}else{
holder = (IndexViewHolder) view.getTag();
}
ForecastBean.ResultsBean.IndexBean indexBean = mDatas.get(i);
holder.titleTv.setText(indexBean.getTitle());
holder.zsTv.setText(indexBean.getZs());
holder.desTv.setText(indexBean.getDes());
holder.tiptTv.setText(indexBean.getTipt());
return view;
}
class IndexViewHolder{
TextView titleTv,zsTv,tiptTv,desTv;
}
}
同理,我们可知需要在spinner的每一个item当中展示指数信息,即bean类当中的result集合的对象包括的weather_data集合的信息,了解了数据源类型,我们同样可以非常容易完成适配器部分:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.animee.testinfo.R;
import com.squareup.picasso.Picasso;
import java.util.List;
public class WeatherAdapter extends BaseAdapter{
Context context;
// Spinner的数据源
List mDatas;
public WeatherAdapter(Context context, List mDatas) {
this.context = context;
this.mDatas = mDatas;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int i) {
return mDatas.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
WeatherViewHolder holder = null;
if (view==null) {
view = LayoutInflater.from(context).inflate(R.layout.item_forecast_sp,null);
holder = new WeatherViewHolder();
holder.dayIv = (ImageView) view.findViewById(R.id.item_sp_iv_day);
holder.nightIv = (ImageView) view.findViewById(R.id.item_sp_iv_night);
holder.dateTv = (TextView) view.findViewById(R.id.item_sp_tv_date);
holder.tempTv = (TextView) view.findViewById(R.id.item_sp_tv_temp);
holder.weatherTv = (TextView) view.findViewById(R.id.item_sp_tv_weather);
holder.windTv = (TextView) view.findViewById(R.id.item_sp_tv_wind);
view.setTag(holder);
}else{
holder = (WeatherViewHolder) view.getTag();
}
ForecastBean.ResultsBean.WeatherDataBean bean = mDatas.get(i);
holder.dateTv.setText(bean.getDate());
holder.tempTv.setText(bean.getTemperature());
holder.windTv.setText(bean.getWind());
holder.weatherTv.setText(bean.getWeather());
// 使用Picasso加载白天图片和夜晚图片
Picasso.with(context).load(bean.getDayPictureUrl()).into(holder.dayIv);
Picasso.with(context).load(bean.getNightPictureUrl()).into(holder.nightIv);
return view;
}
class WeatherViewHolder{
ImageView dayIv,nightIv;
TextView dateTv,tempTv,weatherTv,windTv;
}
}
前面的铺垫已经完毕,我们正式进入正题,开始编写activity对应代码,设置适配器视图的信息,以及获取网络数据,通知其更新信息。获取网络数据,这里我使用原生的HttpURLConnection,先封装一下工具类:
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class HttpUtils {
public static String getJsonContent(String path){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
int hasRead = 0;
byte[]buf = new byte[1024];
while ((hasRead = is.read(buf))!=-1){
baos.write(buf,0,hasRead);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return baos.toString();
}
}
接下来就可以代入到使用当中:
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.WindowDecorActionBar;
import android.view.Window;
import android.widget.ListView;
import android.widget.Spinner;
import com.animee.testinfo.R;
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.List;
public class ForecastActivity extends AppCompatActivity {
private Spinner spinner;
private ListView listView;
// ListView的数据源
private List indexList = new ArrayList<>();
// Spinner的数据源
private List weather_dataList = new ArrayList<>();
private IndexAdapter indexAdapter;
private WeatherAdapter weatherAdapter;
public String url = "http://api.map.baidu.com/telematics/v3/weather?location=九江&output=json&ak=FkPhtMBK0HTIQNh7gG4cNUttSTyr0nzo";
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==1) {
String s = (String) msg.obj;
// 使用Gson进行解析
Gson gson = new Gson();
ForecastBean forecastBean = gson.fromJson(s, ForecastBean.class);
ForecastBean.ResultsBean resultsBean = forecastBean.getResults().get(0);
// 获取ListView的解析数据,然后添加到数据源当中,提示更新
List index = resultsBean.getIndex();
indexList.addAll(index);
indexAdapter.notifyDataSetChanged();
// 获取Spinner解析后的数据,添加到数据源当中,提示更新
List data = resultsBean.getWeather_data();
weather_dataList.addAll(data);
weatherAdapter.notifyDataSetChanged();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_forecast);
// 查找控件
spinner = (Spinner) findViewById(R.id.forecast_sp);
listView = (ListView) findViewById(R.id.forecast_lv);
// 获取数据源
// 创建适配器对象
indexAdapter = new IndexAdapter(this,indexList);
listView.setAdapter(indexAdapter);
weatherAdapter = new WeatherAdapter(this,weather_dataList);
spinner.setAdapter(weatherAdapter);
// 联网获取数据
loadData();
}
private void loadData() {
new Thread(new Runnable() {
@Override
public void run() {
// 联网获取数据的步骤·
String content = HttpUtils.getJsonContent(url);
Message msg = new Message();
msg.what = 1;
msg.obj = content;
handler.sendMessage(msg);
}
}).start();
}
}
最后一定要记得在清单文件当中注册activity,并且添加网络权限哦!
然后运行一下,就会发现简单天气预报功能实现了,当然你也可以更改地区信息,可让用户输入相关地区,或者使用定位功能,获取用户当前位置,然后查找对应天气情况,推送信息给使用者,大家可以尝试一下。感谢您的阅读!