Tutorials

Tutorials about HTML, CSS, PHP, Javascript, and Photoshop

  • Home
    Home This is where you can find all the blog posts throughout the site.
  • Categories
    Categories Displays a list of categories from this blog.
  • Tags
    Tags Displays a list of tags that has been used in the blog.
  • Archives
    Archives Contains a list of blog posts that were created previously.
  • Login

Create a Weather App on Android

by in Photoshop
  • Font size: Larger Smaller
  • Hits: 7188
  • 0 Comments
  • Subscribe to this entry
  • Print
7188
Final product image
What You'll Be Creating

Or include features that most of us never use, A lot of the popular weather apps on Google Play are either full of ads, require too many permissions. Wouldn't it be great if you could build your own weather app from scratch

In this tutorial,  I'm going to show you how. Showing the user exactly what they need to know about the current weather conditions, Our app will have a simple and minimalist user interface. Let's get started

1. Prerequisites

Before you continue, double-check that you have the following set up:

  • Eclipse ADT Bundle: You can download it at the Android Developer website
  • OpenWeatherMap API Key But it's free, : This isn't required to complete the tutorial. You can obtain one by signing up at theOpenWeatherMap website
  • Icons: I recommend you download the weather icons font created byErik FlowersYou need to download theTTFBecause we'll be using it in a native app, file. We'll use the font to render various icons depending on the weather conditions

2. Create a New Project

I'm going to call this appSimpleWeatherBut feel free to give it any name you like. Set the minimum required SDK to, Enter a unique package nameAndroid 2. 2And set the target SDK toAndroid 4. 4You can leave the theme at Holo Dark

Create a new Android Application

This app will only have one Activity and it will be based on the Blank Activity Template as shown below

Create a new activity

Name theActivity WeatherActivityWe'll be using aFragmentInside thisActivityThe layout associated with theActivityIsActivity_weather. XmlThe layout associated with theFragmentIsFragment_weather. Xml

The Activity details screen

3. Add the Custom Font

CopyWeathericons-regular-webfont. TtfTo your project'sAssets/fontsDirectory and rename it toWeather. Ttf

4. Edit the Manifest

The only permission this app needs isAndroid. Permission. INTERNET

<uses-permission android:name="android.permission.INTERNET"/>

We're only going to support, To keep this tutorial simplePortraitMode. The Activity node of the manifest should look like this:

<activity
    android:name="ah.hathi.simpleweather.WeatherActivity"
    android:label="@string/app_name"
    android:screenOrientation="portrait"
    >
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

5. Edit the Activity's Layout

There isn't much to change inActivity_weather. Xml It should already have aFrameLayoutAdd an extra property to change the color of theBackground to#FF0099CC

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ah.hathi.simpleweather.WeatherActivity"
    tools:ignore="MergeRootFrame"
    android:background="#FF0099CC" />

6. Edit the Fragment's Layout

EditFragment_weather. Xml by adding five TextViewTags to show the following information:

  • City and country
  • Current temperature
  • An icon showing the current weather condition
  • A timestamp telling the user when the weather information was last updated
  • More detailed information about the current weather, such as description and humidity

Use aRelativeLayoutTo arrange the text views. You can adjust theTextSizeTo suit various devices

<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="ah.hathi.simpleweather.WeatherActivity$PlaceholderFragment" >

    <TextView
        android:id="@+id/city_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/updated_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/city_field"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textSize="13sp" />

    <TextView
        android:id="@+id/weather_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"        
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="70sp"
        />

    <TextView
        android:id="@+id/current_temperature_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="40sp" />

    <TextView 
        android:id="@+id/details_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/weather_icon"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        />        

</RelativeLayout>

7. Edit strings.xml

This file contains the strings used in our app as well as the Unicode character codes that we'll use to render the weather icons. The application will be able to display eight different types of weather conditions. Then refer to, If you want to handle morethis cheat sheetAdd the following toValues/strings. Xml:

<. Xml version="1.0" encoding="utf-8". >
<resources>

    <string name="app_name">Simple Weather</string>
    <string name="change_city">Change city</string>

    <. -- Put your own APP ID here -->
    <string name="open_weather_maps_app_id">11111</string>
    
    <string name="weather_sunny">&#xf00d;</string>
    <string name="weather_clear_night">&#xf02e;</string>
    
    <string name="weather_foggy">&#xf014;</string>
    <string name="weather_cloudy">&#xf013;</string>
    <string name="weather_rainy">&#xf019;</string>
    <string name="weather_snowy">&#xf01b;</string>
    <string name="weather_thunder">&#xf01e;</string>
    <string name="weather_drizzle">&#xf01c;</string>
    
    <string name="place_not_found">Sorry, no weather data found. </string>    
    
</resources>

8. Add a Menu Item

The user should be able to choose the city whose weather they want to see. EditMenu/weather. XmlAnd add an item for this option

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="ah.hathi.simpleweather.WeatherActivity" >

    <item
        android:id="@+id/change_city"
        android:orderInCategory="1"
        android:title="@string/change_city"
        app:showAsAction="never"/>

</menu>

Now that all the XML files are ready to use, let's move on and query the OpenWeatherMap API to fetch weather data

9. Fetch Data From OpenWeatherMap

We can get the current weather details of any city formatted as JSON using the OpenWeatherMap API. We pass the city's name and the metric system the results should be in, In the query string

 we send a request to , For example, to get the current weather information for Canberra, using the metric systemhttp://api.openweathermap.org/data/2.5/weather?q=Canberra&units=metric

The response we get back from the API looks like this:

{
    "base": "cmc stations", "clouds": {
        "all": 90
    }"cod": 200, "coord": {
        "lat": -35. 28, "lon": 149. 13
    }"temp_max": -1, "temp_min": -1, "pressure": 1023, "id": 2172517, "main": {
        "humidity": 100, "dt": 1404390600, "temp": -1
    }"name": "Canberra", "message": 0, "sys": {
        "country": "AU". "sunset": 1404370965, "sunrise": 1404335563, 313
    }"weather": [
        {
            "description": "overcast clouds", "id": 804, "main": "Clouds", "icon": "04n"
        }], "wind": {
        "deg": 305. 004, "speed": 1. 07
    }
}

Create a new Java class and name itRemoteFetch. JavaThis class is responsible for fetching the weather data from the OpenWeatherMap API

We use the HttpURLConnection class to make the remote request. The OpenWeatherMap API expects the API key in an HTTP header namedX-api-keyThis is specified in our request using theSetRequestPropertyMethod

We use aBufferedReaderTo read the API's response into aStringBufferWhen we have the complete response, we convert it to aJSONObject object

 the JSON data contains a field named , As you can see in the above responseCodIts value is200 if the request was successful. We use this value to check whether the JSON response has the current weather information or not

TheRemoteFetch. JavaClass should look like this:

Package ah. Hathi. Simpleweather;

import java. Io. BufferedReader;
import java. Io. InputStreamReader;
import java. Net. HttpURLConnection;
import java. Net. URL;

import org. Json. JSONObject;

