Bài 64: Xây dựng phần mềm dự báo thời tiết


Tui muốn tổng hợp các bài tập về Google Map, về Đa tiến trình, về JSon, Webservice…. để xây dựng phần mềm dự báo thời tiết đơn giản như hình dưới đây:

android_64_0– Phần mềm sẽ có 4 chức năng như mô tả ở trên, để làm được bài này thì các bạn cần phải có các kiến thức sau:

+ Kiến thức về lập trình đa tiến trình:

Bài tập 33: Sử dụng ContentProvider trong Android

Bài tập 34: đa tiến trình trong Android (Multi-Threading)

Bài 35 : Vẽ Button lúc Runtime, dùng Using Message của Handler class

Bài 36: Update ListView At runtime by Handler class using post

Bài 37: Xử lý đa tiến trình bằng AsyncTask

Bài 38: Lấy kết quả trả về sau khi thực hiện đa tiến trình bằng AsyncTask

Bài 39: Kết hợp AsyncTask và Handler class

+ Kiến thức về lập trình Google Map:

Bài 53: Google Maps Android API – phần 1

Bài 54: Google Maps Android API – phần 2

Bài 55: Google Maps Android API – phần 3

Bài 56: Google Maps Android API – phần 4

+Kiến thức về Webservice với định dạng JSON hoặc SOAP (XML):

Bài 43: Android vs .net Web Services

Bài 44: Cách tạo Webservice

Bài 45: Sử dụng .Net Webservice trong C#

Bài 46: Sử dụng .Net Webservice trong Android

Bài 51: Xử lý JSON trong Android

Bài 52: Tạo định dạng JSON trong C# asp.net service vs Android

+Kiến thức về chuyển đổi JSON qua Java class:

Bài 61: Cách đưa định dạng JSon về Java class bằng GSon

Bài 62: Cách đưa định dạng JSon về Java class bằng GSon (tiếp 1)

Bài 63: Cách đưa định dạng JSon về Java class bằng GSon (tiếp 2)

Đồng thời nghiên cứu thêm API Open Weather Map(Hỗ trợ xem thời tiết hầu hết mọi nơi trên thế giới, được đánh giá là một trong những API cũng cấp webservice về dự báo thời tiết tốt nhất hiện nay)

Cập nhật ngày 13/12/2015

Để sử dụng được API này bạn cần đăng nhập để lấy appid, chi tiết đọc tại : http://openweathermap.org/appid

Khi đăng ký và đăng nhập thành công, bạn sẽ có API key bên dưới:

key

http://api.openweathermap.org/data/2.5/forecast/daily?lat=10.778182&lon=106.665504&cnt=1&appid=be8d3e323de722ff78208a7dbb2dcd6f

Ví dụ 0: Xem thời tiết ở Thành Phố Hồ Chí Minh:

http://api.openweathermap.org/data/2.5/weather?q=hồ chí minh&appid=be8d3e323de722ff78208a7dbb2dcd6f

Ví dụ 1: Xem thời tiết ở Đà Lạt

http://api.openweathermap.org/data/2.5/forecast/daily?q=đà lạt&appid=be8d3e323de722ff78208a7dbb2dcd6f

Ví dụ 2: Xem thời tiết ở Đà Lạt sau 15 ngày nữa thì như thế nào:

http://api.openweathermap.org/data/2.5/forecast/daily?q=đà lạt&cnt=15&appid=be8d3e323de722ff78208a7dbb2dcd6f

Ví dụ 3: Xem thời tiết ở 1 Kinh độ và Vĩ độ bất kỳ (ứng dụng hiển thị trên Google Map)

http://api.openweathermap.org/data/2.5/forecast/daily?lat=35&lon=139&cnt=10&mode=json&appid=be8d3e323de722ff78208a7dbb2dcd6f

Chi tiết hướng dẫn sử dụng: http://openweathermap.org/api

Các bạn tranh thủ ôn lại các bài trên sau đó Tui sẽ trình bày chi tiết từng bước cách thực hiện dự án này.

Trước khi bắt tay vào lập trình bài này thì ta xem sơ qua cấu trúc JSON khi kiểm tra thời tiết của một địa điểm nào đó, ví dụ ở Thành Phố Hồ Chí Minh:

http://api.openweathermap.org/data/2.5/weather?q=hồ chí minh&appid=be8d3e323de722ff78208a7dbb2dcd6f

hay dùng kinh độ vĩ độ (để áp dụng cho chức năng đầu tiên là xem thời tiết tại địa điểm hiện tại, theo địa chỉ bất kỳ, hay xem trên Google Map):

http://api.openweathermap.org/data/2.5/weather?lat=10.778182&lon=106.665504&appid=be8d3e323de722ff78208a7dbb2dcd6f

Ta được kết quả tương tự như sau:

android_64_1

Các source code bạn tự nối đuôi appid mà Tui đã chỉ ở trên là có thể chạy tốt.

End update 13/12/2015

—————————————–

Việc phân tích cấu trúc JSON để viết Java class các bạn đã được học kỹ ở các bài trước rồi, nên bài này Tui không nói nữa mà Tui chỉ show Mô hình Java class đã viết ra như sau (Bạn tự viết):

android_64_2– Ta sẽ dùng GSon để chuyển JSON qua Java class, từ đó dễ dàng cho việc sử dụng lấy thông tin dự báo thời tiết.

– Còn dưới đây là cấu trúc JSON cho trường hợp dự báo các ngày khác (Daily):

Ví dụ:

http://api.openweathermap.org/data/2.5/forecast/daily?lat=10.778182&lon=106.665504&cnt=10

Hay:

http://api.openweathermap.org/data/2.5/forecast/daily?q=hồ chí minh&cnt=10

Thì ta có kết quả với cấu trúc JSON khá khác cho với trường hợp trên như sau:

android_64_3–> Bạn tự phân tích cấu trúc ở trên để viết Java Class cho trường hợp Daily (chức năng số 3).

Trong Project này Tui sẽ thực hiện 3 chức năng (thời tiết địa điểm hiện tại của thiết bị, xem thời tiết theo địa điểm nhập bất kỳ và xem thời tiết trên Google Map), còn chức năng dự báo ngày kế tiếp các bạn tiếp tục thực hiện.

Tui có cấu trúc tập tin, class của dự án như sau:

android64_1– Tui chụp màn hình sử dụng phần mềm dự báo thời tiết như sau:

1) Từ màn hình chính, nếu bấm vào “Thời tiết địa điểm hiện tại của thiết bị”:

android64_2Chương trình sẽ hiển thị thông báo chi tiết dự báo thời tiết như sau:

android64_3Ta thấy chi tiết của địa điểm hiện tại của thiết bị (vị trị hiện tại tương đối trong phạm vi thiết bị), ở trên ta thấy nhiệt độ, bầu trời bằng hình có nắng, có mây, có mưa…, tốc độ gió, áp suất…

2) Từ màn hình chính, nếu bấm vào “Xem thời tiết theo địa điểm nhập bất kỳ”:

android64_4Chương trình sẽ hiển thị màn hình cho phép nhập địa chỉ bất kỳ, hoặc chọn các tỉnh thành có sẵn của Việt Nam (muốn bất kỳ địa điểm nào khác ở Việt Nam hay trên thế giới thì tự gõ địa chỉ vào), màn hình chọn địa điểm:

