Android Core

Android Weather app using Yahoo weather provider and AutoCompleteTextView (Part 1)

This post is the first of a series where we will explore Yahoo Weather API. Our goal is building an app that gives us the actual weather condition using yahoo weather provider. We already talked about other weather provider like openweathermap, in other two post. You can give a look here and here if you are interested.

In this first post, we want to analyze how we can retrieve city information using yahoo api. We will assume you have already an yahoo developer account, if not you can create it using this link. It is important you have your appid, that is totally free but it is necessary to use yahoo api. While we will explore yahoo api, we have the chance to describe some interesting Android UI component like AutoCompleteTextView and XML parsing in Android. Our goal is having an Android app that is shows by now the list of cities that matches a partial name entered by the user as shown below:

android_yahoo_weather_autocomplete[4]

Yahoo Woeid

The first step to get weather information is retrieving woeid. This a spatial id that yahoo uses to identify city/region. We have, then, to find a way to get this woeid from the city name entered by the user.

From the UI perspective, we want that an user inserts just the name of the city or a partial name and we have to find a way to get a list of cities that matches the data entered by the user with the corresponding woeid:  we can use the api shown below to get a list of cities matching a pattern we entered:

<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>

If you use this api in your browser you will get an XML file with a list of information about cities that match the city_name_pattern.

Android Yahoo XML data Parser

It is time we create an XML parser so that we can extract information from the data we get when using the api shown above. The first step is creating a data model that holds the city information, in our case it is quite simple:

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;
    }
}

Now we create a parser class that we call YahooClient. This class is in charge of retrieving XML data and parse it. It has a simple static method that accepts the pattern we have to use to get the city list. At the beginning, we open an HTTP connection and pass the stream to the XML parser:

yahooHttpConn= (HttpURLConnection) (new URL(query)).openConnection();
yahooHttpConn.connect();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(new InputStreamReader(yahooHttpConn.getInputStream()));

then, we start parsing the data, looking for some tags we are interested on. By now, considering our data model too, we are just interested on the woeid, city name and country. There are other information in the XML but we don’t want to extract them by now.

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();
}

The code below is very simple, at line 1 we simply get the first XML event and we start traversing the XML document until we reach the end.  At the method end, we have a list of cities with the woied that is the information we were looking for.

AutoCompleteTextView and ArrayAdapter with Filter

Once we know how to get data from the XML we have to show to the user a list of items we get using yahoo api. There are several way to get this goal, we will use AutoCompleteTextView. This component is defined in Android doc as “ An editable text view that shows completion suggestions automatically while the user is typing. The list of suggestions is displayed in a drop down menu from which the user can choose an item to replace the content of the edit box with.”. It satisfies our needs! Using this component is trivial but a little more complex is using the array adapter and how we filter the result. Usually this component is used with a static item list, in our case we have to retrieve it from a remote server. First we implement a custom adapter that extends the ArrayAdapter and this is very simple:

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;
}
...
}

Much more interesting is how we can retrieve the data from a remote server. If we implement the Filterable interface in our custom adapter we can get the result we are looking for. So we have:

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;
        }
..
}

At line 4, we implement our Filter, that has two other methods we have to implement. In performFiltering method we make the HTTP call and retrieve the data (line 12). Apparently we could have ANR problem, and we know very well we shouldn’t do an HTTP call in the main thread. But if you read the documentation about performFiltering you will find out that this method runs in a separate thread so we won’t have any problem.

Finally, we can set the adapter in our UI component and handle the even when user clicks on an item:

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
   }
});

In the next post we will retrieve weather information using the woied, so stay tuned!

  • Source code available soon

 

Francesco Azzola

He's a senior software engineer with more than 15 yrs old experience in JEE architecture. He's SCEA certified (Sun Certified Enterprise Architect), SCWCD, SCJP. He is an android enthusiast and he has worked for long time in the mobile development field.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Joyce
Joyce
10 years ago

Hi,

I am wondering what “txtCityName” mean as a id ? As I am totally fresh to android and there is no detail xml file of this project.

Thank you so much

Francesco Azzola
10 years ago

In the CityAdapter at line 38 you find:

TextView tv = (TextView) result.findViewById(R.id.txtCityName);

where R.id.txtCityName is the name of the TextView in the layout.
In the original post you can find the source code:

http://www.survivingwithandroid.com/2014/02/android-weather-app-using-yahoo-provider.html

Francesco Azzola
10 years ago

If you want to go directly to the source code:

https://github.com/survivingwithandroid/Swa-app/tree/master/AndroidYahooWeather

or you can if you are interested on the ready-to-use lib you can go here:

https://github.com/survivingwithandroid/WeatherLib

Jagjit
Jagjit
10 years ago

There is a very simple link that i have found about using xml parsing in android i have used it and it also gives demo files to download contains good explanation of code an awesome link that made my day

http://geeksprogrammings.blogspot.in/2014/07/xml-parsing-in-android.html

Back to top button