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

Это последний урок курса, в котором мы добавим в приложение еще один экран, для

отображения детальной информации о криптовалюте и графика ее цены. Переход на этот


экран будет осуществляться по тапу на каждом элементе списка криптовалют.

Настройка графика
Для визуализации графика криптовалют мы будем использовать библиотеку
MPAndroidChart, которую импортировали на прошлом уроке.

Внимание! Мы используем в проекте версию библиотеки v3.1.0-alpha. Если вы хотите


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

В папке res/layout создаем макет маркера custom_marker_view.xml:

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


2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3 android:layout_width="wrap_content"
4 android:layout_height="wrap_content"
5 android:background="#505050">
6
7     <TextView
8 android:id="@+id/tvContent"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content"
11 android:layout_centerHorizontal="true"
12 android:layout_marginTop="7dp"
13 android:layout_marginLeft="5dp"
14 android:layout_marginRight="5dp"
15 android:layout_marginBottom="7dp"
16 android:text=""
17 android:gravity="center"
18 android:textSize="12sp"
19 android:textColor="@android:color/white"
20 android:textAppearance="?android:attr/textAppearanceSmall" />
21
22
23 </RelativeLayout>

В макете только текстовое поле. Вы можете задать свой фон для корневого элемента
разметки.

В папке chart создаем класс маркера MyMarkerView:

1 package info.fandroid.top100currencies.chart
2
3
4 import android.widget.TextView
5 import android.annotation.SuppressLint
6 import android.content.Context
7 import com.github.mikephil.charting.components.MarkerView
8 import com.github.mikephil.charting.data.Entry
9 import com.github.mikephil.charting.highlight.Highlight
10 import com.github.mikephil.charting.utils.MPPointF
11 import info.fandroid.top100currencies.R
12 import info.fandroid.top100currencies.dateToString
13
14
15 @SuppressLint("ViewConstructor")
16 class MyMarkerView(context: Context, layoutResource: Int) : MarkerView(context,
17 layoutResource) {
18
19     private val tvContent: TextView
20
21     init {
22
23 tvContent = findViewById(R.id.tvContent)
24     }
25
26     // runs every time the MarkerView is redrawn, can be used to update the
27     // content (user-interface)
28     override fun refreshContent(e: Entry, highlight: Highlight) {
29
30
31 tvContent.text = e.y.toString() + "\n" + e.x.dateToString("MMM dd, yyyy")
32
33
34 super.refreshContent(e, highlight)
35     }
36
37     override fun getOffset(): MPPointF {
38 return MPPointF((-(width / 2)).toFloat(), (-height).toFloat())
39     }
}

Класс наследуется от библиотечного класса MarkerView. Конструктор класса принимает


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

Функция  refreshContent принимает объект класса Entry — это запись на графике, может
содержать одно или множество значений. Второй параметр — Highlight — содержит
информацию, необходимую для определения выделенного значения. В теле функции
заполняем текстовое поле маркера значениями графика по осям с применением
необходимого форматирования. Функция getOffset() возвращает положение маркера.

Теперь создадим класс графика LatestChart:

1 package info.fandroid.top100currencies.chart
2
3 import android.content.Context
4 import android.graphics.Color
5 import com.github.mikephil.charting.charts.LineChart
6 import com.github.mikephil.charting.components.XAxis
7 import com.github.mikephil.charting.data.Entry
8 import com.github.mikephil.charting.data.LineData
9 import com.github.mikephil.charting.data.LineDataSet
10 import com.github.mikephil.charting.interfaces.datasets.ILineDataSet
11 import info.fandroid.top100currencies.R
12 import info.fandroid.top100currencies.di.App
13 import info.fandroid.top100currencies.formatters.YearValueFormatter
14 import javax.inject.Inject
15
16 class LatestChart {
17
18     @Inject
19     lateinit var context: Context
20
21     @Inject
22     lateinit var formatter: YearValueFormatter
23
24     lateinit var chart: LineChart
25
26
27     init {
28 App.appComponent.inject(this)
29     }
30
31     fun initChart(chart: LineChart) {
32 this.chart = chart
33
34
35 // enable description text
36 chart.description.isEnabled = false
37
38 // enable touch gestures
39 chart.setTouchEnabled(true)
40
41 // enable scaling and dragging
42 chart.isDragEnabled = true
43 chart.setScaleEnabled(false)
44 chart.isScaleXEnabled = true
45 chart.setDrawGridBackground(false)
46 chart.isDoubleTapToZoomEnabled = false
47
48 // if disabled, scaling can be done on x- and y-axis separately
49 chart.setPinchZoom(false)
50
51 //Sets the maximum distance in screen dp a touch can be away from an entry to
52 cause it to get highlighted
53 chart.maxHighlightDistance = 300F
54
55 val data = LineData()
56 data.setValueTextColor(Color.BLACK)
57
58 // add empty data
59 chart.data = data
60
61 // get the legend (only possible after setting data)
62 chart.legend.isEnabled = true
63
64
65 //add marker
66 chart.setDrawMarkers(true)
67 chart.marker =
68 MyMarkerView(context, R.layout.custom_marker_view)
69
70
71 val xl = chart.xAxis
72 xl.textColor = Color.BLACK
73 xl.position = XAxis.XAxisPosition.BOTTOM
74 xl.setDrawGridLines(false)
75 xl.valueFormatter = formatter
76 xl.labelCount = 3
77 xl.granularity = 48F
78
79
80 xl.setAvoidFirstLastClipping(true)
81 xl.isEnabled = true
82
83 val leftAxis = chart.axisLeft
84 leftAxis.textColor = Color.BLACK
85 leftAxis.setDrawGridLines(true)
86
87 val rightAxis = chart.axisRight
88 rightAxis.isEnabled = true
89
90     }
91
92     //добавление данных на график
93     fun addEntry(value: Float, date: Float) {
94 val data = chart.data
95
96 if (data != null) {
97
98 var set: ILineDataSet? = data.getDataSetByIndex(0)
99
100 if (set == null) {
101 set = createSet()
102 data.addDataSet(set)
103 }
104
105
106 data.addEntry(Entry(date, value), 0)
107 data.notifyDataChanged()
108
109 chart.notifyDataSetChanged()
110
111
112 chart.moveViewToX(date)
113
114 chart.highlightValue(date, 0)
115 }
116     }
117
118     //создание и настройка набора данных
119     private fun createSet(): LineDataSet {
120 val set = LineDataSet(null, "Price, USD")
121
122 set.mode = LineDataSet.Mode.CUBIC_BEZIER
123 set.cubicIntensity = 0.2f
124 set.setDrawFilled(true)
125 set.setDrawCircles(false)
126 set.lineWidth = 1.8f
127 set.circleRadius = 4f
128 set.setCircleColor(Color.BLACK)
129 set.highlightLineWidth = 1.2f
130 set.highLightColor = context.resources.getColor(R.color.colorAccent)
131 set.color = Color.BLACK
132 set.fillColor = Color.BLACK
133 set.enableDashedHighlightLine(10f, 5f, 0f)
134 set.fillAlpha = 100
135 set.setDrawHorizontalHighlightIndicator(true)
136 set.setFillFormatter { _, _ ->
137 chart.axisLeft.axisMinimum
138 }
139 return set
140     }
141
}

Инжектим контекст и форматтер, объявляем объект класса LineChar для построения


графика. Инициализируем AppComponent Даггера.

Функция initChart инициализирует график. Здесь можно изменять такие параметры графика,
как описание, поддержку жестов, масштабирование и перетаскивание, причем
масштабирование может выполняться только по одной из осей. Больше параметров
смотрите в документации.

Далее объявляем объект класса LineData, который инкапсулирует все данные для графика.
Он пока пуст. Устанавливаем цвет отрисовки данных и отдаем объект графику.

Затем определяем опцию отрисовки легенды, функцией setDrawMarkers включаем опцию


отрисовки маркера, инициализируем маркер.

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

Функция addEntry добавляет данные на график, используя объект интерфейса ILineDataSet.


Заполняются данные для графика и вызывается notifyDataChanged() для уведомления об
изменении данных. Вызов chart.moveViewToX(date) смещает окно графика в нужную
позицию по оси x и инициирует обновление графика. Візов chart.highlightValue(date, 0)
Выделяет значение y при данном значении x в данном наборе данных.

Функция createSet() настраивает и возвращает объект класса LineDataSet. Каждый DataSet


объект представляет группу записей (например, класс Entry) внутри графика, которые
принадлежат друг другу. Он предназначен для логического разделения различных групп
значений на графике. Мы здесь создаем одну группу значений — это цены с привязкой ко
времени. Можно создать несколько групп значений, например, объем торгов или
капитализацию во времени, и разместить на этом же графике.

Вы можете экспериментировать с настройками группы значений, добавлять свои группы


значений и т.д.
Осталось добавить график в AppComponent:

1 fun inject(chart: LatestChart)

Теперь его можно использовать в приложении.

Создание экрана для графика


В файл strings.xml в папке res/values добавим строковые ресурсы:

1 <string name="market_cap_rank_text">Market Cap Rank</string>


2     <string name="mc_hange_in_24h_text">MC сhange in 24h, %</string>
3     <string name="all_time_high_ath_text">All Time High (ATH)</string>
4     <string name="ath_change_text">ATH change, %</string>
5     <string name="circulating_supply_text">Circulating Supply</string>
6     <string name="total_supply_text">Total Supply</string>
7

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