android64_5Nếu ta chọn hoặc nhập địa điểm bất kỳ, ví dụ như Hồ Chí Minh, ta có kết quả sau:

android64_6Ở hình trên ta thấy đấy, Hồ Chí Minh tuy có mưa nhưng nhiệt độ vẫn nóng là 34.2 độ C. Đúng là tại thời điểm Tui post hình này lên là Sài gòn đang mưa và vẫn nóng le lưỡi.

3) Tại màn hình chính, chọn “Xem thời tiết trên Google Map”, chương trình sẽ tự động hiển thị thời tiết tại vị trí hiện tại trên bản đồ, và cho phép bạn nhấn chọn bất kỳ địa điểm nào đó trên bản đồ để xem thời tiết. Đây là chức năng rất thú vị và coding hơi phức tạp.

android64_7Ta có kết quả:

android64_8Nhớ là bạn có thể nhấn chọn vị trí khác để xem thời tiết, ví dụ Tui chọn Nguyễn văn Trỗi:

android64_9Nếu làm tốt bài này bạn có thể áp dụng một cách uyển chuyển vào các dự án liên quan tới Du lịch, xem thời tiết….

Sau đây là Coding chi tiết cho từng phần, tui sẽ giải thích những đặc tính mới trong Android Studio, còn những cái này tương tự như Eclipse thì thôi  (Tui chỉ trỏ link tới rồi các bạn đọc lại).

Ta cần xây dựng mô hình class cho Weather theo tọa độ và địa chỉ của kết quả Json 2 loại dưới đây:

http://api.openweathermap.org/data/2.5/weather?lat=10.778182&lon=106.665504

http://api.openweathermap.org/data/2.5/weather?q=Đà lạt

Như sau:

android64_10Tui sẽ không phân tích hay giải thích tại sao phải viết các lớp như trên nữa, vì Tui đã hướng dẫn chi tiết ở các bài 61, bài 62, bài 63 các bạn bắt buộc phải đọc lại.

Soure code từng lớp như sau:

Lớp Clouds:


package com.tranduythanh.model;

/**
* Created by drthanh on 04/04/2015.
*/
public class Clouds {
private int all;

public int getAll() {
return all;
}

public void setAll(int all) {
this.all = all;
}
}

Lớp Coord:


package com.tranduythanh.model;

/**
* Created by drthanh on 04/04/2015.
*/
public class Coord {
private  double lon;
private double lat;

public double getLon() {
return lon;
}

public void setLon(double lon) {
this.lon = lon;
}

public double getLat() {
return lat;
}

public void setLat(double lat) {
this.lat = lat;
}
}

Lớp Main:


package com.tranduythanh.model;

public class Main {
private double temp;
private double temp_min;
private double temp_max;
private double pressure;
private double sea_level;
private double grnd_level;
private double humidity;
public double getTemp() {
return temp;
}
public void setTemp(double temp) {
this.temp = temp;
}
public double getTemp_min() {
return temp_min;
}
public void setTemp_min(double temp_min) {
this.temp_min = temp_min;
}
public double getTemp_max() {
return temp_max;
}
public void setTemp_max(double temp_max) {
this.temp_max = temp_max;
}
public double getPressure() {
return pressure;
}
public void setPressure(double pressure) {
this.pressure = pressure;
}
public double getHumidity() {
return humidity;
}
public void setHumidity(double humidity) {
this.humidity = humidity;
}

public double getSea_level() {
return sea_level;
}

public void setSea_level(double sea_level) {
this.sea_level = sea_level;
}

public double getGrnd_level() {
return grnd_level;
}

public void setGrnd_level(double grnd_level) {
this.grnd_level = grnd_level;
}
}

Lớp Sys:


package com.tranduythanh.model;

public class Sys {
private double message;
private String country;
private long sunrise;
private long sunset;
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public long getSunrise() {
return sunrise;
}
public void setSunrise(long sunrise) {
this.sunrise = sunrise;
}
public long getSunset() {
return sunset;
}
public void setSunset(long sunset) {
this.sunset = sunset;
}

public double getMessage() {
return message;
}

public void setMessage(double message) {
this.message = message;
}
}

Lớp WeatherItem:


package com.tranduythanh.model;

public class WeatherItem {
private long id;
private String main;
private String description;
private String icon;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getMain() {
return main;
}
public void setMain(String main) {
this.main = main;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}

}

Lớp Wind:


package com.tranduythanh.model;

public class Wind {
private double speed;
private double deg;

public double getSpeed() {
return speed;
}

public void setSpeed(double speed) {
this.speed = speed;
}

public double getDeg() {
return deg;
}

public void setDeg(double deg) {
this.deg = deg;
}
}

Và cuối cùng là lớp tổng hợp JSON:

Lớp OpenWeatherJSon:


package com.tranduythanh.model;

import java.util.List;

