这篇文章是我们探索Yahoo Weather API的系列文章的第一篇。 我们的目标是构建一个应用程序,使用yahoo天气提供商为我们提供实际的天气状况。 在其他两个帖子中,我们已经讨论了其他天气提供商,例如openweathermap 。 如果您有兴趣,可以在这里和这里看看。
在第一篇文章中,我们想分析如何使用yahoo api检索城市信息。 我们将假设您已经拥有yahoo开发者帐户,如果没有,您可以使用此链接创建它。 拥有免费的appid非常重要,但这完全免费,但是必须使用yahoo api。 在探索yahoo api的同时,我们有机会描述一些有趣的Android UI组件,例如AutoCompleteTextView和Android中的XML解析。 我们的目标是拥有一个Android应用,该应用现在显示与用户输入的部分名称相匹配的城市列表,如下所示:
雅虎·沃伊德
获取天气信息的第一步是检索woeid 。 yahoo用来标识城市/地区的空间ID。 然后,我们必须找到一种方法来从用户输入的城市名称中获取此错误信息。
从用户界面的角度来看,我们希望用户仅输入城市名称或部分名称,并且我们必须找到一种方法来获取与用户输入的数据和相应的woeid相匹配的城市列表:下面显示的api获取与我们输入的模式匹配的城市列表:
<a href="http://where.yahooapis.com/v1/places.q(city_name_pattern);count=MAX_RESULT_SIZE?appid=your_app_id">http://where.yahooapis.com/v1/places.q(city_name_pattern);count=MAX_RESULT_SIZE?appid=your_app_id</a>
如果在浏览器中使用此api,您将获得一个XML文件,其中包含与city_name_pattern匹配的城市的信息列表。
Android Yahoo XML数据解析器
现在是时候创建一个XML解析器了,以便我们可以从使用上述api时获得的数据中提取信息。 第一步是创建一个保存城市信息的数据模型,在我们的例子中,这很简单:
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数据并进行解析。 它有一个简单的静态方法,可以接受我们用来获取城市列表的模式。 首先,我们打开一个HTTP连接,并将流传递给XML解析器:
yahooHttpConn= (HttpURLConnection) (new URL(query)).openConnection();
yahooHttpConn.connect();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(new InputStreamReader(yahooHttpConn.getInputStream()));
然后,我们开始解析数据,寻找我们感兴趣的标签。 到目前为止,也考虑我们的数据模型,我们只对woeid , 城市名称和国家/地区感兴趣。 XML中还有其他信息,但是我们现在不希望提取它们。
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();
}
下面的代码非常简单,在第1行,我们仅获得第一个XML事件,然后开始遍历XML文档,直到到达结尾为止。 在方法的最后,我们有一份清单,其中列出了我们一直在寻找的信息。
AutoCompleteTextView和带有过滤器的ArrayAdapter
一旦我们知道了如何从XML中获取数据,就必须向用户显示使用yahoo api获得的项目列表。 有几种方法可以实现此目标,我们将使用AutoCompleteTextView。 该组件在Android文档中定义为“ 一个可编辑的文本视图,该视图在用户键入时自动显示完成建议。 建议列表显示在一个下拉菜单中,用户可以从中选择一个项目以用。代替编辑框的内容 。” 满足我们的需求! 使用这个组件很简单,但是使用数组适配器以及我们如何过滤结果则稍微复杂一些。 通常,此组件与静态项目列表一起使用,在本例中,我们必须从远程服务器中检索它。 首先,我们实现一个扩展ArrayAdapter的自定义适配器,这非常简单:
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接口,则可以得到我们想要的结果。 因此,我们有:
private class CityAdapter extends ArrayAdapter<CityResult> implements Filterable {
....
@Override
public Filter getFilter() {
Filter cityFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if (constraint == null || constraint.length() < 2)
return results;
List<CityResult> cityResultList = YahooClient.getCityList(constraint.toString());
results.values = cityResultList;
results.count = cityResultList.size();
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
cityList = (List) results.values;
notifyDataSetChanged();
}
};
return cityFilter;
}
..
}
在第4行,我们实现了Filter,它还有另外两种必须实现的方法。 在performFiltering
方法中,我们进行HTTP调用并检索数据(第12行)。 显然我们可能遇到ANR问题,并且我们很清楚我们不应该在主线程中进行HTTP调用。 但是,如果您阅读有关performFiltering的文档,您会发现此方法在单独的线程中运行,因此我们不会有任何问题。
最后,我们可以在UI组件中设置适配器,即使用户单击某个项目也可以处理适配器:
AutoCompleteTextView edt = (AutoCompleteTextView) rootView.findViewById(R.id.edtCity);
CityAdapter adpt = new CityAdapter(this.getActivity(), null);
edt.setAdapter(adpt);
edt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// We handle the onclick event and select the city chosen by the user
}
});
在下一篇文章中,我们将使用woied检索天气信息,敬请期待!
- 源代码即将推出