#スマホアプリとグラフ
Androidアプリでグラフを表示させたい場合、
MPAndroidChart
というライブラリがよく使われます。 (iOS版のChartsライブラリも存在)
多機能かつUIも優れた素晴らしいライブラリですが、
日本語情報の不足もあり、実装難度は結構高いと感じます。
そこで、簡易にグラフを作成するための追加パッケージを作成してみました!
Github
下のようなグラフを簡単に作る事ができます
なお、本パッケージはKotlinでの実装を前提としておりますが、
希望があればJava・Swift(iOS)バージョンも作ろうと思います。
その他質問、メソッド追加要望等あれば、気軽にコメント頂けますとありがたいです!
##MPAndroidChartをより使いやすくするには?
MPAndroidChartでグラフを含んだアプリを作成して、私が苦戦したのは以下の部分です。
1. 時系列グラフの作成
2. フォーマット指定(特に色)
3. ツールヒントの作成
4. 日本語ドキュメントが少ない
##1. 時系列グラフの作成
詳しくは補足に書きますが、時系列グラフの作成にはデータの格納や軸のフォーマット等、相当な手間が掛かります。
###本パッケージでの対応
時系列グラフ専用のメソッドを作成し、簡単な操作で作成できるようにしました。
また、下記のように時間が等間隔でない場合、
val x = listOf<Date>(
sdf.parse("2020/09/01 00:00:00"),
sdf.parse("2020/09/01 06:00:00"),
sdf.parse("2020/09/01 12:00:00"),
sdf.parse("2020/09/01 18:00:00"),
sdf.parse("2020/09/02 00:00:00"),
sdf.parse("2020/09/02 06:00:00"),
sdf.parse("2020/09/02 12:00:00"),
sdf.parse("2020/09/03 18:00:00"),//ここのみ時間間隔が飛んでいる
)
上記の方法ではインデックス情報のみに基づいて等間隔にプロットされてしまい、
時間の間隔が横軸上で正確に表現されません。
本パッケージでは、横軸が等間隔でない場合も正確にプロットされるような表示モードも準備しました
(ラベルは最初と最後のみ表示されます)
##2. UIフォーマット指定
MPAndroidChartでは、グラフ表示のためにおおざっぱに
①データを入力する処理
②UIフォーマットを指定する処理
の2種類が必要となります。
コード上でもこの2種類の処理をまとめて別個に指定できることが、独立性の観点から望ましいです。
しかし折れ線グラフのように複数のY軸の値を指定する場合、
下のように①と②に処理が入り組んだ指定方法となってしまいます。
//Entryにデータ格納 → ①データ入力処理
var entryList1 = mutableListOf<Entry>()//1本目の線
var entryList2 = mutableListOf<Entry>()//2本目の線
for(i in x.indices){
entryList1.add(
Entry(x[i], y1[i])
)
entryList2.add(
Entry(x[i], y2[i])
)
}
//X軸の設定 → ②UIフォーマット指定処理
lineChart.xAxis.apply {
isEnabled = true
textColor = Color.BLACK
}
//左Y軸の設定 → ②UIフォーマット指定処理
lineChart.axisLeft.apply {
isEnabled = true
textColor = Color.BLACK
}
//右Y軸の設定 → ②UIフォーマット指定処理
lineChart.axisLeft.apply {
isEnabled = false
}
//LineDataSet(線1本ごとの)のリストを作成 → ①データ入力処理
val lineDataSets = mutableListOf<ILineDataSet>()
//線1本目のデータ格納 → ①データ入力処理
val lineDataSet1 = LineDataSet(entryList1, "linear")
//線1本目の色 → ②UIフォーマット指定処理
lineDataSet1.color = Color.BLUE
//リストに格納 → ①データ入力処理
lineDataSets.add(lineDataSet1)
//線2本目のデータ格納 → ①データ入力処理
val lineDataSet2 = LineDataSet(entryList2, "square")
//線2本目の色 → ②UIフォーマット指定処理
lineDataSet2.color = Color.RED
//LineDataにLineDataSetのリスト格納 → ①データ入力処理
lineDataSets.add(lineDataSet2)
//LineChartにData格納 → ①データ入力処理
lineChart.data = LineData(lineDataSets)
###本パッケージでの対応
上記のような指定法は独立性やコードの可読性の観点から好ましい状態ではないので、
・UIフォーマット指定用クラス(ChartとDataSetの2種類。両者の違いはこちら参照)
・データ入力用メソッド
・上記UI指定&データを基にグラフ描画するメソッド
の順で、独立して指定できるような構成としました。
また、**フォーマット指定ようわからん!**という方のために、
フォーマット指定をしなかった(コンストラクタに引数を入れない)場合も、
私の主観でいい感じ(抽象的な表現ですが‥笑)に設定してくれるような機能を加えています。
特に色設定は設定箇所が多く、手動設定が面倒なので、
・カラーユニバーサルデザインに基づき、線や棒の色を自動指定
・背景が黒(輝度が0.5以下)のときは、文字を白に自動変更
という機能を追加しました
##3. ツールヒント作成
データ点をタップした際にデータの詳細を表示してくれる「ツールヒント」
があると、UIの見やすさが格段に向上します。
しかし公式ドキュメントにも実装法がまともに記載されておらず。
サンプルコードを見ながら手探りでの実装が求められます。
###本パッケージでの対応
簡単にツールヒントを表示できるよう、フォーマット指定用クラスで下記の指定を可能としました
A. ツールヒント表示の有無
ツールヒントの表示有無を指定します
B. 表示するデータの軸方向(X、Y、XY両方)
下の図のように表示するデータの軸方向を選択します(左から表示なし、Xのみ、Yのみ、XY両方)
C. 時系列グラフの場合、時刻表示のフォーマット(例:"M/d HH:mm")
X軸が時系列のとき、時刻表示のフォーマットを指定できるようにしました。
下の図では、"d日H時"というフォーマットを指定しています
D. データに付与する単位(例:℃、%、Paなど)
X軸、Y軸ともに、表示するデータに単位を付加できます。
下の図では、Y軸に"円"という単位を付加しています
##4. 日本語ドキュメントが少ない
日本語で網羅的に解説した記事は皆無といっても良い状況です。
特にUIフォーマット指定系は公式英語ドキュメントに説明が記載されていないメソッド・プロパティも多いです。
###本パッケージでの対応
本記事に、UIフォーマット指定プロパティの一覧と、指定による変化を図示したリンクを記載しました
(DataSetのUIフォーマット指定プロパティ一覧はこちら)
どのプロパティを設定すれば、どのようにグラフ形状が変わるかを、
日本語+画像で簡便に追えるかと思います
#必要なもの
下記の開発環境を構築してください
・開発用のPC(今回はWindows10を使用)
・Android Studio(今回は4.0.1を使用、AndroidバージョンはAPI26以降推奨)
・動作させるAndroidスマホ(今回はPixel 3aを使用)
#導入方法
下記のような手順となります
1. MPAndroidChartの導入
2. CustomMPAndroidChartパッケージの導入
##1. MPAndroidChartの導入
プロジェクト内に、グラフ描画ライブラリであるMPAndroidChartを導入します
build.grandle(Project)に、
allprojects {
repositories{
:
maven { url 'https://jitpack.io' }
:
build.grandle(Module:app)に、
dependencies {
:
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
:
メニューバーの「File → Close Project」でプロジェクトを閉じて開き直すと、
ライブラリが反映されます。
##2. CustomMPAndroidChartパッケージの導入
上記MPAndroidChartのグラフを簡易的に作成するためのメソッド・クラス集を、
「CustomMPAndroidChart」としてパッケージ化しました。
Githubにもアップロードしております
###パッケージ概要
下記の6つのモジュールからなります
・LineChartMethods.kt:折れ線グラフ用メソッド集
・BarChartMethods.kt:棒グラフ用メソッド集
・CandleStickChartMethods.kt:ローソク足グラフ(株価のチャートのようなグラフ)用メソッド集
・PieChartMethods.kt:折れ線グラフ用メソッド集
・MarkerViews.kt:ツールヒント表示用クラス
・ChartFormats.kt:UIフォーマット指定用クラスを集めたモジュール
また、ツールヒントで使用するレイアウトファイル
simple_marker_view.xmlの導入も必要となります。
###パッケージの導入方法
グラフを作成するプロジェクト内に、下記の手順で導入します。
※手動操作が多いので、より良い提供方法ご存知であればご教示いただけるとありがたいです
javaフォルダ直下のパッケージフォルダを右クリックし、New → Packageを選択し、"chart"と名前をつけます
####折れ線グラフ描画用モジュールの作成
上記作成したchartフォルダを右クリックし、New → Kotlin File/Classを選択し、"LineChartMethods"と名前を付け、GitHub上のLineChartMethods.ktをコピペします。
※コード上部の「プロジェクト構成に合わせ変更」とコメントしてある部分は、プロジェクト構成に合わせて適宜修正してください
####棒グラフ描画用モジュールの作成
上記作成したchartフォルダを右クリックし、New → Kotlin File/Classを選択し、"BarChartMethods"と名前を付け、GitHub上のBarChartMethods.ktをコピペします。
※コード上部の「プロジェクト構成に合わせ変更」とコメントしてある部分は、プロジェクト構成に合わせて適宜修正してください
####ローソク足グラフ描画用モジュールの作成
上記作成したchartフォルダを右クリックし、New → Kotlin File/Classを選択し、"CandleStickChartMethods"と名前を付け、GitHub上のCandleStickChartMethods.ktをコピペします。
※コード上部の「プロジェクト構成に合わせ変更」とコメントしてある部分は、プロジェクト構成に合わせて適宜修正してください
円グラフ描画用モジュールの作成
上記作成したchartフォルダを右クリックし、New → Kotlin File/Classを選択し、"PieChartMethods"と名前を付け、GitHub上のPieChartMethods.ktをコピペします。
※コード上部の「プロジェクト構成に合わせ変更」とコメントしてある部分は、プロジェクト構成に合わせて適宜修正してください
ツールヒント表示用クラスの作成
上記作成したchartフォルダを右クリックし、New → Kotlin File/Classを選択し、"MarkerViews"と名前を付け、GitHub上のMarkerViews.ktをコピペします。
※コード上部の「プロジェクト構成に合わせ変更」とコメントしてある部分は、プロジェクト構成に合わせて適宜修正してください
UIフォーマット指定用クラスの作成
上記作成したchartフォルダを右クリックし、New → Kotlin File/Classを選択し、"ChartFormats"と名前を付け、GitHub上のChartFormats.ktをコピペします。
※コード上部の「プロジェクト構成に合わせ変更」とコメントしてある部分は、プロジェクト構成に合わせて適宜修正してください
ツールヒント用レイアウトファイルの作成
クリック時のツールヒント用レイアウトファイルを、下記手順で作成します。
res/layoutを右クリックし、New → Layout Resource Fileを選択し、"simple_marker_view"と名前をつけます
作成されたxmlファイルを、下記内容に書き換えます
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="@color/toolTipBgColor"
tools:ignore="Overdraw">
<TextView
android:id="@+id/tvSimple"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="7dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:text=""
android:textSize="12sp"
android:textColor="@color/toolTipTextColor"
android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
res/values/colors.xmlに、下記内容を追記します。
(色コードはこちらを参考に適宜変更してください)
:
<color name="toolTipBgColor">#999999</color>//背景色コード
<color name="toolTipTextColor">#ffffff</color>//テキスト色コード
:
以上で、パッケージの導入が完了しました
#使用方法
折れ線グラフ、棒グラフ、ローソク足グラフ、円グラフそれぞれに関して、
実装方法を解説します。
GitHubにサンプルコードをアップしていますので、こちらも参照頂けると分かりやすいかと思います
##1. 折れ線グラフの実装方法
折れ線グラフの実装方法を、レイアウト(.xml)と処理部(.kt)にわけて解説します。
###レイアウトの実装
下記のように、LineChart用のウィジェットをレイアウト(例:activity_main.xml)中に組み込みます。
:
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/lineChartExample"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
:
###折れ線グラフ作成メソッド呼び出し処理の実装
折れ線グラフ作成メソッドを呼び出す処理を、Kotlinファイル(例:MainActivity.kt)内に実装します。
・線が1本のとき
・横軸を時系列にしたいとき
・複数のとき
で例を分けて解説します。
####線が1本のとき
基本的な流れとしては
・Chartフォーマットの指定
・DataSetフォーマットの指定
・EntryにmakeLineChartDataメソッドでデータ格納
・setupLineChartメソッドでグラフ描画
となります。
//表示用サンプルデータの作成//
val x = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)//X軸データ
val y = x.map{it*it}//Y軸データ(X軸の2乗)
//Chartフォーマット
var lineChartFormat = LineChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var lineDataSetFormat = mapOf(
"linear" to LineDataSetFormat(/*ここでDataSetフォーマット指定*/)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
"linear" to makeLineChartData(x, y)
)
//②~⑦グラフの作成
setupLineChart(allLinesEntries, findViewById(R.id.lineChartExample), lineChartFormat, lineDataSetFormat, context)
ChartフォーマットやDataSetフォーマットはグラフのUIを指定します。詳しくは後述します
上記コードを実行すると、下図のようなグラフが表示されます
####横軸を時系列にしたいとき
横軸を時系列にしたいときは、Entryにデータ格納するメソッドを、
makeLineChartData() → makeDateLineChartData()
に変更します
//表示用サンプルデータの作成//
//X軸データ(時間)
val sdf: SimpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
val x = listOf<Date>(
sdf.parse("2020/09/01 00:00:00"),
sdf.parse("2020/09/01 06:00:00"),
sdf.parse("2020/09/01 12:00:00"),
sdf.parse("2020/09/01 18:00:00"),
sdf.parse("2020/09/02 00:00:00"),
sdf.parse("2020/09/02 06:00:00"),
sdf.parse("2020/09/02 12:00:00"),
sdf.parse("2020/09/03 18:00:00"),
)
val y = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)//Y軸データ(数値)
//Chartフォーマット
var lineChartFormat = LineChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var lineDataSetFormat = mapOf(
"linear" to LineDataSetFormat(/*ここでDataSetフォーマット指定*/)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
"linear" to makeDateLineChartData(x, y, lineChartFormat.timeAccuracy)
)
//②~⑦グラフの作成
setupLineChart(allLinesEntries, findViewById(R.id.lineChartExample), lineChartFormat, lineDataSetFormat, context)
#####非等間隔な時間を正確に表現したいとき
前述のように、上記の方法はデータ点がX方向に等間隔にプロットされるため、
データの取得間隔が一定でないときは、時間が横軸上で正確に表現されません。
このようなときに時間を正確に表現したい時は、Chartフォーマットで
timeAccuracy = true
と指定します。
//Chartフォーマット
var lineChartFormat = LineChartFormat(
timeAccuracy = true,
/*ここでその他のChartフォーマット指定*/
)
####線が複数本のとき
1本の時との違いは、
・DataSetフォーマットを線の本数だけ指定
・Entryに線の本数だけデータ格納
となります。
//表示用サンプルデータの作成//
val x = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)//X軸データ
val y1 = x.map{it}//Y軸データ1(X軸の1乗)
val y2 = x.map{it*it}//Y軸データ2(X軸の2乗)
//Chartフォーマット
var lineChartFormat = LineChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var lineDataSetFormat = mapOf(
"linear" to LineDataSetFormat(/*ここでDataSetフォーマット指定*/),
"square" to LineDataSetFormat(/*ここでDataSetフォーマット指定*/)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
"linear" to makeLineChartData(x, y1),
"square" to makeLineChartData(x, y2)
)
//②~⑦グラフの作成
setupLineChart(allLinesEntries, lineChart, lineChartFormat, lineDataSetFormat, context)
なお、データ格納時のメソッドをmakeDateLineChartDataにすれば、複数線かつ時系列のグラフも作成可能です
##2. 棒グラフの実装方法
棒グラフの実装方法を、レイアウト(.xml)と処理部(.kt)にわけて解説します。
###レイアウトの実装
LineChartのときと同様に、BarChart用のウィジェットをレイアウト(例:activity_main.xml)中に組み込みます。
:
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/barChartExample"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
:
###実行コードの実装
棒グラフ作成メソッドを呼び出す処理を、Kotlinファイル(例:MainActivity.kt)内に実装します。
・棒が1本のとき
・横軸を時系列にしたいとき
・複数の棒を積み上げ表示するとき
・複数の棒を横に並べて表示するとき
で例を分けて解説します。
####棒が1本のとき
折れ線グラフのときとほぼ同様です。
"Line~"という名前になっているクラス名を"Bar~"と変えるだけでいけるかと思います。
//表示用サンプルデータの作成//
val x = listOf<Float>(1f, 2f, 3f, 4f, 6f, 7f, 8f, 9f)//X軸データ
val y = x.map{it*it}//Y軸データ(X軸の2乗)
//Chartフォーマット
var barChartFormat = BarChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var barDataSetFormat = mapOf(
"square" to BarDataSetFormat(/*ここでDataSetフォーマット指定*/)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
"square" to makeBarChartData(x, y)
)
//②~⑦グラフの作成
setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)
####横軸を時系列にしたいとき
横軸を時系列にしたいときは、Entryにデータ格納するメソッドを、
makeBarChartData() → makeDateBarChartData()
に変更します
※折れ線グラフのときと異なり、timeAccuracyプロパティ指定はできません
//表示用サンプルデータの作成//
//X軸データ(時間)
val sdf: SimpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
val x = listOf<Date>(
sdf.parse("2020/09/01 00:00:00"),
sdf.parse("2020/09/01 06:00:00"),
sdf.parse("2020/09/01 12:00:00"),
sdf.parse("2020/09/01 18:00:00"),
sdf.parse("2020/09/02 00:00:00"),
sdf.parse("2020/09/02 06:00:00"),
sdf.parse("2020/09/02 12:00:00"),
sdf.parse("2020/09/03 18:00:00"),
)
val y = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)//Y軸データ(数値)
//Chartフォーマット
var barChartFormat = BarChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var barDataSetFormat = mapOf(
"square" to BarDataSetFormat(/*ここでDataSetフォーマット指定*/)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
"square" to makeDateBarChartData(x, y)
)
//②~⑦グラフの作成
setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)
####複数の棒を積み上げ表示するとき
1本の時との違いは、
・Entryに格納するY軸データは、List<MutableList<Float>>で積み上げたいデータをまとめて格納
・棒ごとのカテゴリ名は、DataSetフォーマットのプロパティstackLabelsに、Listで指定
・棒ごと色指定は、プロパティ"color"ではなく、"colors"にリストで指定が必要(参考)
となります。
//表示用サンプルデータの作成//
val x = listOf<Float>(1f, 2f, 3f, 4f, 6f, 7f, 8f, 9f)//X軸データ
val y = x.map{ mutableListOf(it, it*it)}//Y軸データ(1項目:X軸の1乗、2項目:Xの2乗)
//Chartフォーマット
var barChartFormat = BarChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var barDataSetFormat = mapOf(
"stack" to BarDataSetFormat(
stackLabels = listOf("linear","square"),
/*ここでその他のDataSetフォーマット指定*/
)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
"stack" to makeStackBarChartData(x, y)
)
//②~⑦グラフの作成
setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)
####複数の棒を横に並べて表示するとき
複数折れ線と同様、1本の時との違いは、
・DataSetフォーマットを棒の本数だけ指定
・Entryに棒の本数だけデータ格納
となります。
※横軸間隔が一定のときのみ使用可能なので、ご注意ください
//表示用サンプルデータの作成
val x = listOf<Float>(1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)//X軸データ
val y1 = x.map{it}//Y軸データ1(X軸の1乗)
val y2 = x.map{it*it}//Y軸データ2(X軸の2乗)
//Chartフォーマット
var barChartFormat = BarChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var barDataSetFormat = mapOf(
"linear" to BarDataSetFormat(/*ここでDataSetフォーマット指定*/),
"square" to BarDataSetFormat(/*ここでDataSetフォーマット指定*/)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
"linear" to makeBarChartData(x, y1),
"square" to makeBarChartData(x, y2)
)
//②~⑦グラフの作成
setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)
##3. ローソク足グラフの実装方法
「ローソク足グラフ」とは株価のチャートで使われているグラフで、
箱ひげ図の代用にも使えます
実装方法を、レイアウト(.xml)と処理部(.kt)にわけて解説します。
###レイアウトの実装
LineChartのときと同様に、CandleStickChart用のウィジェットをレイアウト(例:activity_main.xml)中に組み込みます。
:
<com.github.mikephil.charting.charts.CandleStickChart
android:id="@+id/candleStickChartExample"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
:
###実行コードの実装
ローソク足グラフ作成メソッドを呼び出す処理を、Kotlinファイル(例:MainActivity.kt)内に実装します。
・横軸が数値のとき
・横軸を時系列にしたいとき
で例を分けて解説します。
####横軸が数値のとき
Entryへのデータ格納時に、X軸の値、Y最大値、Y最小値、Y開始値、Y終了値の5種類の引数を指定する必要があることに注意してください
//表示用サンプルデータの作成//
val x = listOf<Float>(2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f)//X軸データ
val yHigh = x.map{it * 2}//Y軸データ(最大値)
val yLow = x.map{it}//Y軸データ(最小値)
val yOpen = x.map{it + 1}//Y軸データ(開始値)
val yClose = x.map{it + 2}//Y軸データ(終了値)
//Chartフォーマット
var candleChartFormat = CandleChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット(カテゴリ名のMap)
var candleDataSetFormat = CandleDataSetFormat(/*ここでDataSetフォーマット指定*/)
//①Entryにデータ格納(カテゴリ名のMap)
val candleEntries = makeCandleChartData(x, yHigh, yLow, yOpen, yClose)
//②~⑦グラフの作成
setupCandleStickChart(candleEntries, candleStickChart, candleChartFormat, candleDataSetFormat, context)
####横軸を時系列にしたいとき
横軸を時系列にしたいときは、Entryにデータ格納するメソッドを、
makeCandleChartData() → makeDateCandleChartData()
に変更します
※timeAccuracyプロパティ指定はできません
//表示用サンプルデータの作成//
//X軸データ(時間)
val sdf: SimpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
val x = listOf<Date>(
sdf.parse("2020/09/01 00:00:00"),
sdf.parse("2020/09/01 06:00:00"),
sdf.parse("2020/09/01 12:00:00"),
sdf.parse("2020/09/01 18:00:00"),
sdf.parse("2020/09/02 00:00:00"),
sdf.parse("2020/09/02 06:00:00"),
sdf.parse("2020/09/02 12:00:00"),
sdf.parse("2020/09/03 18:00:00"),
)
val ySeed = listOf<Float>(2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f)//Y軸データ生成用
val yHigh = ySeed.map{it * 2}//Y軸データ(最大値)
val yLow = ySeed.map{it}//Y軸データ(最小値)
val yOpen = ySeed.map{it + 1}//Y軸データ(開始値)
val yClose = ySeed.map{it + 2}//Y軸データ(終了値)
//Chartフォーマット
var candleChartFormat = CandleChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット
var candleDataSetFormat = CandleDataSetFormat(/*ここでDataSetフォーマット指定*/)
//①Entryにデータ格納
val candleEntries = makeDateCandleChartData(x, yHigh, yLow, yOpen, yClose)
//②~⑦グラフの作成
setupCandleStickChart(candleEntries, candleStickChart, candleChartFormat, candleDataSetFormat, context)
##4. 円グラフの実装方法
円グラフの実装方法を、レイアウト(.xml)と処理部(.ktあるいは.java)にわけて解説します。
###レイアウトの実装
LineChartのときと同様に、PieChart用のウィジェットをレイアウト(例:activity_main.xml)中に組み込みます。
:
<com.github.mikephil.charting.charts.PieChart
android:id="@+id/pieChartExample"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
:
###実行コードの実装
円グラフ作成メソッドを呼び出す処理を、Kotlinファイル(例:MainActivity.kt)内に実装します。
//表示用サンプルデータの作成//
val dimensions = listOf("A", "B", "C", "D")//分割円の名称(String型)
val values = listOf(1f, 2f, 3f, 4f)//分割円の大きさ(Float型)
//Chartフォーマット
var pieChartFormat = PieChartFormat(/*ここでChartフォーマット指定*/)
//DataSetフォーマット
var pieDataSetFormat = PieDataSetFormat(/*ここでDataSetフォーマット指定*/)
//①Entryにデータ格納
val pieEntries = makePieChartEntries(dimensions, values)
//②~⑦グラフの作成
setupPieChart(pieEntries, pieChart, "PieChart", pieChartFormat, pieDataSetFormat)
#UIフォーマットの指定方法
グラフ全体(Chart)に適用するものと、カテゴリ毎(DataSet)に適用するものを分けて解説します。
適用方法は、下のようにコンストラクタに引数を与えることで各プロパティ内容が指定できます。
(指定がない場合は前章「使用方法」で図示しているようなUIとなります)
//表示用サンプルデータの作成//
//X軸データ(時間)
val sdf: SimpleDateFormat = SimpleDateFormat("yyyy/M")
val x = listOf<Date>(
sdf.parse("1990/1"),
sdf.parse("1995/1"),
sdf.parse("2000/1"),
sdf.parse("2005/1"),
sdf.parse("2010/1"),
sdf.parse("2015/1"),
sdf.parse("2018/1")
)
val y1 = listOf(6.0f, 7.6f, 10.3f, 13.0f, 15.0f, 18.2f, 20.6f)//Y軸データ1(アメリカ)
val y2 = listOf(0.4f, 0.7f, 1.2f, 2.3f, 6.1f, 11.2f, 13.4f)//Y軸データ2(中国)
val y3 = listOf(3.1f, 5.4f, 4.8f, 4.8f, 5.7f, 4.4f, 5.0f)//Y軸データ3(日本)
val y4 = listOf(1.6f, 2.6f, 1.9f, 2.8f, 3.4f, 3.4f, 4.0f)//Y軸データ4(ドイツ)
///////////ここでChartフォーマットの指定///////////
var lineChartFormat = LineChartFormat(
legendTextSize = 10f,
description = "主要国GDP推移",
descriptionTextSize = 15f,
descriptionYOffset = -10f,
bgColor = Color.DKGRAY,
xAxisDateFormat = SimpleDateFormat("yyyy年"),
toolTipDateFormat = SimpleDateFormat("yyyy年"),
toolTipDirection = "xy",
toolTipUnitY = "兆ドル"
)
///////////ここでDataSetフォーマットの指定(カテゴリ名のMap)///////////
var lineDataSetFormat = mapOf(
"アメリカ" to LineDataSetFormat(//線1のDataSetフォーマット指定
lineColor = UNIVERSAL_BLUE,
lineWidth = 2f
),
"中国" to LineDataSetFormat(//線2のDataSetフォーマット指定
lineColor = UNIVERSAL_RED,
lineWidth = 2f
),
"日本" to LineDataSetFormat(//線3のDataSetフォーマット指定
lineColor = UNIVERSAL_SKYBLUE,
lineWidth = 2f
),
"ドイツ" to LineDataSetFormat(//線4のDataSetフォーマット指定
lineColor = Color.LTGRAY,
lineWidth = 2f
)
)
//①Entryにデータ格納(カテゴリ名のMap)
val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
"アメリカ" to makeDateLineChartData(x, y1, false),
"中国" to makeDateLineChartData(x, y2, false),
"日本" to makeDateLineChartData(x, y3, false),
"ドイツ" to makeDateLineChartData(x, y4, false)
)
//②~⑦グラフの作成
setupLineChart(allLinesEntries, lineChart, lineChartFormat, lineDataSetFormat, context)
GitHubのMainActivity.ktに実装例がいくつかある(特に後半の4メソッド)ので、こちらを参照頂けると分かりやすいかと思います
##グラフ全体(Chart)に適用するUIフォーマット一覧
下記に、Chartフォーマットにおいて指定可能なプロパティ一覧を記載します。
「MPAndroidChartでのプロパティ名」列のリンクで、実際にどのようにUIが変わるかを図示しました(別記事)
※MPAndroidChartに対応が存在しない本パッケージオリジナルプロパティに関しては、本記事内で別途変化を図示しております。
適用対象 | 変更項目 | Chartフォーマットでのプロパティ名 | 型 | 備考 | MPAndroidChartでのプロパティ名 | 折れ線 | 棒グラフ | ローソク足 | 円グラフ |
---|---|---|---|---|---|---|---|---|---|
凡例 | マーク形状 | legendFormat | Legend.LegendForm? | nullなら凡例表示なし | .legend.form | 〇 | 〇 | 〇 | 〇 |
凡例 | 文字色 | legentTextColor | Int? | nullならデフォルト(黒) | .legend.textColor | 〇 | 〇 | 〇 | 〇 |
凡例 | 文字サイズ | legendTextSize | Float? | nullならデフォルト | .legend.textSize | 〇 | 〇 | 〇 | 〇 |
説明ラベル | 表示文字列 | description | String? | nullなら説明ラベルなし | .description.text | 〇 | 〇 | 〇 | 〇 |
説明ラベル | 文字色 | descriptionTextColor | Int? | nullならデフォルト(黒) | .description.textColor | 〇 | 〇 | 〇 | 〇 |
説明ラベル | 文字サイズ | descriptionTextSize | Float? | nullならデフォルト | .description.textSize | 〇 | 〇 | 〇 | 〇 |
説明ラベル | 横位置微調整 | descriptionXOffset | Float? | nullならデフォルト | .description.xOffset | 〇 | 〇 | 〇 | 〇 |
説明ラベル | 縦位置微調整 | descriptionYOffset | Float? | nullならデフォルト | .description.yOffset | 〇 | 〇 | 〇 | 〇 |
背景 | 背景色 | bgColor | Int? | nullならデフォルト(白) | .setBackgroundColor() | 〇 | 〇 | 〇 | 〇 |
タッチ操作 | 有効無効 | touch | Boolean | .setTouchEnabled() | 〇 | 〇 | 〇 | ||
X軸ラベル | 表示有無 | xAxisEnabled | Boolean | .xAxis.isEnabled | 〇 | 〇 | 〇 | ||
X軸ラベル | 文字色 | xAxisTextColor | Int? | nullならデフォルト(黒) | .xAxis.textColor | 〇 | 〇 | 〇 | |
X軸ラベル | 文字サイズ | xAxisTextSize | Float? | nullならデフォルト | .xAxis.textSize | 〇 | 〇 | 〇 | |
X軸ラベル | 時刻表示フォーマット | xAxisDateFormat | SimpleDateFormat? | nullならM/d H:mm | - | 〇 | 〇 | 〇 | |
左Y軸ラベル | 表示有無 | yAxisLeftEnabled | Boolean | .axisLeft.isEnabled | 〇 | 〇 | 〇 | ||
左Y軸ラベル | 文字色 | yAxisLeftTextColor | Int? | nullならデフォルト(黒) | .axisLeft.textColor | 〇 | 〇 | 〇 | |
左Y軸ラベル | 文字サイズ | yAxisLeftTextSize | Float? | nullならデフォルト | .axisLeft.textSize | 〇 | 〇 | 〇 | |
左Y軸ラベル | 表示下限 | yAxisLeftMin | Float? | nullなら下限なし | .axisLeft.axisMinimum | 〇 | 〇 | 〇 | |
左Y軸ラベル | 表示上限 | yAxisLeftMax | Float? | nullなら上限なし | .axisLeft.axisMaximam | 〇 | 〇 | 〇 | |
右Y軸ラベル | 表示有無 | yAxisRightEnabled | Boolean | .axisRight.isEnabled | 〇 | 〇 | 〇 | ||
右Y軸ラベル | 文字色 | yAxisRightTextColor | Int? | nullならデフォルト(黒) | .axisRight.textColor | 〇 | 〇 | 〇 | |
右Y軸ラベル | 文字サイズ | yAxisRightTextSize | Float? | nullならデフォルト | .axisRight.textSize | 〇 | 〇 | 〇 | |
右Y軸ラベル | 表示下限 | yAxisRightMin | Float? | nullなら下限なし | .axisRight.axisMinimum | 〇 | 〇 | 〇 | |
右Y軸ラベル | 表示上限 | yAxisRightMax | Float? | nullなら上限なし | .axisRight.axisMaximam | 〇 | 〇 | 〇 | |
拡大操作 | 拡大方向 | zoomDirection | String? | "x", "y", "xy" nullなら拡大無効 |
.isScaleXEnabled .isScaleYEnabled .setScaleEnabled() |
〇 | 〇 | 〇 | |
拡大操作 | ピンチ操作有効 | zoomPinch | Boolean | .setPinchZoom() | 〇 | 〇 | 〇 | ||
ツールヒント | 表示する軸 | toolTipDirection | String? | "x", "y", "xy" nullならツールヒントなし |
.marker | 〇 | 〇 | 〇 | |
ツールヒント | 時系列グラフのときのフォーマット | toolTipDateFormat | SimpleDateFormat? | nullならM/d H:mm | .marker | 〇 | 〇 | 〇 | |
ツールヒント | X軸の単位 | toolTipUnitX | String | デフォルトは単位なし("") | .marker | 〇 | 〇 | 〇 | |
ツールヒント | Y軸の単位 | toolTipUnitY | String | デフォルトは単位なし("") | .marker | 〇 | 〇 | 〇 | |
X軸表示法 | 時間軸スケールの正確性 | timeAccuracy | Boolean | Trueなら時間軸を正確表示(ラベルは最大最小値のみ) | - | 〇 | |||
ラベル | 文字色 | labelColor | Int? | nullならデフォルト(黒) | .setEntryLabelColor() | 〇 | |||
ラベル | 文字サイズ | labelTextSize | Float? | nullならデフォルト | .setEntryLabelTextSize() | 〇 | |||
中央のテキスト | 表示文字列 | centerText | String? | nullなら中央テキストなし | .centerText | 〇 | |||
中央のテキスト | 文字色 | centerTextColor | Int? | nullならデフォルト(黒) | .setCenterTextColor() | 〇 | |||
中央のテキスト | 文字サイズ | centerTextSize | Float? | nullならデフォルト | .setCenterTextSize() | 〇 | |||
中央の穴 | 穴の半径 | holeRadius | Float? | nullならデフォルト | .holeRadius | 〇 | |||
中央の穴 | 穴周辺の色が薄い部分の幅 | transparentCircleRadius | Float? | nullならデフォルト | .transparentCircleRadius | 〇 | |||
中央の穴 | 穴の塗りつぶし色 | holeColor | Int? | nullならデフォルト(黒) | .setHoleColor() | 〇 | |||
______________ | ____________ | ________________ |
###本パッケージオリジナルメソッド
####xAxisDateFormat
X軸の時刻表示フォーマットを変更します(時系列グラフのときのみ。デフォルトは"M/d H:mm")
var lineChartFormat = LineChartFormat(xAxisDateFormat = SimpleDateFormat("d日H時"))
####timeAccuracy
前述した内容と同様に、時間軸スケールの正確性を指定します(時系列折れ線グラフのみ)
var lineChartFormat = LineChartFormat(timeAccuracy=true)
var lineChartFormat = LineChartFormat(timeAccuracy=false)
####toolTipDirection
var lineChartFormat = LineChartFormat(toolTipDirection=null)
var lineChartFormat = LineChartFormat(toolTipDirection="x")
var lineChartFormat = LineChartFormat(toolTipDirection="y")
var lineChartFormat = LineChartFormat(toolTipDirection="xy")
####toolTipDateFormat
ツールヒントの時刻表示フォーマットを変更します(時系列グラフのときのみ。デフォルトは"M/d H:mm")
var lineChartFormat = LineChartFormat(
toolTipDirection="xy",
toolTipDateFormat = SimpleDateFormat("d日H時")
)
####toolTipUnitX
ツールヒントのX軸表示に付加する単位を指定します(デフォルトは単位なし)
var lineChartFormat = LineChartFormat(
toolTipDirection="xy",
toolTipUnitX = "日目"
)
####toolTipUnitY
ツールヒントのY軸表示に付加する単位を指定します(デフォルトは単位なし)
var lineChartFormat = LineChartFormat(
toolTipDirection="xy",
toolTipUnitY = "円"
)
##カテゴリごと(DataSet)に適用するUIフォーマット一覧
下記に、DataSetフォーマット(線ごと、棒ごとetc.に指定)において指定可能なプロパティ一覧を記載します。
「MPAndroidChartでのプロパティ名」列のリンクで、実際にどのようにUIが変わるかを図示しました(別記事)
※MPAndroidChartに対応が存在しない本パッケージオリジナルプロパティに関しては、本記事内で別途変化を図示しております。
適用対象 | 変更項目 | DataSetフォーマットでのプロパティ名 | 型 | 備考 | MPAndroidChartでのプロパティ名 | 折れ線 | 棒グラフ | ローソク足 | 円グラフ |
---|---|---|---|---|---|---|---|---|---|
値表示 | 値表示の有無 | drawValue | Boolean | .setDrawValues() | 〇 | 〇 | 〇 | 〇 | |
値表示 | 値表示の文字色 | valueTextColor | Int? | nullならデフォルト(黒) | .valueTextColor | 〇 | 〇 | 〇 | 〇 |
値表示 | 値表示の文字サイズ | valueTextSize | Float? | nullならデフォルト | .valueTextSize | 〇 | 〇 | 〇 | 〇 |
値表示 | 値表示のフォーマット | valueTextFormatter | String? | nullならデフォルト | .valueFormatter | 〇 | 〇 | 〇 | 〇 |
軸 | 左右軸どちらを使用するか | axisDependency | YAxis.AxisDependency? | nullなら左Y軸 | .axisDependency | 〇 | 〇 | ||
線 | 線の色 | lineColor | Int? | nullならデフォルト(水色) | .color | 〇 | |||
線 | 幅 | lineWidth | Float? | nullならデフォルト | .lineWidth | 〇 | |||
線 | 補完方法 | fittingMode | LineDataSet.Mode? | nullならデフォルト(直線補完) | .mode | 〇 | |||
データ点 | 表示有無 | drawCircles | Boolean | .setDrawCircles() | 〇 | ||||
データ点 | 枠の色 | circleColor | Int? | nullならデフォルト(水色) | .setCircleColor() | 〇 | |||
データ点 | 枠の半径 | circleRadius | Float? | nullならデフォルト | .circleRadius | 〇 | |||
データ点 | 穴の塗りつぶし色 | circleHoleColor | Int? | nullならデフォルト(白) | .circleHoleRadius | 〇 | |||
データ点 | 穴の半径 | circleHoleRadius | Float? | nullならデフォルト | .circleHoleColor | 〇 | |||
棒 | 棒の色 | barColor | Int? | nullならデフォルト(水色) | .color | 積み上げ以外 | |||
棒 | 各積み上げ棒の色リスト | barColors | List | .colors | 積み上げ | ||||
棒 | 積み上げ棒のカテゴリ名 | stackLabels | List? | nullならデフォルト(ラベル名で埋める) | .stackLabels | 積み上げ | |||
ローソク細線 | 線の色 | shadowColor | Int | .shadowColor | 〇 | ||||
ローソク細線 | 幅 | shadowWidth | Float? | nullならデフォルト | .shadowWidth | 〇 | |||
ローソク太線 | 減少時の色 | decreasingColor | Int | .decreasingColor | 〇 | ||||
ローソク太線 | 減少時の塗りつぶし形式 | decreasingPaint | Paint.Style? | nullなら塗りつぶしあり | .decreasingPaintStyle | 〇 | |||
ローソク太線 | 増加時の色 | increasingColor | Int? | nullなら色なし | .increasingColor | 〇 | |||
ローソク太線 | 増加時の塗りつぶし形式 | increasingPaint | Paint.Style? | nullなら塗りつぶしなし | .increasingPaintStyle | 〇 | |||
分割円 | 分割円の色 | colors | List | .colors | 〇 | ||||
______________ | ____________ | ________________ |
###本パッケージオリジナルメソッド
####valueTextFormatter
値表示のフォーマットを指定します
(MPAndroidChartではValueFormatterクラス内をオーバーライドして指定しますが、本パッケージではString型で指定します)
var lineDataSetFormat = mapOf(
"linear" to LineDataSetFormat(
drawValue = true,
valueTextSize = 12f,
valueTextFormatter = "%.0f"
)
)
var lineDataSetFormat = mapOf(
"linear" to LineDataSetFormat(
drawValue = true,
valueTextSize = 12f,
valueTextFormatter = "%.2f"
)
)
var lineDataSetFormat = mapOf(
"linear" to LineDataSetFormat(
drawValue = true,
valueTextSize = 12f,
valueTextFormatter = "%.1f円"
)
)
#参考
##MPAndroidChartにおける時系列グラフの作り方
###時系列でないグラフの場合
時系列ではない(X軸がFloat型の数値)グラフでは、
val x = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)
val y = x.map{it*it}
var entryList = mutableListOf<Entry>()
for(i in x.indices){
entryList.add(Entry(x[i], y[i]))
}
というように、Entryの第一項にX軸の値を、第二項にY軸の値を入れれば、入力したX軸の値に合わせて
val lineDataSets = ListOf<ILineDataSet>(LineDataSet())
lineChart.data = LineData(lineDataSets)
###時系列グラフの場合
X軸がDate(java.util.Date)型の数値のとき、Entryの第一項にX軸の値を入力することができません。
val sdf: SimpleDateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
val x = listOf<Date>(
sdf.parse("2020/09/01 00:00:00"),
sdf.parse("2020/09/01 06:00:00"),
sdf.parse("2020/09/01 12:00:00"),
sdf.parse("2020/09/01 18:00:00"),
sdf.parse("2020/09/02 00:00:00"),
sdf.parse("2020/09/02 06:00:00"),
sdf.parse("2020/09/02 12:00:00"),
sdf.parse("2020/09/02 18:00:00"),
)
val y = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)
//Entryにデータ格納
var entryList = mutableListOf<Entry>()
for(i in x.indices){
entryList.add(
Entry(x[i], y[i])//←ここでエラーが出る
)
}
val lineDataSets = listOf<ILineDataSet>(LineDataSet(entryList,"label"))
lineChart.data = LineData(lineDataSets)
Entryの第一項にはFloat型を格納する必要があるので、indexをFloat型に変換して格納します(代わりに第3項に日付データを保持しておく)
:
//Entryにデータ格納
var entryList = mutableListOf<Entry>()
for(i in x.indices){
entryList.add(
Entry(i.toFloat(), y[i], x[i])
)
}
:
のように、X軸のラベルがただのインデックスとなってしまい、時刻が何時だか分かりません。
下記のように、時刻を文字列リストに変換してX軸ラベルに指定することで、ラベルを表示することができます。
:
//X軸の値を日付型→文字列に変換し、X軸ラベルに指定
val xStr = x.map { SimpleDateFormat("M/d H:mm").format(it)}
lineChart.xAxis.valueFormatter = IndexAxisValueFormatter(xStr)
val lineDataSets = listOf<ILineDataSet>(LineDataSet(entryList,"label"))
lineChart.data = LineData(lineDataSets)