public class OpenWeatherJSon {
private Coord coord;
private Sys sys;
private List<WeatherItem> weather;
private String base;
private Main main;
private Wind wind;
private Clouds clouds;
private long dt;
private long id;
private String name;
private int cod;

public Coord getCoord() {
return coord;
}

public void setCoord(Coord coord) {
this.coord = coord;
}

public Sys getSys() {
return sys;
}

public void setSys(Sys sys) {
this.sys = sys;
}

public List<WeatherItem> getWeather() {
return weather;
}

public void setWeather(List<WeatherItem> weather) {
this.weather = weather;
}

public String getBase() {
return base;
}

public void setBase(String base) {
this.base = base;
}

public Main getMain() {
return main;
}

public void setMain(Main main) {
this.main = main;
}

public Wind getWind() {
return wind;
}

public void setWind(Wind wind) {
this.wind = wind;
}

public Clouds getClouds() {
return clouds;
}

public void setClouds(Clouds clouds) {
this.clouds = clouds;
}

public long getDt() {
return dt;
}

public void setDt(long dt) {
this.dt = dt;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getCod() {
return cod;
}

public void setCod(int cod) {
this.cod = cod;
}
}

– Như vậy ta đã có đủ mô hình lớp về kết quả dự báo thời tiết theo định dạng JSON.

– Bạn cần chú ý phải thêm thư viện cho dự án để đưa về Json thông qua GSon (Bạn xem lại thư viện ở các bài trước, ở bài này Tui không hướng dẫn nữa).

– Tiến hành viết thư viện đọc thời tiết bằng đa tiến trình, thư viên này được sử dụng và tái sử dụng cho các chức năng kiểm tra thời tiết theo tọa độ hay địa chỉ.

android64_11Chi tiết từng lớp như sau:

Enum TypePrediction:


package com.tranduythanh.utils;

/**
* Created by drthanh on 11/05/2015.
*/
public enum TypePrediction {
ADDRESS_NAME,//nhập theo địa chỉ cụ thể
LATITUDE_LONGITUDE//nhập theo vĩ độ kinh độ
}

 

enum này để chia ra 2 loại là xem thời tiết theo địa chỉ và theo tọa độ.

Lớp OpenWeatherMapAPI, lớp này dùng để đọc thông tin thời tiết (đưa Json về model java class thông qua GSon):


package com.tranduythanh.utils;

import com.google.gson.Gson;
import com.tranduythanh.model.OpenWeatherJSon;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;

/**
* Created by drthanh on 03/04/2015.
*/
public class OpenWeatherMapAPI {
public static OpenWeatherJSon prediction(String q)
{
try {
String location= URLEncoder.encode(q, "UTF-8");

URL url = new URL("http://api.openweathermap.org/data/2.5/weather?q="+location);
InputStreamReader reader = new InputStreamReader(url.openStream(),"UTF-8");
OpenWeatherJSon results = new Gson().fromJson(reader, OpenWeatherJSon.class);

String idIcon = results.getWeather().get(0).getIcon().toString();
String urlIcon = "http://openweathermap.org/img/w/"+idIcon+".png";
URL urlImage = new URL(urlIcon);

return results;

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
return null;
}

/**
*
* http://api.openweathermap.org/data/2.5/weather?lat=10.778182&lon=106.665504
* @param lat
* @param lon
* @return
*/
public static OpenWeatherJSon prediction(double lat,double lon)
{
try {

URL url = new URL("http://api.openweathermap.org/data/2.5/weather?lat="+lat+"&lon="+lon);
InputStreamReader reader = new InputStreamReader(url.openStream(),"UTF-8");
OpenWeatherJSon results = new Gson().fromJson(reader, OpenWeatherJSon.class);

String idIcon = results.getWeather().get(0).getIcon().toString();
String urlIcon = "http://openweathermap.org/img/w/"+idIcon+".png";
URL urlImage = new URL(urlIcon);

return results;

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
return null;
}

/**
* Sửa lại WeatherJSON vì chưa phù hợp trong trường hợp Daily
* http://api.openweathermap.org/data/2.5/forecast/daily?lat=10.778182&lon=106.66550&cnt=10
* @param lat
* @param lon
* @param cnt
* @return
*/
public static OpenWeatherJSon predictionDaily(double lat,double lon,int cnt)
{
try {

URL url = new URL("http://api.openweathermap.org/data/2.5/forecast/daily?lat="+lat+"&lon="+lon+"&cnt="+cnt);
InputStreamReader reader = new InputStreamReader(url.openStream(),"UTF-8");
OpenWeatherJSon results = new Gson().fromJson(reader, OpenWeatherJSon.class);

String idIcon = results.getWeather().get(0).getIcon().toString();
String urlIcon = "http://openweathermap.org/img/w/"+idIcon+".png";
URL urlImage = new URL(urlIcon);

return results;

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
return null;
}

/**
* Sửa lại WeatherJSON vì chưa phù hợp trong trường hợp Daily
* http://api.openweathermap.org/data/2.5/forecast/daily?q=Đà lạt&cnt=10
* @param q
* @param cnt
* @return
*/
public static OpenWeatherJSon predictionDaily(String q,int cnt)
{
try {
String location= URLEncoder.encode(q, "UTF-8");
URL url = new URL("http://api.openweathermap.org/data/2.5/forecast/daily?q="+location+"&cnt="+cnt);
InputStreamReader reader = new InputStreamReader(url.openStream(),"UTF-8");
OpenWeatherJSon results = new Gson().fromJson(reader, OpenWeatherJSon.class);

String idIcon = results.getWeather().get(0).getIcon().toString();
String urlIcon = "http://openweathermap.org/img/w/"+idIcon+".png";
URL urlImage = new URL(urlIcon);

return results;

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
return null;
}
}

– Lớp WeatherAsyncTask, để thực hiện đa tiến trình truy vấn thời tiết, hiển thị lên giao diện. Lớp này được sử dụng chung cho cả theo Địa chỉ hay theo tọa độ.


package com.tranduythanh.utils;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Address;
import android.location.Geocoder;
import android.os.AsyncTask;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Marker;
import com.gtranslate.Language;
import com.gtranslate.Translator;
import com.tranduythanh.model.OpenWeatherJSon;
import com.tranduythanh.weatherprediction.MyInfoWindowAdapter;
import com.tranduythanh.weatherprediction.R;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
* Created by drthanh on 11/05/2015.
*/
public class WeatherAsyncTask extends AsyncTask<Void,Void,OpenWeatherJSon>{
ProgressDialog dialog;
Activity activity;
TypePrediction typePrediction;
String q;
double latitude;
double longitude;
NumberFormat format = new DecimalFormat("#0.0");
Bitmap myBitmap=null;

Marker marker=null;
GoogleMap map=null;

/**
* Constructor dùng để lấy thời tiết theo địa chỉ bất kỳ
* @param activity
* @param q
*/
public WeatherAsyncTask(Activity activity,String q)
{
this.activity=activity;
this.typePrediction=TypePrediction.ADDRESS_NAME;
this.q=q;
this.dialog=new ProgressDialog(activity);
this.dialog.setTitle("Đang tải thông tin ...");
this.dialog.setMessage("Vui lòng chờ...");
this.dialog.setCancelable(true);
}

/**
* constructor cho phép lấy thông tin thời tiết theo tọa độ bất kỳ
* @param activity
* @param latitude
* @param longitude
*/
public WeatherAsyncTask(Activity activity,double latitude,double longitude)
{
this.activity=activity;
this.typePrediction=TypePrediction.LATITUDE_LONGITUDE;
this.latitude=latitude;
this.longitude=longitude;
this.dialog=new ProgressDialog(activity);
this.dialog.setTitle("Đang tải thông tin ...");
this.dialog.setMessage("Vui lòng chờ...");
this.dialog.setCancelable(true);
}

/**
* constructor cho lấy thông tin thời tiết theo tọa độ bất kỳ trên bản đồ
* @param marker
* @param map
* @param activity
* @param latitude
* @param longitude
*/
public WeatherAsyncTask(Marker marker,GoogleMap map,Activity activity,double latitude,double longitude)
{
this(activity,latitude,longitude);
this.marker=marker;
this.map=map;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
this.dialog.show();
}

@Override
protected OpenWeatherJSon doInBackground(Void... params) {
OpenWeatherJSon openWeatherJSon=null;
if(typePrediction== TypePrediction.LATITUDE_LONGITUDE)
openWeatherJSon= OpenWeatherMapAPI.prediction(latitude,longitude);
else
openWeatherJSon= OpenWeatherMapAPI.prediction(q);
try {
String idIcon = openWeatherJSon.getWeather().get(0).getIcon().toString();
String urlIcon = "http://openweathermap.org/img/w/"+idIcon+".png";
//Tiến hành tạo đối tượng URL
URL urlConnection = new URL(urlIcon);
//Mở kết nối
HttpURLConnection connection = (HttpURLConnection) urlConnection
.openConnection();
connection.setDoInput(true);
connection.connect();
//Đọc dữ liệu
InputStream input = connection.getInputStream();
//Tiến hành convert qua hình ảnh
myBitmap = BitmapFactory.decodeStream(input);
} catch (Exception e) {
e.printStackTrace();
}
return openWeatherJSon;
}

@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}

@Override
protected void onPostExecute(OpenWeatherJSon openWeatherJSon) {
super.onPostExecute(openWeatherJSon);
if(map!=null) {
map.setInfoWindowAdapter(new MyInfoWindowAdapter(openWeatherJSon,myBitmap,marker, this.activity,latitude,longitude));
marker.showInfoWindow();
this.dialog.dismiss();
return;
}
TextView txtTemperature=(TextView) activity.findViewById(R.id.txtTemperature);
TextView txtCurrentAddressName=(TextView) activity.findViewById(R.id.txtCurrentAddressName);
ImageView imageView=(ImageView) activity.findViewById(R.id.imgBauTroi);
TextView txtMaxtemp=(TextView) activity.findViewById(R.id.txtMaxTemp);
TextView txtMinTemp=(TextView) activity.findViewById(R.id.txtMinTemp);
TextView txtWind=(TextView) activity.findViewById(R.id.txtWind);
TextView txtCloudliness= (TextView) activity.findViewById(R.id.txtCloudliness);
TextView txtPressure= (TextView) activity.findViewById(R.id.txtPressure);
TextView txtHumidty= (TextView) activity.findViewById(R.id.txtHumidty);
TextView txtSunrise= (TextView) activity.findViewById(R.id.txtSunrise);
TextView txtSunset= (TextView) activity.findViewById(R.id.txtSunset);
double temperature=openWeatherJSon.getMain().getTemp()-273.15;
String maxtemp= format.format(openWeatherJSon.getMain().getTemp_max()-273.15)+"°C";
String mintemp= format.format(openWeatherJSon.getMain().getTemp_min()-273.15)+"°C";
String wind= openWeatherJSon.getWind().getSpeed()+" m/s";
String mesg = openWeatherJSon.getWeather().get(0).getMain();
//  Translator translate = Translator.getInstance();
// String cloudiness=mesg+" ("+translate.translate(mesg, Language.ENGLISH, Language.VIETNAMESE)+")";
String cloudiness=mesg;
String pressure= openWeatherJSon.getMain().getPressure()+" hpa";
String humidity=openWeatherJSon.getMain().getHumidity()+" %";

Date timeSunrise = new Date(openWeatherJSon.getSys().getSunrise()*1000);
String Sunrise= timeSunrise.getHours()+":"+timeSunrise.getMinutes()+" AM";
Date timeSunSet = new Date(openWeatherJSon.getSys().getSunset()*1000);
String sunset= timeSunSet.getHours()+":"+timeSunSet.getMinutes();
txtTemperature.setText(format.format(temperature)+"°C");
imageView.setImageBitmap(myBitmap);
txtMaxtemp.setText(maxtemp);
txtMinTemp.setText(mintemp);
txtWind.setText(wind);
txtCloudliness.setText(cloudiness);
txtPressure.setText(pressure);
txtHumidty.setText(humidity);
txtSunrise.setText(Sunrise);
txtSunset.setText(sunset);

try {
Geocoder geocoder;
List<Address> addresses;
geocoder = new Geocoder(this.activity, Locale.getDefault());
if(typePrediction==TypePrediction.LATITUDE_LONGITUDE)
addresses = geocoder.getFromLocation(latitude, longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5
else
{
addresses = geocoder.getFromLocationName(q, 1);
}
Address address=null;
if(addresses.size()>0)
address=addresses.get(0);
if(address!=null)
{
if(typePrediction==TypePrediction.LATITUDE_LONGITUDE)
txtCurrentAddressName.setText(address.getAddressLine(0));
else
txtCurrentAddressName.setText(q);
/*String city = address.getLocality();
String state = address.getAdminArea();
String country = address.getCountryName();
String postalCode = address.getPostalCode();
String knownName = address.getFeatureName();*/
}

} catch (IOException e) {
e.printStackTrace();
}
this.dialog.dismiss();
}
}

–  Sau đó ta tiến hành thiết kế các màn hình về viết code cho từng màn hình.

android64_12-Ở trên 3 Activity đều sử dụng 1 màn hình (vì cho dù tìm theo kiểu nào đi nữa thì thông tin thời tiết cũng đầy đủ thông số giống nhau).

– Ta đi vào từng màn hình, trước tiên là màn hình MainActivity:

Activity_main.xml:

android64_13


<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"
tools:context=".MainActivity">

<ImageView
android:layout_width="wrap_content"
android:layout_height="100dp"
android:id="@+id/imageView"
android:src="@drawable/weatherforecast"
android:layout_gravity="center_horizontal" />

<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1"
>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<TextView
android:layout_span="2"
android:layout_width="wrap_content"
android:layout_height="2dp"
android:text=""
android:id="@+id/textView10"
android:background="#004040" />
</TableRow>
<TableRow
android:onClick="showCurrentWeatherClick"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ImageButton
android:onClick="showCurrentWeatherClick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageButton"
android:src="@drawable/weathercurrent" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView"
android:text="Thời tiết địa điểm hiện tại của \nthiết bị "
android:textAlignment="center"
android:layout_gravity="center_vertical" />
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<TextView
android:layout_span="2"
android:layout_width="match_parent"
android:layout_height="2dp"
android:text=""
android:id="@+id/textView5"
android:background="#004040" />
</TableRow>
<TableRow
android:onClick="showWeatherByAddressClick"
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ImageButton
android:onClick="showWeatherByAddressClick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageButton2"
android:src="@drawable/weatheranywhere" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView2"
android:layout_gravity="center_vertical"
android:gravity="fill_vertical"
android:text="Xem thời tiết theo \nđịa điểm nhập bất kỳ " />

</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<TextView
android:layout_span="2"
android:layout_width="match_parent"
android:layout_height="2dp"
android:text=""
android:id="@+id/textView6"
android:background="#004040" />
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageButton3"
android:src="@drawable/weatherpredict" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView3"
android:layout_gravity="center_vertical"
android:gravity="fill_vertical"
android:text="Xem thời tiết theo địa điểm \nnhập bất kỳ và dự báo các \nngày kế tiếp " />
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<TextView
android:layout_span="2"
android:layout_width="match_parent"
android:layout_height="2dp"
android:text=""
android:id="@+id/textView7"
android:background="#004040" />
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:onClick="showMapClick"
android:layout_height="fill_parent">

<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="showMapClick"
android:id="@+id/imageButton4"
android:src="@drawable/weathermap" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView4"
android:ellipsize="middle"
android:layout_gravity="center_vertical"
android:text="Xem thời tiết trên Google Map " />
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent">

<TextView
android:layout_span="2"
android:layout_width="match_parent"
android:layout_height="2dp"
android:text=""
android:id="@+id/textView8"
android:background="#004040" />
</TableRow>

</TableLayout>

</LinearLayout>

– Source code MainActivity:


package com.tranduythanh.weatherprediction;

import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

/**
* sự kiện mở màn hình xem thời tiết trên bản đồ
* @param v
*/
public void showMapClick(View v)
{
if(!isNetworkConnected())
{
Toast.makeText(MainActivity.this,"Bạn cần mở kết nối internet",Toast.LENGTH_LONG).show();
return;
}
Intent iMap=new Intent(MainActivity.this,WeatherMapsActivity.class) ;
startActivity(iMap);
}

/**
* sự kiện xem thời tiết tại vị trí hiện tại
* @param v
*/
public void showCurrentWeatherClick(View v)
{
if(!isNetworkConnected())
{
Toast.makeText(MainActivity.this,"Bạn cần mở kết nối internet",Toast.LENGTH_LONG).show();
return;
}
Intent iCurrent=new Intent(MainActivity.this,WeatherCurrentLocationActivity.class) ;
startActivity(iCurrent);
}

/**
* sự kiện dùng để xem thời tiết theo địa chỉ nhập bất kỳ
* @param v
*/
public void showWeatherByAddressClick(View v)
{
if(!isNetworkConnected())
{
Toast.makeText(MainActivity.this,"Bạn cần mở kết nối internet",Toast.LENGTH_LONG).show();
return;
}
Intent iaddress=new Intent(MainActivity.this,ChooseAddressActivity.class) ;
startActivity(iaddress);
}

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

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

/**
* hàm dùng để kiểm tra xem điện thoại đang kết nối internet hay không
* @return
*/
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null) {
// There are no active networks.
return false;
} else
return true;
}
}

– Tiếp tục xử lý cho màn hình xem thời tiết tại địa điểm hiện tại của thiết bị, đó là

WeatherCurrentLocationActivity, chú ý là cả 3 trường hợp hiển thị chi tiết thời tiết đều sử dụng chung layout “activity_weather_current_location.xml“:

android64_14nội dung XML của màn hình hiển thị chi tiết :


<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.tranduythanh.weatherprediction.WeatherCurrentLocationActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Địa chỉ:"
android:id="@+id/textView11" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtCurrentAddressName"
android:hint="Địa chỉ hiện tại"
android:gravity="center"
android:textColor="#ff1919ff" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView12"
android:background="#ff06ff22"
android:height="1dp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Nhiệt độ:"
android:id="@+id/textView9" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtTemperature"
android:hint="nhiệt độ ở đây"
android:gravity="center"
android:textColor="#ffff2d34" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="Bầu trời:"
android:id="@+id/textView15" />

<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:id="@+id/imgBauTroi"
android:layout_gravity="center" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView16"
android:background="#ff06ff22"
android:height="1dp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Chi tiết"
android:id="@+id/textView17"
android:layout_gravity="center" />

<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/scrollView" >

<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" >

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Max Temp:"
android:id="@+id/textView18" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtMaxTemp"
android:hint="Nhiệt độ lớn nhất ở đây" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Min Temp:"
android:id="@+id/textView20" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtMinTemp"
android:hint="Nhiệt độ nhỏ nhất ở đây" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Wind:"
android:id="@+id/textView21" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtWind"
android:hint="Tố độ gió ở đây" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Cloudiness:"
android:id="@+id/textView22" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtCloudliness"
android:hint="Hướng mây" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Pressure:"
android:id="@+id/textView23" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtPressure"
android:hint="Áp suất ở đây" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Humidity:"
android:id="@+id/textView24" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtHumidty"
android:hint="độ ẩm ở đây" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Sunrise:"
android:id="@+id/textView25" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtSunrise"
android:hint="Mặt trời mọc" />
</TableRow>

<TableRow
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Sunset:"
android:id="@+id/textView26" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/txtSunset"
android:hint="Mặt trời mọc" />
</TableRow>
</TableLayout>
</ScrollView>

</LinearLayout>

– source code WeatherCurrentLocationActivity(xử lý xem chi tiết thời tiết tại vị trí hiện tại của thiết bị):


package com.tranduythanh.weatherprediction;

import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.tranduythanh.utils.WeatherAsyncTask;

import java.text.DecimalFormat;
import java.text.NumberFormat;

public class WeatherCurrentLocationActivity extends ActionBarActivity {
NumberFormat format = new DecimalFormat("#0.0");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather_current_location);

}

@Override
protected void onResume() {
super.onResume();
getCurrentLocation();
}

/**
* source code lấy địa điểm hiện tại của thiết bị
* chú ý nhớ cấp quyền trong Manifest để cho phép truy suất
*/
private void getCurrentLocation() {

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();

Location lastLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
if (lastLocation != null)
{
//ta lấy được thì truyền vĩ độ, kinh độ để xem thời tiết
WeatherAsyncTask task=new WeatherAsyncTask(this,lastLocation.getLatitude(),lastLocation.getLongitude());
task.execute();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_weather_current_location, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

-Ta qua màn hình xem thời tiết tại một địa chỉ bất kỳ:

android64_15Như vậy chức năng này cũng sử dụng chung màn hình hiển thị chi tiết thời tiết, và ta cần cung cấp màn hình chọn tỉnh thành Việt Nam có sẵn hoặc trong ô nhập địa chỉ cho phép người sử dụng nhập bất kỳ địa điểm nào đó trên thế giới. Bấm nút Xem thời tiết hệ thống sẽ xuất hiện kết quả như hình số 3 ở trên (bạn thấy đó ở sài gòn bây giờ là 27.9 độ C quá là mát mẻ… vì Tui chụp hình này lúc 10h đêm, chứng tỏ phần mềm này nó thông minh, nó biết ban đêm thường là mát hơn ban ngày ).

– Ta xem layout của màn hình chọn địa chỉ (activity_choose_address.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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context="com.tranduythanh.weatherprediction.ChooseAddressActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nhập địa chỉ:"
android:id="@+id/textView13" />

<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/txtAddressCheck"
android:completionThreshold="1"
android:hint="Địa chỉ kiểm tra ở đây" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Xem thời tiết"
android:id="@+id/btnCheckWeather"
android:layout_gravity="center_horizontal" />

<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Chọn theo tỉnh thành có sẵn"
android:id="@+id/textView14"
android:layout_gravity="center_horizontal" />

<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/lvTinhThanh"
android:layout_gravity="center_horizontal" />
</LinearLayout>

Source xử lý cho màn hình này như sau (ChooseAddressActivity):


package com.tranduythanh.weatherprediction;

import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.ListView;

public class ChooseAddressActivity extends ActionBarActivity {

AutoCompleteTextView txtAddress;
ListView lvTinhThanh;
String []arrTinhThanh;
ArrayAdapter<String>adapterTinhThanh;

Button  btnCheckWeather;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_address);
addControls();
addEvents();
}
public void  addControls()
{
txtAddress=(AutoCompleteTextView)findViewById(R.id.txtAddressCheck);
lvTinhThanh= (ListView) findViewById(R.id.lvTinhThanh);
arrTinhThanh=getResources().getStringArray(R.array.arrTinhThanh);
adapterTinhThanh=new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,arrTinhThanh);
lvTinhThanh.setAdapter(adapterTinhThanh);
btnCheckWeather= (Button) findViewById(R.id.btnCheckWeather);
txtAddress.setAdapter(adapterTinhThanh);
}
public  void addEvents()
{
txtAddress.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
adapterTinhThanh.getFilter().filter(s);
}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {

}
});
btnCheckWeather.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//mở màn hình xem thời tiết theo địa chỉ nhập bất kỳ
Intent intent=new Intent(ChooseAddressActivity.this,WeatherByAddressActivity.class);
//truyền địa chỉ qua bên kia để xử lý
intent.putExtra("ADDRESS",txtAddress.getText()+"");
startActivity(intent);
}
});
lvTinhThanh.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
txtAddress.setText(arrTinhThanh[position]);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_weather_by_address, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

