
В этой статье вы узнаете как получить погоду с openweathermap.org по координатам пользователя для текущего дня и прогноз на ближайщие 5 дней с помощью библиотеки Retrofit.
Retrofit это REST клиент для Android написанный в Square. Эта библиотека позволяет легко получить и загрузить JSON (или любую другую структуру данных) через REST. Retrofit позволяет настроить какой конвертор использовать для сериализации/десериализации данных. Например, для JSON можно использовать GSON, также можно определить произвольный конвертор для парсинга других структур данных (например, для XML). Retrofit использует OkHttp библиотеку для HTTP запросов.
Побробней познакомится с возможностями Retrofit можно в статье How to get remote resource with Retrofit in Android.
В качестве источника погоды для
Перед использованием OpenWeatherMap API необходимо зарегистрироваться и получить ключ.
В файл AndroidManifest.xml добавим права на Internet и гео-локацию
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
В файл build.gradle (Module:app) добавим Retrofit, Glide и GSON-конвертор
dependencies {
...
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.squareup.retrofit2:retrofit:2.2.0'
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
}
Опишем модель данных для прогноза погоды на текущий день как того желает Retrofit.
// файл WeatherDay.java
public class WeatherDay {
public class WeatherTemp {
Double temp;
Double temp_min;
Double temp_max;
}
public class WeatherDescription {
String icon;
}
@SerializedName("main")
private WeatherTemp temp;
@SerializedName("weather")
private List<WeatherDescription> desctiption;
@SerializedName("name")
private String city;
@SerializedName("dt")
private long timestamp;
public WeatherDay(WeatherTemp temp, List<WeatherDescription> desctiption) {
this.temp = temp;
this.desctiption = desctiption;
}
public Calendar getDate() {
Calendar date = Calendar.getInstance();
date.setTimeInMillis(timestamp * 1000);
return date;
}
public String getTemp() { return String.valueOf(temp.temp); }
public String getTempMin() { return String.valueOf(temp.temp_min); }
public String getTempMax() { return String.valueOf(temp.temp_max); }
public String getTempInteger() { return String.valueOf(temp.temp.intValue()); }
public String getTempWithDegree() { return String.valueOf(temp.temp.intValue()) + "\u00B0"; }
public String getCity() { return city; }
public String getIcon() { return desctiption.get(0).icon; }
public String getIconUrl() {
return "http://openweathermap.org/img/w/" + desctiption.get(0).icon + ".png";
}
}
Опишем модель данных для прогноза погоды на 5 дней, тут проще, так-как это список из объектов WeatherDay.
// файл WeatherForecast.java
public class WeatherForecast {
@SerializedName("list")
private List<WeatherDay> items;
public WeatherForecast(List<WeatherDay> items) {
this.items = items;
}
public List<WeatherDay> getItems() {
return items;
}
}
Опишем сам API, т.е. укажем ключ, входные URL для каждого из прогнозов и параметры которые передаются для получения погоды.
public class WeatherAPI {
public static String KEY = "KEY";
public static final String BASE_URL = "http://api.openweathermap.org/data/2.5/";
private static Retrofit retrofit = null;
public interface ApiInterface {
@GET("weather")
Call<WeatherDay> getToday(
@Query("lat") Double lat,
@Query("lon") Double lon,
@Query("units") String units,
@Query("appid") String appid
);
@GET("forecast")
Call<WeatherForecast> getForecast(
@Query("lat") Double lat,
@Query("lon") Double lon,
@Query("units") String units,
@Query("appid") String appid
);
}
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Макет activity_main.xml для Activity простой. В первом LinearLayout (с id = llToday) будет отображаться погода для текущего дня, а во втором LinearLayout (с id = llForecast) будет отображаться прогноз погоды на ближайшие 5 дней (наполнятся програмно), оставляем его пустым.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/llToday"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="30dip">
<TextView
android:id="@+id/tvTemp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="50dp"
android:text="Weather... " />
<ImageView
android:id="@+id/ivImage"
android:layout_width="wrap_content"
android:layout_height="60dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/llForecast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="30dip">
</LinearLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get"
android:onClick="getWeather"/>
</LinearLayout>
Непосредственно сама Activity ниже. Изображения для погоды подгружается с помощью Glide.
public class MainActivity extends AppCompatActivity {
String TAG = "WEATHER";
TextView tvTemp;
ImageView tvImage;
LinearLayout llForecast;
WeatherAPI.ApiInterface api;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTemp = (TextView) findViewById(R.id.tvTemp);
tvImage = (ImageView) findViewById(R.id.ivImage);
llForecast = (LinearLayout) findViewById(R.id.llForecast);
api = WeatherAPI.getClient().create(WeatherAPI.ApiInterface.class);
}
public void getWeather(View v) {
Double lat = 49.22;
Double lng = 28.409;
String units = "metric";
String key = WeatherAPI.KEY;
Log.d(TAG, "OK");
// get weather for today
Call<WeatherDay> callToday = api.getToday(lat, lng, units, key);
callToday.enqueue(new Callback<WeatherDay>() {
@Override
public void onResponse(Call<WeatherDay> call, Response<WeatherDay> response) {
Log.e(TAG, "onResponse");
WeatherDay data = response.body();
//Log.d(TAG,response.toString());
if (response.isSuccessful()) {
tvTemp.setText(data.getCity() + " " + data.getTempWithDegree());
Glide.with(MainActivity.this).load(data.getIconUrl()).into(tvImage);
}
}
@Override
public void onFailure(Call<WeatherDay> call, Throwable t) {
Log.e(TAG, "onFailure");
Log.e(TAG, t.toString());
}
});
// get weather forecast
Call<WeatherForecast> callForecast = api.getForecast(lat, lng, units, key);
callForecast.enqueue(new Callback<WeatherForecast>() {
@Override
public void onResponse(Call<WeatherForecast> call, Response<WeatherForecast> response) {
Log.e(TAG, "onResponse");
WeatherForecast data = response.body();
//Log.d(TAG,response.toString());
if (response.isSuccessful()) {
SimpleDateFormat formatDayOfWeek = new SimpleDateFormat("E");
LayoutParams paramsTextView = new LayoutParams(
LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
LayoutParams paramsImageView = new LayoutParams(convertDPtoPX(40, MainActivity.this),
convertDPtoPX(40, MainActivity.this));
int marginRight = convertDPtoPX(15, MainActivity.this);
LayoutParams paramsLinearLayout = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
paramsLinearLayout.setMargins(0, 0, marginRight, 0);
llForecast.removeAllViews();
for (WeatherDay day : data.getItems()) {
if (day.getDate().get(Calendar.HOUR_OF_DAY) == 15) {
String date = String.format("%d.%d.%d %d:%d",
day.getDate().get(Calendar.DAY_OF_MONTH),
day.getDate().get(Calendar.WEEK_OF_MONTH),
day.getDate().get(Calendar.YEAR),
day.getDate().get(Calendar.HOUR_OF_DAY),
day.getDate().get(Calendar.MINUTE)
);
Log.d(TAG, date);
Log.d(TAG, day.getTempInteger());
Log.d(TAG, "---");
// child view wrapper
LinearLayout childLayout = new LinearLayout(MainActivity.this);
childLayout.setLayoutParams(paramsLinearLayout);
childLayout.setOrientation(LinearLayout.VERTICAL);
// show day of week
TextView tvDay = new TextView(MainActivity.this);
String dayOfWeek = formatDayOfWeek.format(day.getDate().getTime());
tvDay.setText(dayOfWeek);
tvDay.setLayoutParams(paramsTextView);
childLayout.addView(tvDay);
// show image
ImageView ivIcon = new ImageView(MainActivity.this);
ivIcon.setLayoutParams(paramsImageView);
Glide.with(MainActivity.this).load(day.getIconUrl()).into(ivIcon);
childLayout.addView(ivIcon);
// show temp
TextView tvTemp = new TextView(MainActivity.this);
tvTemp.setText(day.getTempWithDegree());
tvTemp.setLayoutParams(paramsTextView);
childLayout.addView(tvTemp);
llForecast.addView(childLayout);
}
}
}
}
@Override
public void onFailure(Call<WeatherForecast> call, Throwable t) {
Log.e(TAG, "onFailure");
Log.e(TAG, t.toString());
}
});
}
public int convertDPtoPX(int dp, Context ctx) {
float density = ctx.getResources().getDisplayMetrics().density;
int px = (int)(dp * density);
return px;
}
}
Результат