Thursday, January 31, 2019

Làm ứng dụng thông tin thời tiết

Ta sẽ làm một ứng dụng lấy thông tin thời tiết trên mạng.

Tạo một project với tên bất kỳ, trong file Manifest.xml, thêm quyền internet.
<uses-permission android:name="android.permission.INTERNET" />
Thêm vào khai báo của class Main.
android:screenOrientation="portrait"    android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar"
Sửa minSdk thành.
android:minSdkVersion="11"
Trong file strings.xml của thẻ values, thêm đoạn sau vào.
<string name="change_city">Change city</string>

    <!-- Put your own APP ID here -->
    <string name="open_weather_maps_app_id">abaf05e51e3c176324d5c6b5f0696e46</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> 
Muốn dùng API key khác bạn vào trang này để đăng ký rồi tạo API key.
Trong file main.xml của thẻ main, thay đoạn sau vào.
<item
        android:id="@+id/change_city"
        android:orderInCategory="1"
        android:title="@string/change_city"
        app:showAsAction="never"/>
Trong folder layout, tạo một file fragment_weather.xml như sau
<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:textColor="#ffffff"
        />

    <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:textColor="#ffffff"
        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_below="@+id/current_temperature_field"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:textColor="#ffffff"
        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_below="@+id/updated_field"
        android:layout_centerHorizontal="true"
        android:textColor="#ffffff"
        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_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:textColor="#ffffff"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>
File activity_main.xml như sau.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="260dp"
    android:layout_height="300dp"
    tools:context="ah.ha.simpleweather.WeatherActivity"
    tools:ignore="MergeRootFrame"
    android:background="#FF0099CC" />
Tải font thời tiết về tại đây.
Ấn nút Clone or download để tải, lấy file weathericons-regular-webfont.ttf ở trong font cho vào thư mục fonts trong thư mục assets, sửa tên thành weather.ttf.
Tạo một class có tên CityPreference.
import android.app.Activity;
import android.content.SharedPreferences;
public class CityPreference {
     SharedPreferences prefs;
   
    public CityPreference(Activity activity){
        prefs = activity.getPreferences(Activity.MODE_PRIVATE);
    }
    String getCity(){
        return prefs.getString("city", "Hanoi, VN");       
    }
    
    void setCity(String city){
        prefs.edit().putString("city", city).commit();
    }
    
}
Tạo một class có tên RemoteFetch.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.json.JSONObject;

import android.content.Context;
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, String city){
        try {
            URL url = new URL(String.format(OPEN_WEATHER_MAP_API, city));          
            HttpURLConnection connection =
                    (HttpURLConnection)url.openConnection();
            
            connection.addRequestProperty("x-api-key",
                    context.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;
        }
    }  
}
Tạo một class có tên WeatherFragment.
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import org.json.JSONObject;
import android.app.Fragment;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class WeatherFragment extends Fragment {
    Typeface weatherFont;
    TextView cityField;
    TextView updatedField;
    TextView detailsField;
    TextView currentTemperatureField;
    TextView weatherIcon;
    
    Handler handler;

    public WeatherFragment(){  
        handler = new Handler();
    }
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); 
        weatherFont = Typeface.createFromAsset(getActivity().getAssets(), "fonts/weather.ttf");    
        updateWeatherData(new CityPreference(getActivity()).getCity());
       
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = 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;
    }
    private void updateWeatherData(final String city){
        new Thread(){
            public void run(){
                final JSONObject json = RemoteFetch.getJSON(getActivity(), city);
                if(json == null){
                    handler.post(new Runnable(){
                        public void run(){
                  detailsField.setText( "No internet connection!");
                         
                        }
                    });
                } else {
                    handler.post(new Runnable(){
                        public void run(){
                            renderWeather(json);
                        }
                    });
                }              
            }
        }.start();
    }
    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.getInt("id"),
                    json.getJSONObject("sys").getLong("sunrise") * 1000,
                    json.getJSONObject("sys").getLong("sunset") * 1000);
            
        }catch(Exception e){
            Log.e("SimpleWeather", "One or more fields not found in the JSON data");
        }
    }
    private void setWeatherIcon(int actualId, long sunrise, long sunset){
        int id = actualId / 100;
        String icon = "";
        if(actualId == 800){
            long currentTime = new Date().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);
    }
     public void changeCity(String city) {
          updateWeatherData(city);
     }
}
Class MainActivity như sau.
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.InputType;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
public class MainActivity extends Activity {

     @Override
     protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          if (savedInstanceState == null) {
             getFragmentManager().beginTransaction()
                     .add(R.id.container, new WeatherFragment())
                     .commit();
         }
     }

     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.main, menu);
          return true;
     }

     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
          int id = item.getItemId();
          if (id == R.id.change_city) {
                showInputDialog();
               return false;
          }
          return super.onOptionsItemSelected(item);
     }
     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.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 changeCity(input.getText().toString());
             }
         });
         builder.show();
     }
     public void changeCity(String city){
         WeatherFragment wf = (WeatherFragment)getFragmentManager()
                                .findFragmentById(R.id.container);
          wf.changeCity(city);
         new CityPreference(this).setCity(city);
     }
}
Chạy thử để thấy ứng dụng hoạt động nếu có kết nối mạng.

Ấn nút Menu, Change city để đổi thành phố. Nhập vào dalat để có thời tiết của thành phố Đà Lạt.

2 comments:

  1. If you're attempting to lose pounds then you need to try this totally brand new tailor-made keto plan.

    To create this keto diet, licensed nutritionists, fitness trainers, and top chefs have joined together to develop keto meal plans that are effective, painless, cost-efficient, and enjoyable.

    Since their launch in January 2019, thousands of individuals have already remodeled their body and health with the benefits a certified keto plan can provide.

    Speaking of benefits: clicking this link, you'll discover eight scientifically-certified ones given by the keto plan.

    ReplyDelete
  2. Easy "water hack" burns 2 lbs OVERNIGHT

    More than 160 thousand women and men are losing weight with a easy and secret "liquid hack" to burn 1-2lbs each night in their sleep.

    It is very easy and works on anybody.

    This is how you can do it yourself:

    1) Take a glass and fill it with water half the way

    2) Then learn this proven hack

    so you'll become 1-2lbs lighter the very next day!

    ReplyDelete