2013年2月6日 星期三

利用Yahoo天氣API,開發Android天氣查詢App

轉自:
Android-er: Search WOEID and query Yahoo Weather

因為Google停止了iGoogle的服務,也停止了Google Weather Api的服務,因為沒有好用的Google提供的天氣Api只好找尋其他的天氣服務。

首先利用Yahoo提供的API將城市名轉換為WOEID,再利用WOEID取得Yahoo Weather API中所提供的天氣訊息,詳情請參考連結。

不過目前對於web的存取及解析還不太了解,這些等之後有更進一步的研究之後,再來好好回過頭來分析這支程式在寫啥吧

另外改天在把修改後的source code貼上~~~

Search WOEID and query Yahoo Weather

User enter location to be searched for WOEID, then clicked the WOEID to start another activity querying Yahoo for the weather.

And also, the code to read from internet have been moved to AsyncTask to prevent fromNetworkOnMainThreadException.

Main activity, MainActivity.java.
package com.example.androidwoeidweather;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.app.Activity;
import android.content.Intent;

public class MainActivity extends Activity {
 
 //Example for "New York"
 //http://query.yahooapis.com/v1/public/yql?q=select*from geo.places where text="New York"&format=xml
 final String yahooPlaceApisBase = "http://query.yahooapis.com/v1/public/yql?q=select*from%20geo.places%20where%20text=";
 final String yahooapisFormat = "&format=xml";
 String yahooPlaceAPIsQuery;

 EditText place;
 Button search;
 ListView listviewWOEID;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        place = (EditText)findViewById(R.id.place);
        search = (Button)findViewById(R.id.search);
        listviewWOEID = (ListView)findViewById(R.id.woeidlist);

        search.setOnClickListener(searchOnClickListener);
    }
    
    Button.OnClickListener searchOnClickListener
    = new Button.OnClickListener(){

  @Override
  public void onClick(View arg0) {
   if(place.getText().toString().equals("")){
    Toast.makeText(getBaseContext(),
      "Enter place!",
      Toast.LENGTH_LONG).show();
   }else{
    new MyQueryYahooPlaceTask().execute();
   }
  }
    };
    
    private class MyQueryYahooPlaceTask extends AsyncTask<Void, Void, Void>{

     ArrayList<String> l;
     
  @Override
  protected Void doInBackground(Void... arg0) {
   l = QueryYahooPlaceAPIs();
   return null;
  }

  @Override
  protected void onPostExecute(Void result) {
   ArrayAdapter<String> aa = new ArrayAdapter<String>(
     getBaseContext(), android.R.layout.simple_list_item_1, l);
   listviewWOEID.setAdapter(aa);
   
   listviewWOEID.setOnItemClickListener(new OnItemClickListener(){

    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     
     String selWoeid = parent.getItemAtPosition(position).toString();
     
     /*
     Toast.makeText(getApplicationContext(), 
       selWoeid, 
       Toast.LENGTH_LONG).show();
     */
     
     Intent intent = new Intent();
              intent.setClass(MainActivity.this, GetWeather.class);
              Bundle bundle = new Bundle();
              bundle.putCharSequence("SEL_WOEID", selWoeid);
              intent.putExtras(bundle);
              startActivity(intent);
    }});
   
   super.onPostExecute(result);
  }
     
    }
    
    private ArrayList<String> QueryYahooPlaceAPIs(){
     String uriPlace = Uri.encode(place.getText().toString());
     
     yahooPlaceAPIsQuery = yahooPlaceApisBase
       + "%22" + uriPlace + "%22"
       + yahooapisFormat;
     
     String woeidString = QueryYahooWeather(yahooPlaceAPIsQuery);
     Document woeidDoc = convertStringToDocument(woeidString);
     return  parseWOEID(woeidDoc);
    }
    
    private ArrayList<String> parseWOEID(Document srcDoc){
     ArrayList<String> listWOEID = new ArrayList<String>();
     
     NodeList nodeListDescription = srcDoc.getElementsByTagName("woeid");
     if(nodeListDescription.getLength()>=0){
      for(int i=0; i<nodeListDescription.getLength(); i++){
       listWOEID.add(nodeListDescription.item(i).getTextContent()); 
      } 
     }else{
      listWOEID.clear(); 
     }
     
     return listWOEID;
    }
    
    private Document convertStringToDocument(String src){
     Document dest = null;
     
     DocumentBuilderFactory dbFactory =
       DocumentBuilderFactory.newInstance();
     DocumentBuilder parser;
     
     try {
      parser = dbFactory.newDocumentBuilder();
      dest = parser.parse(new ByteArrayInputStream(src.getBytes())); 
     } catch (ParserConfigurationException e1) {
      e1.printStackTrace(); 
     } catch (SAXException e) {
      e.printStackTrace(); 
     } catch (IOException e) {
      e.printStackTrace(); 
     }
     
     return dest; 
    }
    
    private String QueryYahooWeather(String queryString){
     String qResult = "";
     
     HttpClient httpClient = new DefaultHttpClient();
     HttpGet httpGet = new HttpGet(queryString);
     
     try {
      HttpEntity httpEntity = httpClient.execute(httpGet).getEntity();
      
      if (httpEntity != null){
       InputStream inputStream = httpEntity.getContent();
       Reader in = new InputStreamReader(inputStream);
       BufferedReader bufferedreader = new BufferedReader(in);
       StringBuilder stringBuilder = new StringBuilder();
       
       String stringReadLine = null;
       
       while ((stringReadLine = bufferedreader.readLine()) != null) {
        stringBuilder.append(stringReadLine + "\n"); 
       }
       
       qResult = stringBuilder.toString(); 
      } 
     } catch (ClientProtocolException e) {
      e.printStackTrace();; 
     } catch (IOException e) {
      e.printStackTrace(); 
     }
     
     return qResult; 
    }

}