– sau khi bấm vào nút xem thời tiết, hệ thống sẽ mở màn hình (WeatherByAddressActivity), chú ý màn hình này như đã nói ở trên là sử dụng chung layout với chức năng xem thời tiết tại vị trí hiện tại của máy.

source code của lớp WeatherByAddressActivity như sau:


package com.tranduythanh.weatherprediction;

import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

import com.tranduythanh.utils.WeatherAsyncTask;

public class WeatherByAddressActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather_current_location);

}

@Override
protected void onResume() {
super.onResume();
getWeatherByAddress();
}

/**
* hàm gọi đa tiến trình truy suất thời tiết theo địa chỉ
*/
private void getWeatherByAddress() {
Intent i=getIntent();
//lấy địa chỉ từ bên ChooseAddressActivity gửi qua:
String q=i.getStringExtra("ADDRESS");
WeatherAsyncTask task=new WeatherAsyncTask(this,q);
task.execute();
}

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

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

-Chức năng cuối cùng Tui muốn hướng dẫn là xem thời tiết tại vị trí bất kỳ trên bản đồ (lần đầu tiên mở lên thì hệ thống sẽ hiển thị thời tiết tại vị trí hiện tại của thiết bị lên bản đồ, sau đó người sử dụng muốn xem nơi khác thì chỉ cần nhấn vào vị trí trên bản đồ, chức năng này rất tiện lợi):

android64_16– Chức năng này ta cần xử lý coding hơi phức tạp chút xíu, cần phải xử lý custom InfoWindowAdapter cho việc hiển thị giao diện khác nên bản đồ cũng như xác định vị trí mà người sử dụng chọn bất kỳ trên bản đồ rồi di chuyển và hiển thị chi tiết thời tiết tại địa chỉ mới này.

– Bạn chú ý để hiển thị Map trong Android Studio thì nó đơn giản hơn rất nhiều so với Eclipse vì Android Studio đã hỗ trợ rất tốt chức năng này.

– Ta cần tạo Google Map Activity tên là WeatherMapsActivity như sau:

– Bấm chuột phải vào Package/ chọn New/ chọn Google/ chọn Google Maps Activity:

android64_17– Sau đó màn hình tạo activity hiển thị lên, bạn đặt tên là WeatherMapsActivity hệ thống sẽ tạo ra layout tương thích để hiển thị Map (activity_weather_maps.xml):


<fragment 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:id="@+id/map"
tools:context="com.tranduythanh.weatherprediction.WeatherMapsActivity"
android:name="com.google.android.gms.maps.SupportMapFragment" />

Và lớp WeatherMapsActivity kế thừa từ FragmentActivity.

Đồng thời nó sẽ phát sinh ra thêm 1 file google_maps_api.xml để lưu trữ KEY sử dụng Map như đã hướng dẫn ở các bài trước (bạn cần tự tạo để lấy KEY dán vào đây):

android64_18Ở trên bạn cần làm theo bước: Copy toàn bộ dữ liệu trong dòng mà tui đánh dấu là số 1, đưa nó vào link trong vùng Tui đánh dấu là số 2 (cách tạo key trong này đã hướng dẫn từ các bài trước về Map, bạn tự coi lại), sau khi nó tạo ra Key truy suất MAP, bạn dán Key đó vào vùng số 3. Ở vùng Số  hệ thông có tự đặt tên là google_maps_key.

– Sau đó bạn mở AndroidManifest lên để quát sát nó có cái gì ở đây:

android64_19Bạn thấy đấy, nó đơn giản hơn rất là nhiều, nó tự làm giùm cho chúng ta.

– Tiếp theo ta tiến hành coding để xử lý cho cho thời tiết trên map:

– Tạo lớp MyInfoWindowAdapter (sử dụng chung Layout xem thời tiết như đã đề cập ở trên) để hiển thị lên Map:


package com.tranduythanh.weatherprediction;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Marker;
import com.tranduythanh.model.OpenWeatherJSon;
import com.tranduythanh.utils.TypePrediction;
import com.tranduythanh.utils.WeatherAsyncTask;

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
* Created by drthanh on 12/05/2015.
*/
public class MyInfoWindowAdapter  implements GoogleMap.InfoWindowAdapter {
private Activity context;
Marker maker=null;
OpenWeatherJSon openWeatherJSon=null;
Bitmap myBitmap=null;
NumberFormat format = new DecimalFormat("#0.0");
double latitude;
double longitude;
public MyInfoWindowAdapter(Activity context)
{
this.context=context;
}
public MyInfoWindowAdapter(OpenWeatherJSon openWeatherJSon,Bitmap myBitmap,Marker maker,Activity context)
{
this(context);
this.maker=maker;
this.openWeatherJSon=openWeatherJSon;
this.myBitmap=myBitmap;
}
public MyInfoWindowAdapter(OpenWeatherJSon openWeatherJSon,Bitmap myBitmap,Marker maker,Activity context,double latitude,double longitude)
{
this(openWeatherJSon,myBitmap,maker,context);
this.latitude=latitude;
this.longitude=longitude;
}
@Override
public View getInfoWindow(Marker marker) {
View v = this.context.getLayoutInflater().inflate(R.layout.activity_weather_current_location, null);

TextView txtTemperature=(TextView) v.findViewById(R.id.txtTemperature);
TextView txtCurrentAddressName=(TextView) v.findViewById(R.id.txtCurrentAddressName);
ImageView imageView=(ImageView) v.findViewById(R.id.imgBauTroi);
TextView txtMaxtemp=(TextView) v.findViewById(R.id.txtMaxTemp);
TextView txtMinTemp=(TextView) v.findViewById(R.id.txtMinTemp);
TextView txtWind=(TextView) v.findViewById(R.id.txtWind);
TextView txtCloudliness= (TextView) v.findViewById(R.id.txtCloudliness);
TextView txtPressure= (TextView) v.findViewById(R.id.txtPressure);
TextView txtHumidty= (TextView) v.findViewById(R.id.txtHumidty);
TextView txtSunrise= (TextView) v.findViewById(R.id.txtSunrise);
TextView txtSunset= (TextView) v.findViewById(R.id.txtSunset);
double temperature=openWeatherJSon.getMain().getTemp()-273.15;
String maxtemp= format.format(openWeatherJSon.getMain().getTemp_max()-273.15)+"°C";
String mintemp= format.format(openWeatherJSon.getMain().getTemp_min()-273.15)+"°C";
String wind= openWeatherJSon.getWind().getSpeed()+" m/s";
String mesg = openWeatherJSon.getWeather().get(0).getMain();
//  Translator translate = Translator.getInstance();
// String cloudiness=mesg+" ("+translate.translate(mesg, Language.ENGLISH, Language.VIETNAMESE)+")";
String cloudiness=mesg;
String pressure= openWeatherJSon.getMain().getPressure()+" hpa";
String humidity=openWeatherJSon.getMain().getHumidity()+" %";

Date timeSunrise = new Date(openWeatherJSon.getSys().getSunrise()*1000);
String Sunrise= timeSunrise.getHours()+":"+timeSunrise.getMinutes()+" AM";
Date timeSunSet = new Date(openWeatherJSon.getSys().getSunset()*1000);
String sunset= timeSunSet.getHours()+":"+timeSunSet.getMinutes();
txtTemperature.setText(format.format(temperature)+"°C");
imageView.setImageBitmap(myBitmap);
txtMaxtemp.setText(maxtemp);
txtMinTemp.setText(mintemp);
txtWind.setText(wind);
txtCloudliness.setText(cloudiness);
txtPressure.setText(pressure);
txtHumidty.setText(humidity);
txtSunrise.setText(Sunrise);
txtSunset.setText(sunset);

try {
Geocoder geocoder;
List<Address> addresses;
geocoder = new Geocoder(this.context, Locale.getDefault());
addresses = geocoder.getFromLocation(latitude, longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5

Address address=null;
if(addresses.size()>0)
address=addresses.get(0);
if(address!=null)
{
txtCurrentAddressName.setText(address.getAddressLine(0));
/*String city = address.getLocality();
String state = address.getAdminArea();
String country = address.getCountryName();
String postalCode = address.getPostalCode();
String knownName = address.getFeatureName();*/
}

} catch (IOException e) {
e.printStackTrace();
}
v.setBackgroundColor (Color.WHITE);
return v;
}

@Override
public View getInfoContents(Marker marker) {
return null;
}
}

– Cuối cùng ta xử lý coding cho phép hiển thị thời tiết bất kỳ trên Bản đồ (WeatherMapsActivity):


package com.tranduythanh.weatherprediction;

import android.app.ProgressDialog;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.support.v4.app.FragmentActivity;
import android.os.Bundle;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.tranduythanh.utils.WeatherAsyncTask;

public class WeatherMapsActivity extends FragmentActivity {

private GoogleMap mMap; // Might be null if Google Play services APK is not available.
ProgressDialog myProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather_maps);
myProgress = new ProgressDialog(this);
myProgress.setTitle("Đang tải Map ...");
myProgress.setMessage("Vui lòng chờ...");
myProgress.setCancelable(true);
//Hiển thị Progress Bar
myProgress.show();
setUpMapIfNeeded();
addEvents();
}

/**
* xử lý chọn địa điểm bất kỳ trên Map
* lấy ra được lớp LatLng để lấy kinh độ vĩ độ
* truyền cho hàm moveAndShowWeatherNewPlace để hiển thị thời tiết  tại địa điể mới
*/
public void addEvents()
{
if(mMap==null)return;
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
moveAndShowWeatherNewPlace(latLng);
}
});
}

/**
* hiển thị lại thời tiết tại địa điểm mới chọn trên bản đồ
* @param latLng
*/
private void moveAndShowWeatherNewPlace(LatLng latLng) {
if (latLng != null)
{
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(latLng.latitude, latLng.longitude), 13));

CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(latLng.latitude, latLng.longitude))      // Sets the center of the map to location user
.zoom(15)                   // Sets the zoom
.bearing(90)                // Sets the orientation of the camera to east
.tilt(40)                   // Sets the tilt of the camera to 30 degrees
.build();                   // Creates a CameraPosition from the builder
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

MarkerOptions option=new MarkerOptions();
option.position(new LatLng(latLng.latitude, latLng.longitude));
option.title("Cho o cua tui").snippet("Tran Duy Thanh");
option.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
//option.icon(BitmapDescriptorFactory.fromResource(R.drawable.icon_tiempo));
//option.alpha(0.8f);
//option.rotation(90);
Marker maker = mMap.addMarker(option);
//maker.showInfoWindow();
WeatherAsyncTask task=new WeatherAsyncTask(maker,mMap,WeatherMapsActivity.this,latLng.latitude,latLng.longitude);
task.execute();
//maker.showInfoWindow();
//mMap.setInfoWindowAdapter(new MyInfoWindowAdapter(maker,WeatherMapsActivity.this,lastLocation));
//tiến hành hiển thị lên Custom marker option lên Map:
//maker.showInfoWindow();
}
}

@Override
protected void onResume() {
super.onResume();
//setUpMapIfNeeded();
}

/**
* Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
* installed) and the map has not already been instantiated.. This will ensure that we only ever
* call {@link #setUpMap()} once when {@link #mMap} is not null.
* <p/>
* If it isn't installed {@link SupportMapFragment} (and
* {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
* install/update the Google Play services APK on their device.
* <p/>
* A user can return to this FragmentActivity after following the prompt and correctly
* installing/updating/enabling the Google Play services. Since the FragmentActivity may not
* have been completely destroyed during this process (it is likely that it would only be
* stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
* method in {@link #onResume()} to guarantee that it will be called.
*/
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
// Check if we were successful in obtaining the map.
if (mMap != null) {
mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {

@Override
public void onMapLoaded() {
//Đã tải thành công thì tắt Dialog Progress đi
myProgress.dismiss();
}
});
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
mMap.getUiSettings().setZoomControlsEnabled(true);
mMap.setMyLocationEnabled(true);

//setUpMap();
TuiDangODau();
}
}
}

/**
* This is where we can add markers or lines, add listeners or move the camera. In this case, we
* just add a marker near Africa.
* <p/>
* This should only be called once and when we are sure that {@link #mMap} is not null.
*/
private void setUpMap() {
LatLng TTTH_KHTN = new LatLng(10.763181, 106.675664);
MarkerOptions option=new MarkerOptions();
option.position(TTTH_KHTN);
option.title("Trung tâm tin học ĐH KHTN").snippet("Tran Duy Thanh");
option.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
//option.alpha(0.8f);
//option.rotation(90);
Marker maker = mMap.addMarker(option);
maker.showInfoWindow();
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(TTTH_KHTN, 15));
//mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}

/**
* mặc định lần đầu mở Map lên sẽ hiển thị chi tiết thời tiết tại vị trí hiện tại
* lên trên bản đồ
*/
private void TuiDangODau() {

LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();

Location lastLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
if (lastLocation != null)
{
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(
new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()), 13));

CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()))      // Sets the center of the map to location user
.zoom(15)                   // Sets the zoom
.bearing(90)                // Sets the orientation of the camera to east
.tilt(40)                   // Sets the tilt of the camera to 30 degrees
.build();                   // Creates a CameraPosition from the builder
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

MarkerOptions option=new MarkerOptions();
option.position(new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()));
option.title("Cho o cua tui").snippet("Tran Duy Thanh");
option.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE));
//option.icon(BitmapDescriptorFactory.fromResource(R.drawable.icon_tiempo));
//option.alpha(0.8f);
//option.rotation(90);
Marker maker = mMap.addMarker(option);
//maker.showInfoWindow();
WeatherAsyncTask task=new WeatherAsyncTask(maker,mMap,WeatherMapsActivity.this,lastLocation.getLatitude(),lastLocation.getLongitude());
task.execute();
//maker.showInfoWindow();
//mMap.setInfoWindowAdapter(new MyInfoWindowAdapter(maker,WeatherMapsActivity.this,lastLocation));
//tiến hành hiển thị lên Custom marker option lên Map:
//maker.showInfoWindow();
}
}
}

– Sau cùng ta cần cấu hình AndroidManifest đầy đủ như sau:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tranduythanh.weatherprediction" >

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but are recommended.
-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

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

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

<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="@string/google_maps_key" />

