What you learn: You will learn how to access the Google Weather API an parse the XML-Result using a SAXParser.
:idea: 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 :roll:). This once again shows the power and flexibility of the Android-Platform :!:
:idea: Questions/Problems: Simply post below...
What it will look like:
Screenshot taken with SDK-version m3:
 example_celsius.jpgexample_fahrenheit.jpg  
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. :larrow:
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:
whole_layout.png  
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. 
single_weather_view.png  
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).

arrow
arrow
    全站熱搜

    戮克 發表在 痞客邦 留言(0) 人氣()