Layout of the main activity, activity_main.xml.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    <EditText
        android:id="@+id/place"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/search"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Search WOEID by place" />
    <ListView
        android:id="@+id/woeidlist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>


The activity to query weather from Yahoo, GetWeather.java.
package com.example.androidwoeidweather;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

public class GetWeather extends Activity {
 
 TextView weather;
 
 class MyWeather{
  
  String description;
  String city;
  String region;
  String country;

  String windChill;
  String windDirection;
  String windSpeed;

  String sunrise;
  String sunset;

  String conditiontext;
  String conditiondate;
  
  public String toString(){
   
   String s;
   
   s = description + " -\n\n" + "city: " + city + "\n"
     + "region: " + region + "\n"
     + "country: " + country + "\n\n"
     + "Wind\n"
     + "chill: " + windChill + "\n"
     + "direction: " + windDirection + "\n"
     + "speed: " + windSpeed + "\n\n"
     + "Sunrise: " + sunrise + "\n"
     + "Sunset: " + sunset + "\n\n"
     + "Condition: " + conditiontext + "\n"
     + conditiondate +"\n";
   
   return s;
  }
 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  // TODO Auto-generated method stub
  super.onCreate(savedInstanceState);
  setContentView(R.layout.layout_weather);
  weather = (TextView)findViewById(R.id.weather);
  
  Bundle bundle = this.getIntent().getExtras();
        String sel_woeid = (String)bundle.getCharSequence("SEL_WOEID");
        
        new MyQueryYahooWeatherTask(sel_woeid).execute();
        
        Toast.makeText(getApplicationContext(), 
          sel_woeid, 
    Toast.LENGTH_LONG).show();
 }
 
 private class MyQueryYahooWeatherTask extends AsyncTask<Void, Void, Void>{

  String woeid;
  String weatherResult;
  String weatherString;
  
  MyQueryYahooWeatherTask(String w){
   woeid = w;
  }
  
  @Override
  protected Void doInBackground(Void... arg0) {
   weatherString = QueryYahooWeather();
   Document weatherDoc = convertStringToDocument(weatherString);
   
   if(weatherDoc != null){
    weatherResult = parseWeather(weatherDoc).toString();
   }else{
    weatherResult = "Cannot convertStringToDocument!";
   }
   
   return null;
  }

  @Override
  protected void onPostExecute(Void result) {
   weather.setText(weatherResult);
   super.onPostExecute(result);
  }
  
  private String QueryYahooWeather(){
   String qResult = "";
     String queryString = "http://weather.yahooapis.com/forecastrss?w=" + woeid;
     
     HttpClient httpClient = new DefaultHttpClient();
     HttpGet httpGet = new HttpGet(queryString);
     
     try {
      HttpEntity httpEntity = httpClient.execute(httpGet).getEntity();
      
      if (httpEntity != null){
       InputStream inputStream = httpEntity.getContent();
       Reader in = new InputStreamReader(inputStream);
       BufferedReader bufferedreader = new BufferedReader(in);
       StringBuilder stringBuilder = new StringBuilder();
       
       String stringReadLine = null;
       
       while ((stringReadLine = bufferedreader.readLine()) != null) {
        stringBuilder.append(stringReadLine + "\n");  
       }
       
       qResult = stringBuilder.toString();  
      }  
     } catch (ClientProtocolException e) {
      e.printStackTrace(); 
     } catch (IOException e) {
      e.printStackTrace(); 
     }
     return qResult;   
  }
  
  private Document convertStringToDocument(String src){
   Document dest = null;
   
   DocumentBuilderFactory dbFactory =
     DocumentBuilderFactory.newInstance();
   DocumentBuilder parser;

   try {
    parser = dbFactory.newDocumentBuilder();
    dest = parser.parse(new ByteArrayInputStream(src.getBytes())); 
   } catch (ParserConfigurationException e1) {
    e1.printStackTrace(); 
   } catch (SAXException e) {
    e.printStackTrace(); 
   } catch (IOException e) {
    e.printStackTrace(); 
   }
   
   return dest; 
  }
  
  private MyWeather parseWeather(Document srcDoc){
   
   MyWeather myWeather = new MyWeather();
   
   //<description>Yahoo! Weather for New York, NY</description>
   NodeList descNodelist = srcDoc.getElementsByTagName("description");
   if(descNodelist != null && descNodelist.getLength() > 0){
    myWeather.description = descNodelist.item(0).getTextContent();
   }else{
    myWeather.description = "EMPTY";
   }

   //<yweather:location city="New York" region="NY" country="United States"/>
   NodeList locationNodeList = srcDoc.getElementsByTagName("yweather:location");
   if(locationNodeList != null && locationNodeList.getLength() > 0){
    Node locationNode = locationNodeList.item(0);
    NamedNodeMap locNamedNodeMap = locationNode.getAttributes();
    
    myWeather.city = locNamedNodeMap.getNamedItem("city").getNodeValue().toString();
    myWeather.region = locNamedNodeMap.getNamedItem("region").getNodeValue().toString();
    myWeather.country = locNamedNodeMap.getNamedItem("country").getNodeValue().toString();
   }else{
    myWeather.city = "EMPTY";
    myWeather.region = "EMPTY";
    myWeather.country = "EMPTY";
   }
   
   //<yweather:wind chill="60" direction="0" speed="0"/>
   NodeList windNodeList = srcDoc.getElementsByTagName("yweather:wind");
   if(windNodeList != null && windNodeList.getLength() > 0){
    Node windNode = windNodeList.item(0);
    NamedNodeMap windNamedNodeMap = windNode.getAttributes();
    
    myWeather.windChill = windNamedNodeMap.getNamedItem("chill").getNodeValue().toString();
    myWeather.windDirection = windNamedNodeMap.getNamedItem("direction").getNodeValue().toString();
    myWeather.windSpeed = windNamedNodeMap.getNamedItem("speed").getNodeValue().toString();
   }else{
    myWeather.windChill = "EMPTY";
    myWeather.windDirection = "EMPTY";
    myWeather.windSpeed = "EMPTY";
   }
   
   //<yweather:astronomy sunrise="6:52 am" sunset="7:10 pm"/>
   NodeList astNodeList = srcDoc.getElementsByTagName("yweather:astronomy");
   if(astNodeList != null && astNodeList.getLength() > 0){
    Node astNode = astNodeList.item(0);
    NamedNodeMap astNamedNodeMap = astNode.getAttributes();
    
    myWeather.sunrise = astNamedNodeMap.getNamedItem("sunrise").getNodeValue().toString();
    myWeather.sunset = astNamedNodeMap.getNamedItem("sunset").getNodeValue().toString();
   }else{
    myWeather.sunrise = "EMPTY";
    myWeather.sunset = "EMPTY";
   }
   
   //<yweather:condition text="Fair" code="33" temp="60" date="Fri, 23 Mar 2012 8:49 pm EDT"/>
   NodeList conditionNodeList = srcDoc.getElementsByTagName("yweather:condition");
   if(conditionNodeList != null && conditionNodeList.getLength() > 0){
    Node conditionNode = conditionNodeList.item(0);
    NamedNodeMap conditionNamedNodeMap = conditionNode.getAttributes();
    
    myWeather.conditiontext = conditionNamedNodeMap.getNamedItem("text").getNodeValue().toString();
    myWeather.conditiondate = conditionNamedNodeMap.getNamedItem("date").getNodeValue().toString();
   }else{
    myWeather.conditiontext = "EMPTY";
    myWeather.conditiondate = "EMPTY";
   }
   
   return myWeather; 
  }
  
 }

}


Layout of GetWeather, layout_weather.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <TextView
            android:id="@+id/weather"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" />
    </ScrollView>

</LinearLayout>


Modify AndroidManifest.xml to add activity ".GetWeather" and permission of "android.permission.INTERNET".
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidwoeidweather"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="15" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".GetWeather">
        </activity>
    </application>

</manifest>


2 則留言: