这篇文章是Yahoo天气API系列文章的第一篇,我们的目标就是使用Yahoo提供的天气接口获取当前的气象情况。之前的两篇文章已经介绍过了其它的天气接口,比如openweathermap
接口,如果你感兴趣的话,可以看看这两篇文章文章一和文章二。
这篇文章中,我们主要介绍怎么用Yahoo的API检索出各个城市信息。假设你已经有了一个Yahoo开发者账户。如果还没有,那么可以通过这个链接注册一个(注册链接)。你必须拥有一个appid,虽然它是完全免费的,但是你在使用yahoo的API的时候必须用上它。在分析Yahoo的API的时候,我们顺便介绍一些有意思的Android上的控件比如AutoCompleteTextView和android上的XML解析器。我们最后要实现一个Android APP,当用户输入一部分城市的名字时,就可以显示出所有和用户输入所匹配的城市选项,如下图所示:
Yahoo Woeid
获得天气信息的第一步就是检索Woeid,这是Yahoo提供给开发者的一个特殊的ID,用来分辨城市/地区信息。我们需要根据用户输入的城市名称来获得这个woeid。
从界面的角度上看,我们希望通过用户输入的城市名称或者城市名称的部分,加上对应的woeid来获取与之匹配的城市信息列表,我们可以使用下面的API来获取匹配某个公式的城市列表信息。
http://where.yahooapis.com/v1/places.q(city_name_pattern);count=MAX_RESULT_SIZE?appid=your_app_id
如果你用浏览器来执行这个API,那么你就可以得到一个xml文件,即匹配city_name_pattern这个式子的城市信息列表。
在Android中解析Yahoo的xml数据
现在我们要创建一个XML解析器来处理上一步中我们获取到的数据。首先我们要新建一个数据model(MVC中的模型,也就是javaBean),对于我们这个例子来说,很简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
; html-script: false ] public class CityResult { private String woeid; private String cityName; private String country; public CityResult() {} public CityResult(String woeid, String cityName, String country) { this.woeid = woeid; this.cityName = cityName; this.country = country; } // get and set methods @Override public String toString() { return cityName + "," + country; } } |
然后新建一个名为YahooClient
的类进行解析。这个类主要负责遍历xml数据,然后进行转换。里面有一个静态方法可以接收一个模式(pattern),通过匹配这个模式可以获取城市信息列表。一开始,先要打开一个HTTP连接获取数据流信息,然后把这个数据流信息传给这个XML解析器,如下:
1 2 3 4 5 6 |
; html-script: false ] yahooHttpConn= (HttpURLConnection) (new URL(query)).openConnection(); yahooHttpConn.connect(); XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser(); parser.setInput(new InputStreamReader(yahooHttpConn.getInputStream())); |
接下来可以开始转换数据了。根据我刚才新建的模型类,可以从xml中寻找我们需要的信息,我们需要Woeid、城市的名字和地区信息,xml文件中还有其它不需要关心的信息,我们可以不用理会。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
; html-script: false ] int event = parser.getEventType(); CityResult cty = null; String tagName = null; String currentTag = null; // We start parsing the XML while (event != XmlPullParser.END_DOCUMENT) { tagName = parser.getName(); if (event == XmlPullParser.START_TAG) { if (tagName.equals("place")) { // place Tag Found so we create a new CityResult cty = new CityResult(); Log.d("Swa", "New City found"); } currentTag = tagName; Log.d("Swa", "Tag ["+tagName+"]"); } else if (event == XmlPullParser.TEXT) { // We found some text. let's see the tagName to know the tag related to the text if ("woeid".equals(currentTag)) cty.setWoeid(parser.getText()); else if ("name".equals(currentTag)) cty.setCityName(parser.getText()); else if ("country".equals(currentTag)) cty.setCountry(parser.getText()); // We don't want to analyze other tag at the moment } else if (event == XmlPullParser.END_TAG) { if ("place".equals(tagName)) result.add(cty); } event = parser.next(); } |
这段代码很简单。第一行获取第一个xml事件,然后开始遍历xml数据文件直到到达文档末尾。在这个方法结束的时候,就可以获取我们想要得到的城市列表数据了。
AutoCompleteTextView和带过滤器的数组适配器
在我们知道怎么利用Yahoo的API从xml文件中获取数据后,我们就要向用户展示这些数据。展示数据有很多中方式,我们使用AutoCompleteTextView
。这个控件在Android文档中是这样定义的: “AutoCompleteTextView是一个可编辑文本视图(View)。当欧诺个户输入时会自动提示符合条件的备选项。提示信息在下拉菜单中显示。用户可以选中其中一项替换当前的编辑框内容。”,正好符合我们的需求。使用这个控件不难,但是使用数组适配器加上过滤操作就有点复杂了。通常来说,使用这个控件时都是使用静态的数据,而现在我们需要从远程服务器检索数据。首先要实现一个自定义的适配器,继承 ArrayAdapter
十分简单,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
; html-script: false ] private class CityAdapter extends ArrayAdapter<CityResult> { private Context ctx; private List<CityResult> cityList = new ArrayList<CityResult>(); public CityAdapter(Context ctx, List<CityResult> cityList) { super(ctx, R.layout.cityresult_layout, cityList); this.cityList = cityList; this.ctx = ctx; } @Override public CityResult getItem(int position) { if (cityList != null) return cityList.get(position); return null; } @Override public int getCount() { if (cityList != null) return cityList.size(); return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { View result = convertView; if (result == null) { LayoutInflater inf = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE); result = inf.inflate(R.layout.cityresult_layout, parent, false); } TextView tv = (TextView) result.findViewById(R.id.txtCityName); tv.setText(cityList.get(position).getCityName() + "," + cityList.get(position).getCountry()); return result; } @Override public long getItemId(int position) { if (cityList != null) return cityList.get(position).hashCode(); return 0; } } |
注意:最重要的是我们要从远程服务器检索数据,然后可以通过实现Filterable
接口来处理我们希望得到的数据,所以代码还需要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |