В этой статье вы узнаете как получить погоду с 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; } }
Результат