import android. Content. Context;
import android. Util. String city){
		try {
			URL url = new URL(String, Log;

public class RemoteFetch {

    private static final String OPEN_WEATHER_MAP_API = 
			"http://api.openweathermap.org/data/2.5/weather?q=%s&units=metric";
	
	public static JSONObject getJSON(Context context. Format(OPEN_WEATHER_MAP_API, city));			
			HttpURLConnection connection = 
					(HttpURLConnection)url. OpenConnection();
			
			connection. Context, addRequestProperty("x-api-key". GetString(R. String. Open_weather_maps_app_id));
			
			BufferedReader reader = new BufferedReader(
					new InputStreamReader(connection. GetInputStream()));
			
			StringBuffer json = new StringBuffer(1024);
			String tmp="";
			while((tmp=reader. ReadLine()). =null)
				json. Append(tmp). Append("n");
			reader. Close();
			
			JSONObject data = new JSONObject(json. ToString());
			
			// This value will be 404 if the request was not
			// successful
			if(data. GetInt("cod"). = 200){
				return null;
			}Return data;
		}Catch(Exception e){
			return null;
		}
	}	
}

10. Store the City as a Preference

The user shouldn't have to specify the name of the city every time they want to use the app. The app should remember the last city the user was interested in. We do this by making use ofSharedPreferencesInstead of directly accessing these preferences from our, HoweverActivity it is better to create a separate class for this purpose,  class

Create a new Java class and name itCityPreference. JavaCreate two methods, To store and retrieve the name of the citySetCity andGetCityTheSharedPreferencesObject is initialized in the constructor. The CityPreference. Java class should look like this:

Package ah. Hathi. Simpleweather;

import android. App. Activity;
import android. Content. SharedPreferences;

public class CityPreference {
    
	SharedPreferences prefs;
	
	public CityPreference(Activity activity){
		prefs = activity. GetPreferences(Activity. MODE_PRIVATE);
	}// If the user has not chosen a city yet, return
    // Sydney as the default city
	String getCity(){
		return prefs. GetString("city", "Sydney, AU");        
	}Void setCity(String city){
		prefs. Edit(). PutString("city", city). Commit();
	}
	
}

11. Create the Fragment

Create a new Java class and name itWeatherFragment. JavaThis fragment usesFragment_weather. XmlAs its layout. Declare the fiveTextView objects and initialize them in theOnCreateView method. Declare a newTypefaceObject namedWeatherFontTheTypeFaceObject will point to the web font you downloaded and stored in theAssets/fontsFolder

We will be making use of a separateThreadTo asynchronously fetch data from the OpenWeatherMap API. We cannot update the user interface from such a background thread. We therefore need aHandlerObject, which we initialize in the constructor of the WeatherFragment class

Public class WeatherFragment extends Fragment {
    Typeface weatherFont;
	
	TextView cityField;
	TextView updatedField;
	TextView detailsField;
	TextView currentTemperatureField;
	TextView weatherIcon;
	
	Handler handler;

	public WeatherFragment(){	
		handler = new Handler();
	}ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater, @Override
    public View onCreateView(LayoutInflater inflater. Inflate(R. Layout. Fragment_weather, container, false);
        cityField = (TextView)rootView. FindViewById(R. Id. City_field);
        updatedField = (TextView)rootView. FindViewById(R. Id. Updated_field);
        detailsField = (TextView)rootView. FindViewById(R. Id. Details_field);
        currentTemperatureField = (TextView)rootView. FindViewById(R. Id. Current_temperature_field);
        weatherIcon = (TextView)rootView. FindViewById(R. Id. Weather_icon);
        
        weatherIcon. SetTypeface(weatherFont);
        return rootView; 
    }
}

Initialize the WeatherFont object by calling CreateFromAsset on the TypefaceClass. We also invoke the UpdateWeatherData method inOnCreate

@Override
public void onCreate(Bundle savedInstanceState) {
    super. OnCreate(savedInstanceState);  
	weatherFont = Typeface. CreateFromAsset(getActivity(). GetAssets(), "fonts/weather.ttf");    	
	updateWeatherData(new CityPreference(getActivity()). GetCity());
}

InUpdateWeatherDataWe start a new thread and call GetJSON on theRemoteFetchClass. If the value returned byGetJSON isNullWe display an error message to the user. We invoke the ,  If it isn'tRenderWeather method

Only the mainThreadIs allowed to update the user interface of an Android app. CallingToastOrRenderWeatherDirectly from the background thread would lead to a runtime error. That is why we call these methods using theHandler'sPostMethod

Private void updateWeatherData(final String city){
    new Thread(){
		public void run(){
			final JSONObject json = RemoteFetch. City);
			if(json == null){
				handler, getJSON(getActivity(). Post(new Runnable(){
					public void run(){
						Toast. GetActivity(), makeText(getActivity(). GetString(R. String. Place_not_found), Toast. LENGTH_LONG). Show(); 
					}
				});
			}Else {
				handler. Post(new Runnable(){
					public void run(){
						renderWeather(json);
					}
				});
			}    			
		}
	}Start();
}

TheRenderWeather method uses the JSON data to update theTextView objects. TheWeatherNode of the JSON response is an array of data. In this tutorial, we will only be using the first element of the array of weather data

Private void renderWeather(JSONObject json){
    try {
    	cityField. SetText(json. GetString("name"). ToUpperCase(Locale. US) + 
    			", " + 
    			json. GetJSONObject("sys").getString("country"));
    	
    	JSONObject details = json. GetJSONArray("weather"). GetJSONObject(0);
    	JSONObject main = json. GetJSONObject("main");
    	detailsField. SetText(
    			details. GetString("description"). ToUpperCase(Locale. US) +
    			"n" + "Humidity: " + main.getString("humidity") + "%" +
    			"n" + "Pressure: " + main.getString("pressure") + " hPa");
    	
    	currentTemperatureField. SetText(
    				String. Format("%.2f", main.getDouble("temp"))+ " ℃");

    	DateFormat df = DateFormat. GetDateTimeInstance();
    	String updatedOn = df. Format(new Date(json. GetLong("dt")*1000));
    	updatedField. SetText("Last update: " + updatedOn);

    	setWeatherIcon(details. Json, getInt("id"). Json, getJSONObject("sys").getLong("sunrise") * 1000. GetJSONObject("sys").getLong("sunset") * 1000);
    	
	}Catch(Exception e){
		Log. E("SimpleWeather", "One or more fields not found in the JSON data");
	}
}

At the end of theRenderWeather we invoke ,  methodSetWeatherIcon with theIdOf the current weather as well as the times of sunrise and sunset. Because the OpenWeatherMap API supports more weather conditions than we can support with the web font we're using, Setting the weather icon is a bit tricky. Which you can read more about on the, the weather ids follow a pattern, FortunatelyOpenWeatherMap website

This is how we map a weather id to an icon:

  • The weather codes in the 200 range are related to thunderstorms, which means we can use R. String. Weather_thunderFor these
  • The weather codes in the 300 range are related to drizzles and we use R. String. Weather_drizzleFor these
  • The weather codes in the 500 range signify rain and we use R. String. Weather_rain for them
  • And so on

Depending on the current time of the day and only if the weather is clear, We use the sunrise and sunset times to display the sun or the moon

Private void setWeatherIcon(int actualId, long sunset){
    int id = actualId / 100;
	String icon = "";
	if(actualId == 800){
		long currentTime = new Date(), long sunrise. GetTime();
		if(currentTime>=sunrise && currentTime<sunset) {
			icon = getActivity(). GetString(R. String. Weather_sunny);
		}Else {
			icon = getActivity(). GetString(R. String. Weather_clear_night);
		}
	}Else {
    	switch(id) {
    	case 2 : icon = getActivity(). GetString(R. String. Weather_thunder);
		 		 break;     	
    	case 3 : icon = getActivity(). GetString(R. String. Weather_drizzle);
		 		 break;    	
    	case 7 : icon = getActivity(). GetString(R. String. Weather_foggy);
    			 break;
    	case 8 : icon = getActivity(). GetString(R. String. Weather_cloudy);
		 		 break;
    	case 6 : icon = getActivity(). GetString(R. String. Weather_snowy);
		 		 break;
    	case 5 : icon = getActivity(). GetString(R. String. Weather_rainy);
    			 break;
    	}
	}WeatherIcon. SetText(icon);
}

Of course, you can handle more weather conditions by adding moreCase statements to theSwitchStatement of theSetWeatherIcon method

Finally, add aChangeCity method to the fragment to let the user update the current city. TheChangeCity method will only be called from the main Activity class

Public void changeCity(String city){
    updateWeatherData(city);
}

12. Edit the Activity

During the project's setup,  Eclipse populatedWeatherActivity. Java with some boilerplate code. Replace the default implementation of theOnCreateMethod with the one below in which we use the WeatherFragmentThe OnCreate method should look like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super. OnCreate(savedInstanceState);
    setContentView(R. Layout. Activity_weather);

    if (savedInstanceState == null) {
        getSupportFragmentManager(). BeginTransaction(). Add(R. Id. New WeatherFragment()), container. Commit();
    }
}

Edit the, NextOnOptionsItemSelected method and handle the only menu option we have. All you have to do here is invoke the ShowInputDialog method

In the ShowInputDialog method,  we useAlertDialog. BuilderTo create aDialog object that prompts the user to enter the name of a city. This information is passed on to theChangeCity method, which stores the name of the city using the CityPreference class and calls theFragment'sChangeCity method

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if(item. GetItemId() == R. Id. Change_city){
		showInputDialog();
	}Return false;
}Private void showInputDialog(){
	AlertDialog. Builder builder = new AlertDialog. Builder(this);
	builder. SetTitle("Change city");
	final EditText input = new EditText(this);
	input. SetInputType(InputType. TYPE_CLASS_TEXT);
	builder. SetView(input);
	builder. SetPositiveButton("Go", new DialogInterface. Int which) {
			changeCity(input, OnClickListener() {
		@Override
		public void onClick(DialogInterface dialog. GetText(). ToString());
		}
	});
	builder. Show();
}Public void changeCity(String city){
	WeatherFragment wf = (WeatherFragment)getSupportFragmentManager(). FindFragmentById(R. Id. Container);
	wf. ChangeCity(city);
	new CityPreference(this). SetCity(city);
}

Your weather app is now ready. Build the project and deploy it to an Android device for testing

The app running on a tablet

Conclusion

You now have a fully functional weather application. Feel free to explore theOpenWeatherMap APITo further enhance your application. You may also want to make use of moreweather iconsBecause we are currently using only a small subset of them

Read more: Create a Weather App on Android

0
Trackback URL for this blog entry.

Comments

  • No comments made yet. Be the first to submit a comment

Leave your comment

Guest Wednesday, 08 July 2020

Testimonial

Thank you so much! We are very happy with our new website. It is easy to use and all of our customers tell us, they love it.

Contact Us

  • 13245 Atlantic Blvd. #4352
    Jacksonville, FL 32225
  • 904-240-5823