Начнем с макета activity_chart.xml в папке res/layout:

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="match_parent">
8
9
10     <com.github.mikephil.charting.charts.LineChart
11 android:id="@+id/chartCurrency"
12 android:layout_width="match_parent"
13 android:layout_height="0dp"
14 app:layout_constraintEnd_toEndOf="parent"
15 app:layout_constraintStart_toStartOf="parent"
16 app:layout_constraintTop_toBottomOf="@+id/totalSupply"
17 app:layout_constraintHorizontal_bias="0.0"
18 android:layout_marginStart="8dp"
19 android:layout_marginEnd="8dp"
20 app:layout_constraintBottom_toBottomOf="parent"
21 android:layout_marginBottom="8dp"/>
22
23     <ProgressBar
24 android:id="@+id/progressChart"
25 tools:background="@android:color/transparent"
26 android:layout_width="match_parent"
27 android:layout_height="0dp"
28 android:padding="100dp"
29 app:layout_constraintStart_toStartOf="parent"
30 app:layout_constraintBottom_toBottomOf="@+id/chartCurrency"
31 app:layout_constraintTop_toBottomOf="@+id/totalSupply"/>
32
33
34     <ImageView
35 android:layout_width="72dp"
36 android:layout_height="72dp"
37 android:id="@+id/ivCurrencyDetailIcon"
38 tools:src="@drawable/bitcoin" app:layout_constraintTop_toTopOf="parent"
39 android:layout_marginTop="16dp" app:layout_constraintStart_toStartOf="parent"
40 android:layout_marginStart="8dp"/>
41
42
43     <TextView
44 android:text="@string/market_cap_rank_text"
45 android:layout_width="wrap_content"
46 android:layout_height="wrap_content"
47 android:id="@+id/marketCap"
48 app:layout_constraintStart_toEndOf="@+id/ivCurrencyDetailIcon"
49 android:layout_marginStart="8dp"
50 android:layout_marginTop="16dp"
51 app:layout_constraintTop_toTopOf="parent"/>
52     <TextView
53 android:text="@string/mc_hange_in_24h_text"
54 android:layout_width="wrap_content"
55 android:layout_height="19dp"
56 android:id="@+id/marketCapChange"
57 app:layout_constraintStart_toStartOf="@+id/marketCap"
58 app:layout_constraintTop_toBottomOf="@+id/marketCap"
59 android:layout_marginTop="2dp"/>
60     <TextView
61 android:text="@string/all_time_high_ath_text"
62 android:layout_width="wrap_content"
63 android:layout_height="wrap_content"
64 android:id="@+id/ath"
65 app:layout_constraintStart_toStartOf="@+id/marketCap"
66 app:layout_constraintTop_toBottomOf="@+id/marketCapChange"
67 android:layout_marginTop="2dp"/>
68     <TextView
69 android:text="@string/ath_change_text"
70 android:layout_width="wrap_content"
71 android:layout_height="wrap_content"
72 android:id="@+id/athChangePercentage"
73 app:layout_constraintStart_toStartOf="@+id/marketCap"
74 app:layout_constraintTop_toBottomOf="@+id/ath"
75 android:layout_marginTop="2dp"/>
76     <TextView
77 android:text="@string/circulating_supply_text"
78 android:layout_width="wrap_content"
79 android:layout_height="wrap_content"
80 android:id="@+id/circulatingSupply"
81 app:layout_constraintTop_toBottomOf="@+id/athChangePercentage"
82 app:layout_constraintStart_toStartOf="@+id/marketCap"
83 android:layout_marginTop="2dp"/>
84     <TextView
85 android:text="@string/total_supply_text"
86 android:layout_width="wrap_content"
87 android:layout_height="wrap_content"
88 android:id="@+id/totalSupply"
89 app:layout_constraintTop_toBottomOf="@+id/circulatingSupply"
90 app:layout_constraintStart_toStartOf="@+id/marketCap"
91 android:layout_marginTop="2dp"/>
92     <TextView
93 android:text=""
94 android:layout_width="wrap_content"
95 android:layout_height="wrap_content"
96 app:layout_constraintTop_toTopOf="parent"
97 android:layout_marginTop="16dp"
98 android:id="@+id/tvDetailMarketCapRank"
99 app:layout_constraintEnd_toEndOf="parent"
100 android:layout_marginEnd="16dp"/>
101     <TextView
102 android:layout_width="wrap_content"
103 android:layout_height="wrap_content"
104 android:id="@+id/tvMarketCapChange"
105 app:layout_constraintEnd_toEndOf="parent"
106 android:layout_marginEnd="16dp" android:layout_marginTop="2dp"
107 app:layout_constraintTop_toBottomOf="@+id/tvDetailMarketCapRank"/>
108     <TextView
109 android:layout_width="wrap_content"
110 android:layout_height="wrap_content"
111 android:id="@+id/tvATH"
112 app:layout_constraintEnd_toEndOf="parent"
113 android:layout_marginEnd="16dp" android:layout_marginTop="2dp"
114 app:layout_constraintTop_toBottomOf="@+id/tvMarketCapChange"/>
115     <TextView
116 android:layout_width="wrap_content"
117 android:layout_height="wrap_content"
118 android:id="@+id/tvAthChange"
119 app:layout_constraintEnd_toEndOf="parent"
120 android:layout_marginEnd="16dp"
121 android:layout_marginTop="2dp"
122 app:layout_constraintTop_toBottomOf="@+id/tvATH"/>
123     <TextView
124 android:layout_width="wrap_content"
125 android:layout_height="wrap_content"
126 android:id="@+id/tvCirculatingSupply"
127 app:layout_constraintEnd_toEndOf="parent"
128 android:layout_marginEnd="16dp"
129 android:layout_marginTop="2dp"
130 app:layout_constraintTop_toBottomOf="@+id/tvAthChange"/>
131     <TextView
132 android:layout_width="wrap_content"
133 android:layout_height="wrap_content"
134 android:id="@+id/tvTotalSupply"
135 app:layout_constraintEnd_toEndOf="parent"
136 android:layout_marginEnd="16dp"
137 android:layout_marginTop="2dp"
138 app:layout_constraintTop_toBottomOf="@+id/tvCirculatingSupply"/>
139
140
141 </android.support.constraint.ConstraintLayout>

Здесь экранный компонент библиотеки для отрисовки окна графика


com.github.mikephil.charting.charts.LineChart, индикатор загрузки, поле для иконки
криптовалюты и текстовые поля для различных данных о криптовалюте, а также подписей к
этим полям.

Версия макета activity_chart.xml для ландшафтной ориентации экрана в папке res/layout-


land:

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="match_parent">
8
9     <ImageView
10 android:layout_width="124dp"
11 android:layout_height="124dp"
12 android:id="@+id/ivCurrencyDetailIcon"
13 tools:src="@drawable/bitcoin"
14 app:layout_constraintTop_toTopOf="@+id/progressChart"
15 android:layout_marginTop="16dp"
16 app:layout_constraintStart_toStartOf="parent"
17 android:layout_marginStart="8dp"
18 app:layout_constraintBottom_toTopOf="@+id/chartCurrency"
19 app:layout_constraintVertical_bias="0.0"
20     />
21
22     <com.github.mikephil.charting.charts.LineChart
23 android:id="@+id/chartCurrency"
24 android:layout_width="0dp"
25 android:layout_height="0dp"
26 android:layout_marginTop="8dp"
27 app:layout_constraintTop_toTopOf="@+id/progressChart"
28 app:layout_constraintStart_toEndOf="@+id/tvMarketCapChange"
29 android:layout_marginStart="8dp"
30 app:layout_constraintEnd_toEndOf="parent"
31 android:layout_marginEnd="8dp"
32 android:layout_marginBottom="8dp"
33 app:layout_constraintBottom_toBottomOf="parent"/>
34
35     <ProgressBar
36 android:id="@+id/progressChart"
37 tools:background="@android:color/transparent"
38 android:layout_width="0dp"
39 android:layout_height="match_parent"
40 android:padding="100dp"
41 app:layout_constraintStart_toEndOf="@+id/tvMarketCapChange"
42 app:layout_constraintEnd_toEndOf="parent" android:layout_marginStart="8dp"/>
43
44     <TextView
45 android:text="@string/market_cap_rank_text"
46 android:layout_width="wrap_content"
47 android:layout_height="wrap_content"
48 android:id="@+id/marketCap"
49 app:layout_constraintStart_toStartOf="parent"
50 android:layout_marginTop="8dp"
51 app:layout_constraintTop_toBottomOf="@+id/ivCurrencyDetailIcon"
52 android:layout_marginStart="8dp"/>
53     <TextView
54 android:text="@string/mc_hange_in_24h_text"
55 android:layout_width="wrap_content"
56 android:layout_height="19dp"
57 android:id="@+id/marketCapChange"
58 app:layout_constraintStart_toStartOf="@+id/marketCap"
59 app:layout_constraintTop_toBottomOf="@+id/marketCap"
60 android:layout_marginTop="2dp"/>
61     <TextView
62 android:text="@string/all_time_high_ath_text"
63 android:layout_width="wrap_content"
64 android:layout_height="wrap_content"
65 android:id="@+id/ath"
66 app:layout_constraintStart_toStartOf="@+id/marketCap"
67 app:layout_constraintTop_toBottomOf="@+id/marketCapChange"
68 android:layout_marginTop="2dp"/>
69     <TextView
70 android:text="@string/ath_change_text"
71 android:layout_width="wrap_content"
72 android:layout_height="wrap_content"
73 android:id="@+id/athChangePercentage"
74 app:layout_constraintStart_toStartOf="@+id/marketCap"
75 app:layout_constraintTop_toBottomOf="@+id/ath"
76 android:layout_marginTop="2dp"/>
77     <TextView
78 android:text="@string/circulating_supply_text"
79 android:layout_width="wrap_content"
80 android:layout_height="wrap_content"
81 android:id="@+id/circulatingSupply"
82 app:layout_constraintTop_toBottomOf="@+id/athChangePercentage"
83 app:layout_constraintStart_toStartOf="@+id/marketCap"
84 android:layout_marginTop="2dp"/>
85     <TextView
86 android:text="@string/total_supply_text"
87 android:layout_width="wrap_content"
88 android:layout_height="wrap_content"
89 android:id="@+id/totalSupply"
90 app:layout_constraintTop_toBottomOf="@+id/circulatingSupply"
91 app:layout_constraintStart_toStartOf="@+id/marketCap"
92 android:layout_marginTop="2dp"/>
93     <TextView
94 android:text=""
95 android:layout_width="wrap_content"
96 android:layout_height="wrap_content"
97 android:id="@+id/tvDetailMarketCapRank"
98 android:layout_marginTop="8dp"
99 app:layout_constraintTop_toBottomOf="@+id/ivCurrencyDetailIcon"
100 app:layout_constraintStart_toEndOf="@+id/marketCap"
101 android:layout_marginStart="8dp"/>
102     <TextView
103 android:layout_width="wrap_content"
104 android:layout_height="wrap_content"
105 android:id="@+id/tvMarketCapChange"
106 android:layout_marginTop="2dp"
107 app:layout_constraintTop_toBottomOf="@+id/tvDetailMarketCapRank"
108 app:layout_constraintStart_toEndOf="@+id/marketCapChange"
109 android:layout_marginStart="16dp"/>
110     <TextView
111 android:layout_width="wrap_content"
112 android:layout_height="wrap_content"
113 android:id="@+id/tvATH"
114 android:layout_marginTop="2dp"
115 app:layout_constraintTop_toBottomOf="@+id/tvMarketCapChange"
116 app:layout_constraintStart_toEndOf="@+id/ath"
117 android:layout_marginStart="16dp"/>
118     <TextView
119 android:layout_width="wrap_content"
120 android:layout_height="wrap_content"
121 android:id="@+id/tvAthChange"
122 android:layout_marginTop="2dp"
123 app:layout_constraintTop_toBottomOf="@+id/tvATH"
124 app:layout_constraintStart_toEndOf="@+id/athChangePercentage"
125 android:layout_marginStart="16dp"/>
126     <TextView
127 android:layout_width="wrap_content"
128 android:layout_height="wrap_content"
129 android:id="@+id/tvCirculatingSupply"
130 android:layout_marginTop="2dp"
131 app:layout_constraintTop_toBottomOf="@+id/tvAthChange"
132 app:layout_constraintStart_toEndOf="@+id/circulatingSupply"
133 android:layout_marginStart="16dp"/>
    <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/tvTotalSupply"
