Вы находитесь на странице: 1из 11

REST API

Для работы с сетевыми запросами будем использовать Retrofit — типобезопасный


HTTP-клиент для Android от Square. Библиотекой удобно пользоваться для запроса к
различным веб-сервисам с командами GET, POST, PUT, DELETE. Может работать в
асинхронном режиме, что избавляет от лишнего кода.

Для преобразования ответов сервера из формата JSON в объекты воспользуемся


библиотекой Gson.

Подключите библиотеки в проект добавлением таких строк в файл сборки build.gradle (app):

1 dependencies {
2 ...
3 //gson
4 implementation 'com.google.code.gson:gson:2.8.2'
5
6 //retrofit
7 implementation 'com.squareup.retrofit2:retrofit:2.4.0'
8 implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
9 implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
10 implementation 'com.squareup.okhttp3:okhttp:3.10.0'
11 implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
12 }
13
Для получения данных криптовалют мы будем работать с АПИ сайта CoinGecko.

Получать список валют будем посредством GET-запроса /coins/markets.

В разделе АПИ по ссылке можно отправить тестовый запрос с необходимыми параметрами.


Нам это нужно для того, чтобы получить представление о построении запроса на сервер и
структуре POJO-файла модели получаемых от сервера данных.

В первом параметре запроса нужно задать целевую валюту, в которой будет отображена
цена криптовалюты, указываем usd.

Из необязательных параметров можно передать идентификаторы требуемых валют —


ничего не указываем, поскольку нам нужно выбрать топ-100 по капитализации из всех
криптовалют.

Следующий параметр — порядок сортировки результатов выдачи. Поскольку рейтинг


криптовалюты у нас будет зависеть от ее рыночной капитализации, значением параметра
order укажем market_cap_desc.

Следующие два параметра — количество валют на страницу выдачи и номер страницы. В


первом пока укажем 10, чтобы уменьшить размер получаемых данных в примере. В
будущем в запросе будем отправлять не 10, а 100, поскольку нам нужен список из 100
криптовалют. Номер страницы указываем 1.

Осталось еще два параметра — sparkline (данные изменения цен во времени) — указываем
false; и процентное изменение цены за указанный период — оставляем пустым.

Жмем выполнить запрос и получаем json-ответ сервера в окошке ниже. Его нужно
скопировать, и вставить на сайт jsonschema2pojo.org для получения модели данных.
Аналогично рассмотрите запрос /coins/{id}/market_chart (на той же странице ниже),
который мы будем отправлять для получения массива цен при построении графика каждой
криптовалюты.

Все три параметра обязательные: идентификатор криптовалюты, валюта цены и глубина


истории данных.

Сервер по этому запросу возвращает три массива: цены, капитализацию и объем торгов,
мы будем использовать только цены.

На основании полученной информации создаем такой kotlin-файл CoinGeckoResponse.kt в


новой подпапке rest главного пакета:

1 package info.fandroid.topcrypts.rest
2
3 data class GeckoCoin(
4 val id: String,
5 val symbol: String,
6 val name: String,
7 val image: String,
8 val current_price: Float,
9 val market_cap: Float,
10 val market_cap_rank: Int,
11 val total_volume: Float,
12 val price_change_percentage_24h: Float,
13 val market_cap_change_percentage_24h: Float,
14 val circulating_supply: Double,
15 val total_supply: Long,
16 val ath: Float,
17 val ath_change_percentage: Float
18 )
19
20 data class GeckoCoinChart (
21 var prices: List<List<Float>>
22 )
23
Здесь два класса данных с необходимыми нам полями, в них будут сохраняться ответы
сервера.

Здесь же создаем интерфейс CoinGeckoApi с функциями для запросов:

1 package info.fandroid.topcrypts.rest
2
3 import io.reactivex.Observable
4 import retrofit2.http.GET
5 import retrofit2.http.Path
6 import retrofit2.http.Query
7
8 interface CoinGeckoApi {
9
10
11
12 //запрос списка криптовалют
13 @GET("coins/markets")
14 fun getCoinMarket(
15 @Query("vs_currency") vs: String = "usd",
16 @Query("per_page") perPage: Int = 100,
17 @Query("sparkline") sparkline: Boolean = false,
18 @Query("order") order: String = "market_cap_desc"
19 ): Observable<List<GeckoCoin>>
20
21
22 //запрос данных для графика
23 @GET("coins/{id}/market_chart")
24 fun getCoinMarketChart(
25 @Path("id") id: String,
26 @Query("vs_currency") vsCurrency: String = "usd",
27 @Query("days") days: String = "max"
28 ): Observable<GeckoCoinChart>
29
30 }
31

Обратите внимание на аннотации библиотеки Retrofit:

@GET — аннотация функции запроса, в скобках указан относительный URL

@Path — переменная для подстановки параметра в URL, в нашем случае id


криптовалюты

@Query — переменные остальных параметров с заданными значениями

Больше информации о возможностях библиотеки можно посмотреть в ее документации.

Теперь, когда мы знаем, с какими данными мы будем работать, мы можем заняться


созданием разметки для списка криптовалют.

Макеты разметки
В папке res/layout создайте файл recycler_view_item.xml с таким содержимым:
1 <?xml version="1.0" encoding="utf-8"?>
2 <android.support.constraint.ConstraintLayout
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:app="http://schemas.android.com/apk/res-auto"
5 xmlns:tools="http://schemas.android.com/tools"
6 android:layout_width="match_parent"
7 android:layout_height="wrap_content">
8
9     <ImageView
10 android:id="@+id/ivCurrencyIcon"
11 android:layout_width="50dp"
12 android:layout_height="50dp"
13 tools:src="@drawable/bitcoin"
14 app:layout_constraintStart_toStartOf="parent"
15 android:layout_marginStart="8dp"
16 app:layout_constraintTop_toTopOf="parent"
17 android:layout_marginTop="12dp"/>
18
19     <TextView
20 android:id="@+id/tvCurrencySym"
21 android:layout_width="wrap_content"
22 android:layout_height="wrap_content"
23 tools:text="BAT"
24 android:textSize="20sp"
25 android:textColor="@android:color/black"
26 app:layout_constraintTop_toTopOf="@+id/ivCurrencyIcon"
27 app:layout_constraintStart_toEndOf="@+id/ivCurrencyIcon"
28 android:layout_marginStart="8dp"
29 app:layout_constraintBottom_toTopOf="@+id/tvCurrencyName"/>
30
31     <TextView
32 android:id="@+id/tvCurrencyName"
33 android:layout_width="wrap_content"
34 android:layout_height="20dp"
35 tools:text="Basic Attention Token"
36 app:layout_constraintTop_toBottomOf="@id/tvCurrencySym"
37 app:layout_constraintStart_toStartOf="@+id/tvCurrencySym"
38 android:layout_marginEnd="16dp"/>
39
40     <TextView
41 android:id="@+id/tvCurrencyMarketCap"
42 android:layout_width="wrap_content"
43 android:layout_height="wrap_content"
44 tools:text="$ 278839000"
45
46 app:layout_constraintEnd_toEndOf="parent"
47 android:layout_marginEnd="8dp"
48 app:layout_constraintBottom_toBottomOf="@+id/tvCurrencySym"
49 android:layout_marginTop="8dp"
50 app:layout_constraintTop_toTopOf="@+id/tvCurrencySym"/>
51     <TextView
52 tools:text="14.4545"
53 android:layout_width="wrap_content"
54 android:layout_height="wrap_content" android:layout_marginEnd="8dp"
55 app:layout_constraintEnd_toEndOf="parent" android:id="@+id/tvCurrencyPrice"
56 app:layout_constraintTop_toTopOf="@+id/tvCurrencyName"/>
57     <TextView
58 android:text="Market Cap "
59 android:layout_width="wrap_content"
60 android:layout_height="wrap_content"
61 android:id="@+id/tvMarketCapRank"
62 app:layout_constraintTop_toTopOf="@+id/tvCurrencyMarketCap"
63 app:layout_constraintBottom_toBottomOf="@+id/tvCurrencyMarketCap"
64 app:layout_constraintEnd_toStartOf="@+id/tvCurrencyMarketCap"
65 app:layout_constraintVertical_bias="0.0"/>
66     <TextView
67 android:text="Price $ "
68 android:layout_width="wrap_content"
69 android:layout_height="wrap_content"
70 android:id="@+id/tvPrice"
71 app:layout_constraintTop_toTopOf="@+id/tvCurrencyPrice"
72 android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="@+id/tvCurrencyPrice"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/tvCurrencyPrice"/>
</android.support.constraint.ConstraintLayout>

На превью должен получиться такой элемент списка:

Приложение будет отображать список из таких элементов, в каждом из которых будет


иконка криптовалюты, символ, имя, рыночная капитализация и цена. Поскольку в ответе
сервера мы будем получать дополнительно также и другие данные, такие как процентное
изменение цены или общее предложение криптовалюты, вы можете расширить макет и
добавить в него дополнительные поля самостоятельно.

Еще один момент касается загрузки иконки криптовалюты. Поскольку иконка приходит в
виде URL, для ее загрузки будем использовать библиотеку Glide, добавьте ее в файл
сборки:

1 //glide
2     implementation 'com.github.bumptech.glide:glide:4.8.0'
3     annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
4
Также добавьте в файл сборки RecyclerView, который мы будем использовать для
отображения списка:

1     implementation 'com.android.support:support-v4:28.0.0'
2     implementation 'com.android.support:recyclerview-v7:28.0.0'
3

Для отображения списка нам понадобится новый фрагмент, создадим его в новой подпапке
fragments главного пакета.

У вас в итоге должен получиться такой пустой фрагмент CurrenciesListFragment:

1 package info.fandroid.top100currencies.fragments
2
3
4 import android.os.Bundle
5 import android.support.v4.app.Fragment
6 import android.view.LayoutInflater
7 import android.view.View
8 import android.view.ViewGroup
9
10 import info.fandroid.top100currencies.R
11
12
13 class CurrenciesListFragment : Fragment() {
14
15     override fun onCreateView(
16 inflater: LayoutInflater, container: ViewGroup?,
17 savedInstanceState: Bundle?
18     ): View? {
19 // Inflate the layout for this fragment
20 return inflater.inflate(R.layout.fragment_currencies_list, container, false)
21     }
22
23
24 }
25
А в папке res/layout появится файл разметки fragment_currencies_list.xml, измените его
таким образом:

1 <?xml version="1.0" encoding="utf-8"?>


2 <android.support.v7.widget.RecyclerView
3 xmlns:android="http://schemas.android.com/apk/res/android"
4 xmlns:tools="http://schemas.android.com/tools"
5 android:id="@+id/list"
6 android:paddingBottom="16dp"
7 android:clipToPadding="false"
8 android:layout_width="match_parent"
9 android:layout_height="match_parent"
10 tools:context=".fragments.CurrenciesListFragment">
11
12
13 </android.support.v7.widget.RecyclerView>
14

Макет главного экрана activity_main.xml измените таким образом:

1 <?xml version="1.0" encoding="utf-8"?>


2
3
4 <android.support.constraint.ConstraintLayout
5 xmlns:android="http://schemas.android.com/apk/res/android"
6 xmlns:tools="http://schemas.android.com/tools"
7 xmlns:app="http://schemas.android.com/apk/res-auto"
8 android:layout_width="match_parent"
9 android:layout_height="match_parent"
10 tools:context=".activities.MainActivity">
11
12 <FrameLayout
13 android:layout_width="match_parent"
14 android:layout_height="match_parent"
15 android:id="@+id/container">
16
17 </FrameLayout>
18
19 <ProgressBar
20 android:id="@+id/progress"
21 tools:background="@android:color/transparent"
22 android:layout_width="match_parent"
23 android:layout_height="match_parent"
24 android:padding="100dp"
25 app:layout_constraintTop_toTopOf="parent"/>
26
27
28 </android.support.constraint.ConstraintLayout>
29

Добавлены два новых компонента: FrameLayout  — контейнер для фрагмента, и


ProgressBar — индикатор загрузки, который будет отображаться в процессе загрузки списка.

Теперь в MainActivity в теле функции onCreate добавьте строки для вызова фрагмента:

1 if (savedInstanceState == null) {
2 supportFragmentManager
3 .beginTransaction()
4 .add(R.id.container, CurrenciesListFragment(), null)
5 .commit()
6 }
7
Адаптер списка
Пришло время создать адаптер списка, который будет связующим звеном между данными
списка и их представлением. Создаем в подпапке adapter главного пакета класс
BaseAdapter:

1 package info.fandroid.top100currencies.adapter
2
3 import android.support.v7.widget.RecyclerView
4 import android.view.View
5
6 //абстрактный базовый класс адаптера
7 abstract class BaseAdapter<VH : BaseAdapter.BaseViewHolder> : RecyclerView.Adapter<VH>() {
8
9 //список элементов списка
10 var items : ArrayList<Any> = ArrayList()
11
12 //возвращающает размер списка
13 override fun getItemCount(): Int {
14 return items.size
15 }
16
17 //связывает views с содержимым
18 override fun onBindViewHolder(holder: VH, position: Int) {
19 holder.bind(getItem(position))
20 }
21
22 //возвращает позицию элемента в списке
23 fun getItem(position: Int): Any {
24 return items[position]
25 }
26
27 //функция добавления одного элемента
28 fun add(newItem: Any) {
29 items.add(newItem)
30 }
31
32 //функция добавления всех элементов
33 fun add(newItems: List<Any>) {
34 items.addAll(newItems)
35 }
36
37 //абстрактный класс ViewHolder
38 abstract class BaseViewHolder(protected val view: View) : RecyclerView.ViewHolder(view)
39 {
40 abstract fun bind(item: Any)
41 }
42 }
Здесь определяем базовые функции и абстрактный вьюхолдер. Этим классом будем
расширять класс адаптера CurrenciesAdapter:

1 package info.fandroid.top100currencies.adapter
2
3 import android.view.LayoutInflater
4 import android.view.View
5 import android.view.ViewGroup
6 import com.bumptech.glide.Glide
7 import info.fandroid.top100currencies.R
8 import kotlinx.android.synthetic.main.recycler_view_item.view.*
9
10 class CurrenciesAdapter : BaseAdapter<CurrenciesAdapter.CurrencyViewHolder>() {
11
12
13 //создает ViewHolder и инициализирует views для списка
14 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CurrencyViewHolder {
15 val v = LayoutInflater.from(parent.context).inflate(R.layout.recycler_view_item,
16 parent, false)
17 return CurrencyViewHolder(v)
18
19 }
20
21 //реализация вьюхолдера
22 class CurrencyViewHolder(view: View) : BaseAdapter.BaseViewHolder(view) {
23
24
25
26 init {
27 //слушатель клика по элементам списка
28 itemView.setOnClickListener {
29
30 }
31 }
32
33
34 //привязываем элементы представления списка к RecyclerView и заполняем данными
35 override fun bind(item: Any) {
36 let {
37 item as Currency
38 Glide.with(view.context).load(item.image).into(view.ivCurrencyIcon)
39 view.tvCurrencySym.text = item.symbol
40 view.tvCurrencyName.text = item.name
41 view.tvCurrencyMarketCap.text = item.marketCap
42 view.tvCurrencyPrice.text = item.price.toString()
43
44 }
45
46
47 }
48 }
49
50 //класс данных для элемента списка
51 data class Currency(
52 val id: String,
53 val symbol: String,
54 val name: String,
55 val image: String,
56 val price: Float,
57 val marketCap: String,
58 val marketCapRank: Int,
59 val totalVolume: Float,
60 val priceChangePercentage24h: Float,
61 val marketCapChangePercentage24h: Float,
62 val circulatingSupply: Double,
63 val totalSupply: Long,
64 val ath: Float,
65 val athChangePercentage: Float
66 )
67 }
Это уже конкретная реализация адаптера, где мы наследуемся от базового адаптера. Такой
подход позволит легко расширять приложение, если вы, например, захотите добавить в
приложение еще несколько списков.

В классе CurrenciesAdapter мы прописали реализацию вьюхолдера и заполнение списка


данными. Слушатель клика по элементам списка пока пустой, его реализацию добавим
позже.

Оценить