<activity
android:name=".WeatherMapsActivity"
android:label="@string/title_activity_weather_maps" >
</activity>
<activity
android:name=".WeatherCurrentLocationActivity"
android:label="@string/title_activity_weather_current_location" >
</activity>
<activity
android:name=".WeatherByAddressActivity"
android:label="@string/title_activity_weather_by_address" >
</activity>
<activity
android:name=".ChooseAddressActivity"
android:label="@string/title_activity_choose_address" >
</activity>
</application>

</manifest>

– Như vậy Tui đã trình bày gần hoàn chỉnh phần mềm xem dự báo thời tiết (còn chức năng dự báo ngày kế tiếp bạn tự làm).

– Bạn cần đọc kỹ lại các bài về Map trước đó nếu như chưa rành, cố gắng đọc hiểu và làm lại bài này nhều lần.

– Các bạn có thể tải source code đầy đủ ở đây (nhớ phải đổi lại KEY mới hoạt động phần Map): http://www.mediafire.com/download/3fwxcr8ziudjt47/WeatherPrediction.rar

– Chúc các bạn thành công.

38 responses

  1. Mong thầy làm thêm nhiều bài viết hơn nữa, em cảm ơn thầy nhiều

  2. Bài viết hay quá mấy thầy ơi 😀

    1. cho e hỏi a đạ chạy được bài này chưa ạ. có thể cho e hỏi một số vấn đề liên quan được k ạ. e làm xong mà bài không chạy được. k biết lỗi gì ạ
      e cảm ơn trước ạ

  3. Thầy ơi sau thầy chưa viết code hướng dẫn

  4. – Thầy ơi cho em hỏi (em không biết hỏi vấn đề này vào mục nào, tạm thời em hỏi ở đây luôn Thầy nhá)
    – Em có tạo webservice với 2 hàm như bên dưới (hình 1)
    – Vấn đề ở đây là khi chạy localhost một trong 2 hàm….nó đều hiện ra cái form cho mình nhập dữ liệu (hình 2)
    – Nhưng khi chạy trên hosting thật…thì nó không hiện ra gì hết nên chạy sai Thầy ơi (hình 3)
    – Mong Thầy giải thích và cách khắc phục giùm em. Em xin chân thành cám ơn.

    Hình 1: http://upanhviet.net/?di=Z8Q4
    Hình 2: http://upanhviet.net/?di=MNOQ
    Hình 3: http://upanhviet.net/?di=6NS1

    1. E khắc phục được rồi 🙂

      1. Trên hosting thật em phải dùng công cụ để tương tác (nó không có hiển thị như em test ở Localhost), Microsoft hỗ trợ sẵn công cụ “WcfTestClient.exe”, thường nó nằm C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE (tùy version em cài)

  5. […] – và nhớ bổ sung thêm hàm kiểm tra xem điện thoại có đang kết nối internet hay không nhé, nếu chưa có internet thì phải tắt nút Upload hình đi, đoạn code kiểm tra internet có hay không đã được đề cập đến trong bài 64 dự báo thời tiết. […]

  6. Dương Nguyễn Đại | Reply

    CHo mình hỏi 2 cái này lấy ở đâu ra zậy?

    import com.google.android.gms.maps.GoogleMap;
    import com.google.android.gms.maps.model.Marker;

    1. Dương Nguyễn Đại | Reply

      đã tìm ra nguyên nhân
      sory!

      1. a đã chạy xong bài này chưa vậy. Sao e làm giống vậy mà bài không chạy được vậy. a có thể cho e hỏi một số cái được k ạ

  7. thay oi lap trinh game android phieu lieu nhu the nao a
    em chi la hoc sinh lop 8 thoi

  8. Thầy ơi, cho em hỏi chút, bây giờ trang http://openweathermap.org/api không cho truy cập thông tin thời tiết nữa thì phải làm sao ạ ?

    1. hiện tại trang http://openweathermap.org/api vẫn cho truy cập và lấy thông tin thời tiết, nhưng cần bổ sung “appid”. Ví dụ: thời tiết tại 1 location: http://api.openweathermap.org/data/2.5/forecast/daily?lat=10.778182&lon=106.665504&cnt=1&appid=YourAppIdHere

      1. Làm sao để dự báo được ngày tiếp theo ạ? Thầy có code tham khảo k cho em với ạ!
        mail cua em la letran1495@gmail.com

      2. Hi em
        Trên trang đó có nói mà em (coi trước 15 ngày được luôn)
        Thầy Thanh.

  9. ai đó cho e hỏi
    private List<WeatherItem> weather;
    dòng này có nghĩa là gì trong lớp OpenWeatherJSon được không ạ

  10. thầy cho e hỏi dòng này private List<WeatherItem> weather; trong OpenWeatherJSON có nghĩa là gì mà e viết nó báo lỗi thầy ạ. thầy giúp e với ạ

    1. Làm sao để dự báo được ngày tiếp theo ạ? Thầy có code tham khảo k cho em với ạ!
      mail cua em la letran1495@gmail.com

  11. có ai cho e hỏi là tại sao e chạy lên rồi mà không xem thời tiết được cái nào vậy ạ. mà e làm giống các bước trên rồi ạ

    1. Bạn phải thêm key của ở trong class OpenWeatherMapAPI mới chảy được

      1. thêm key gì vậy bạn

  12. Bài này thầy tạo api level bao nhiêu vậy thầy, Sao chỗ Location lastLocation = locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, false));
    này em làm mà sao báo call require permission…. code should explicity check to see if permission ….

  13. private List<WeatherItem> weather; cái khai báo này là gì hả thầy ơi 😦

  14. giúp ích khá nhiều (y) mong sẽ được hiểu nhiều hơn về android

  15. thầy cho em hỏi cái key mà thầy nói là sao ? tại em mới học. Thầy cho em cái hình minh họa nha, cảm ơn

  16. Có bạn nào biết code tiếp phần daily của thầy không? Hướng dẫn mình với. Nếu được cho mình xin code tham khỏa luôn. Xin cảm ơn!!

    1. hi bạn!
      bạn làm chạy được mấy phần kia chưa cho mình hỏi với!
      thank bạn!

  17. cho em hỏi muốn xem thời tiết trong vòng 1 tuần thì phải làm sao ạ?

  18. đỗ tấn thành | Reply

    Thầy ơi cho e hỏi . sao e làm mà không hiện ra vị trí hiện tại vậy ak.

    1. Hi em
      Coding cũ có thể version mới ko còn support. Em nên tìm theo version mới

      Thầy Thanh

  19. help! ai co code nhung ngay tiep theo khong. share voi.

    1. hi bạn!
      bạn làm chạy được mấy phần kia chưa cho mình hỏi với!
      thank bạn!

  20. em đã import libary Gson-2.2.4 nhưng sao trong chổ new Gson().formJson(..) bị lỗi gạch đỏ dưới Gson vậy thầy

    1. Em kiểm tra đã implement Serialize cho lớp chưa em? hoặc chụp hình cho Thầy ngó nhé.

      1. https://1drv.ms/f/s!ArQl57gDJ41kqDQXMhKmNyr6c1If dạ link hình ảnh đây thầy

  21. hi thầy!
    phiền thầy cho em hỏi là sao em source code của thầy về chạy thì không báo lỗi gì nhưng lại không load được dữ liệu thời tiết vậy ạ!
    thank thầy ạ!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s