android:layout_marginTop="2dp"
app:layout_constraintTop_toBottomOf="@+id/tvCirculatingSupply"
app:layout_constraintStart_toEndOf="@+id/totalSupply"
android:layout_marginStart="16dp"/>

</android.support.constraint.ConstraintLayout>

Здесь все те же поля и компоненты, отличается только их расположение.

В папке activities создаем новое активити ChartActivity:

1 package info.fandroid.top100currencies.activities
2
3 import android.support.v7.app.AppCompatActivity
4 import android.os.Bundle
5 import android.view.View
6 import android.widget.Toast
7 import com.bumptech.glide.Glide
8 import com.github.mikephil.charting.data.Entry
9 import com.github.mikephil.charting.highlight.Highlight
10 import com.github.mikephil.charting.listener.OnChartValueSelectedListener
11 import info.fandroid.top100currencies.R
12 import info.fandroid.top100currencies.chart.LatestChart
13 import info.fandroid.top100currencies.di.App
14 import info.fandroid.topcrypts.mvp.contract.LatestChartContract
15 import info.fandroid.topcrypts.mvp.presenter.LatestChartPresenter
16 import kotlinx.android.synthetic.main.activity_chart.*
17 import java.text.DecimalFormat
18 import javax.inject.Inject
19
20 class ChartActivity : AppCompatActivity(), OnChartValueSelectedListener,
21 LatestChartContract.View {
22
23
24     @Inject
25     lateinit var latestChart: LatestChart
26
27     @Inject
28     lateinit var presenter: LatestChartPresenter
29
30
31     override fun onCreate(savedInstanceState: Bundle?) {
32 super.onCreate(savedInstanceState)
33 setContentView(R.layout.activity_chart)
34
35 App.appComponent.inject(this)
36 presenter.attach(this)
37
38 supportActionBar?.setDisplayHomeAsUpEnabled(true)
39
40 val name = intent.getStringExtra("name")
41 val marketCapRank = intent.getIntExtra("marketCapRank", 0)
42 val symbol = intent.getStringExtra("symbol")
43 val marketCap = intent.getStringExtra("marketCap")
44 val marketCapChangePercentage24h =
45 intent?.getFloatExtra("marketCapChangePercentage24h", 0.0f)
46 val priceChangePercentage24h = intent?.getFloatExtra("priceChangePercentage24h",
47 0.0f)
48 val totalVolume = intent?.getFloatExtra("totalVolume", 0.0f)
49 val ath = intent?.getFloatExtra("ath", 0.0f)
50 val athChangePercentage = intent?.getFloatExtra("athChangePercentage", 0.0f)
51 val circulatingSupply = intent?.getDoubleExtra("circulatingSupply", 0.0)
52 val totalSupply = intent?.getLongExtra("totalSupply", 0)
53 val image = intent.getStringExtra("image")
54
55 Glide.with(this).load(image).into(ivCurrencyDetailIcon)
56
57 supportActionBar?.title = name
58
59 val df = DecimalFormat("#")
60 df.maximumFractionDigits = 2
61
62 tvDetailMarketCapRank.text = marketCapRank.toString()
63 tvMarketCapChange.text = marketCapChangePercentage24h.toString()
64 tvATH.text = ath.toString()
65 tvAthChange.text = df.format(athChangePercentage)
66 tvCirculatingSupply.text = df.format(circulatingSupply)
67 tvTotalSupply.text = totalSupply.toString()
68
69
70
71 presenter.makeChart(intent.getStringExtra("id"))
72
73 latestChart.initChart(chartCurrency)
74     }
75
76
77
78     override fun onNothingSelected() {
79 TODO("not implemented") //To change body of created functions use File | Settings
80 | File Templates.
81     }
82
83     override fun onValueSelected(e: Entry?, h: Highlight?) {
84 TODO("not implemented") //To change body of created functions use File | Settings
85 | File Templates.
86     }
87
88     override fun addEntryToChart(date: Float, value: Float) {
89
90 latestChart.addEntry(value, date)
91     }
92
93     override fun addEntryToChart(value: Float, date: String) {
94 TODO("not implemented") //To change body of created functions use File | Settings
95 | File Templates.
96     }
97
98     override fun showProgress() {
99 progressChart.visibility = View.VISIBLE
100     }
101
102     override fun hideProgress() {
103 progressChart.visibility = View.INVISIBLE
104     }
105
106     override fun showErrorMessage(error: String?) {
107 Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
108     }
109
110     override fun refresh() {
111 TODO("not implemented") //To change body of created functions use File | Settings
112 | File Templates.
113     }
114
115
116 override fun onResume() {
117 super.onResume()
118 presenter.attach(this)
119     }
120
121     override fun onPause() {
super.onPause()
presenter.detach()
    }

Имплементируем интерфейсы OnChartValueSelectedListener, LatestChartContract.View


библиотеки для графика, реализуем их методы. Инжектим LatestChart и
LatestChartPresenter.  В методе onCreate отдаем презентеру ссылку на текущее активити, и
создаем переменные со значениями, полученными из интента. Эти значения в интент мы
будем передавть по клику в адаптере. При помощи библиотеки Glide загружаем
изображение иконки криптовалюты. Заполняем поля макета экрана, применяя
форматирование. Затем через презентер создаем график и инициализируем его.

Из переопределенных функций интерфейсов мы используем addEntryToChart() для


добавления данных на график, showProgress() и hideProgress() для отображения и скрытия
прогрессбара соответственно, и showErrorMessage() для отображения ошибки.

Чтобы презентер не пересоздавался вместе с активити при повороте экрана, отсоединяем


его в onPause() и присоединяем в onResume().

Не забудьте прописать ChartActivity в файле AndroidManifest.xml с указанием MainActivity в


качестве значения параметра android:parentActivityName:

1 <activity
2 android:name=".activities.ChartActivity"
3 android:parentActivityName=".activities.MainActivity">
4 </activity>

Соединяем все вместе


Добавьте ChartActivity в AppComponent:

1 fun inject(activity: ChartActivity)

Пропишите функции в ChartModule:

Теперь в классе CurrenciesAdapter во внутреннем классе CurrencyViewHolder объявим


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

1 var id: String = ""


2 var symbol: String = ""
3 var name: String = ""
4 var image: String = ""
5 var marketCap: String = ""
6 var marketCapRank: Int = 0
7 var marketCapChangePercentage24h: Float = 0.0f
8 var priceChangePercentage24h: Float = 0.0f
9 var totalVolume: Float = 0.0f
10 var ath: Float = 0.0f
11 var athChangePercentage: Float = 0.0f
12 var circulatingSupply: Double = 0.0
13 var totalSupply: Long = 0

Допишем обработчик клика на элементе списка setOnClickListener:

1 init {
2 //слушатель клика по элементам списка
3 itemView.setOnClickListener {
4 var intent = Intent(itemView.context, ChartActivity::class.java)
5 intent.putExtra("id", id)
6 .putExtra("name", name)
7 .putExtra("symbol", symbol)
8 .putExtra("image", image)
9 .putExtra("marketCapRank", marketCapRank)
10 .putExtra("marketCap", marketCap)
11 .putExtra("marketCapChangePercentage24h", marketCapChangePercentage24h)
12 .putExtra("priceChangePercentage24h", priceChangePercentage24h)
13 .putExtra("totalVolume", totalVolume)
14 .putExtra("ath", ath)
15 .putExtra("athChangePercentage", athChangePercentage)
16 .putExtra("circulatingSupply", circulatingSupply)
17 .putExtra("totalSupply", totalSupply)
18 itemView.context.startActivity(intent)
19 }
20 }

Здесь мы создаем интент и сохраняем в него переменные для полей экрана


дополнительной информации о криптовалюте.

Осталось изменить функцию bind, дописать в нее присвоение вышеуказанным переменным


данных из внутреннего класса Currency:

1 //привязываем элементы представления списка к RecyclerView и заполняем данными


2 override fun bind(item: Any) {
3 let {
4 item as Currency
5 Glide.with(view.context).load(item.image).into(view.ivCurrencyIcon)
6 view.tvCurrencySym.text = item.symbol
7 view.tvCurrencyName.text = item.name
8 view.tvCurrencyMarketCap.text = item.marketCap
9 view.tvCurrencyPrice.text = item.price.toString()
10 id = item.id
11 symbol = item.symbol
12 name = item.name
13 image = item.image
14 marketCapRank = item.marketCapRank
15 marketCapChangePercentage24h = item.marketCapChangePercentage24h
16 priceChangePercentage24h = item.priceChangePercentage24h
17 totalVolume = item.totalVolume
18 ath = item.ath
19 athChangePercentage = item.athChangePercentage
20 circulatingSupply = item.circulatingSupply
21 totalSupply = item.totalSupply
22
23 }
24
25
26 }

Запускаем приложение
Запустите приложение на эмуляторе или устройстве. Отобразится список, все элементы
которого кликабельны. При клике открывается экран дополнительной информации о
криптовалюте с графиком цены.