What you learn: You will learn how to access the Google Weather API an parse the XML-Result using a SAXParser.
Designed/Tested with sdk-version: m5-rc14
What this contains:
Access the Google Weather API
Load data from the web :src: Separate/Extended Tutorial
Parse XML Files (Streams) using the SAXParser :src: Separate/Extended Tutorial
Create a Custom View (extends LinearLayout) :src: Separate/Extended Tutorial
...
Difficulty: 3.5 of 5
I did this within 2 hours of coding (half for the xml-layout ). This once again shows the power and flexibility of the Android-Platform
Questions/Problems: Simply post below...
What it will look like:
Screenshot taken with SDK-version m3:
Description:
0.) In this tutorial we are going to use the Google (iGoogle) Weather API. We will simply call an URL with the following style:
// Style:
http://www.google.com/ig/api?weather=QUERY
// Working Examples ( Note: that " "(space) has to be replaced with its html-expression "%20"
// Your browser manages that conversion automatically )
http://www.google.com/ig/api?weather=Sc ... im,Germany
http://www.google.com/ig/api?weather=New%20York,%20USA
The response is always like the following (when the city could be found):
Note: Originally the xml-code responded comes without LineFeeds + I added comments
<xml_api_reply version="1">
<weather module_id="0" tab_id="0">
<forecast_information>
<!-- Some inner tags containing data about the city found, time and unit-stuff -->
<city data="Schriesheim, BW"/>
<postal_code data="Schriesheim,Germany"/>
<latitude_e6 data=""/>
<longitude_e6 data=""/>
<forecast_date data="2007-12-21"/>
<current_date_time data="2007-12-21 19:50:00 +0000"/>
<unit_system data="US"/>
</forecast_information>
<current_conditions>
<!-- Some inner tags containing data of current weather -->
<condition data="Fog"/>
<temp_f data="23"/>
<temp_c data="-5"/>
<humidity data="Humidity: 93%"/>
<icon data="/images/weather/fog.gif"/>
<wind_condition data="Wind: N at 1 mph"/>
</current_conditions>
<forecast_conditions>
<!-- Some inner tags containing data about future weather -->
<day_of_week data="Today"/>
<low data="24"/>
<high data="37"/>
<icon data="/images/weather/fog.gif"/>
<condition data="Fog"/>
</forecast_conditions>
<forecast_conditions>
<!-- Some inner tags containing data about future weather -->
<day_of_week data="Sat"/>
<low data="24"/>
<high data="37"/>
<icon data="/images/weather/sunny.gif"/>
<condition data="Clear"/>
</forecast_conditions>
<forecast_conditions>
<!-- Another set as above -->
</forecast_conditions>
<forecast_conditions>
<!-- Another set as above -->
</forecast_conditions>
</weather>
</xml_api_reply>
If we call the Google Weather API with an not existing town, like:
http://www.google.com/ig/api?weather=FantasyTown,Disneyland
The "error" xml-return-code for such a bad query is like:
<xml_api_reply version="1">
<weather module_id="0" tab_id="0">
<problem_cause data=""/>
</weather>
</xml_api_reply>
1.) So as we need to extract the information out of the XML-code returned, we have to choose a XML-parser. I decided to use an SAX-Parser (Wiki-Info). SAX stands for Simple API for XML, so it is perfect for us
:rarrow: :src: Separate/Extended Tutorial on Parsing XML using a SAXParser.
So the parsing-part follows that Tutorial (one line above). It will parse the XML-Data returned by the Weather API and in the end it "presents" the following Object to us:
public class WeatherSet {
// ===========================================================
// Fields
// ===========================================================
private WeatherCurrentCondition myCurrentCondition = null;
private ArrayList<WeatherForecastCondition> myForecastConditions =
new ArrayList<WeatherForecastCondition>(4);
// ===========================================================
// Getter & Setter
// ===========================================================
// Getter & Setter for fields above...
}
It simply contains two sub-objects: WeatherCurrentCondition which contains the parsed data from the xml-tag <current_conditions> and a list of WeatherForecastInfoSet which contains the parsed data from all the xml-tags <forecast_conditions>.
The parsing part is finished now (see the full source for the actual code).
2.) Lets take a look at the Layout. The layout is a bit more complex as what has been done previously.
This it how it is cascaded:
I'm using so much TableLayouts, because they can easily stretch its child-Views.
The small entities with the Image and the Temperature-TextView are a customized View ("SingleWeatherInfoView"), which extends LinearLayout.
By extending LinearLayout we've got the ability to set the android:orientation-Attribute to ="horizontal" or to ="vertical" You can see that feature in the picture. The lonely "SingleWeatherInfoView" which shows the WeatherCondition of today, was coded with android:orientation="horizontal". You can see that the "inner" Views appear next to each other. The four "SingleWeatherInfoView" were xml-defined with: android:orientation="vertical", you can see that its "inner" Views appear below each other. This once again shows the power of object-orientated programming at its best
How to create a custom View :src: Separate/Extended Tutorial.
The layout part is finished now (see the full source for the actual code).
3.) So 90% of the work is done now. The last thing we have to do is to put all the parts together.
We apply an OnClickListener to the Submit-Button, which will do the following, when clicked:
Call the Google Weather API with what the user typed to the EditText
Create a SAXParser and parse the result of the previous step
Display the parsed data in the five "SingleWeatherInfoView".
And this is the corresponding code:
Button cmd_submit = (Button)findViewById(R.id.cmd_submit);
cmd_submit.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View arg0) {
URL url;
try {
/* Get what user typed to the EditText. */
String cityParamString =
((EditText)findViewById(R.id.edit_input))
.getText().toString();
String queryString =
"http://www.google.com/ig/api?weather="
+ cityParamString;
/* Replace blanks with HTML-Equivalent. */
url = new URL(queryString.replace(" ", "%20"));
/* Get a SAXParser from the SAXPArserFactory. */
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
/* Get the XMLReader of the SAXParser we created. */
XMLReader xr = sp.getXMLReader();
/* Create a new ContentHandler and apply it to the XML-Reader*/
GoogleWeatherHandler gwh = new GoogleWeatherHandler();
xr.setContentHandler(gwh);
/* Parse the xml-data our URL-call returned. */
xr.parse(new InputSource(url.openStream()));
/* Our Handler now provides the parsed weather-data to us. */
WeatherSet ws = gwh.getWeatherSet();
/* Update the SingleWeatherInfoView with the parsed data. */
updateWeatherInfoView(R.id.weather_today, ws.getWeatherCurrentCondition());
updateWeatherInfoView(R.id.weather_1, ws.getWeatherForecastConditions().get(0));
updateWeatherInfoView(R.id.weather_2, ws.getWeatherForecastConditions().get(1));
updateWeatherInfoView(R.id.weather_3, ws.getWeatherForecastConditions().get(2));
updateWeatherInfoView(R.id.weather_4, ws.getWeatherForecastConditions().get(3));
} catch (Exception e) {
resetWeatherInfoViews();
Log.e(DEBUG_TAG, "WeatherQueryError", e);
}
}
});
Then all you have to do is a request to the URL I gave at the begining of my message replacing the lat and lng by their values, process the result with a bit of SAX parsing and there you go!
This may be a bit overkill but I can't think of nothing simpler for you problem (if anyone has something else, I'm interrested).
留言列表