I made a simple graph library for smartphone apps [MP Android Chart Kai]

Smartphone app and graph

If you want to display the graph on the Android app, MPAndroidChart Library is often used. (iOS version of ** Charts Library ** also exists)

It's a great library with many features and a great UI, Due to the lack of Japanese information, I feel that the implementation difficulty is quite high. So, ** I created an additional package to easily create a graph! ** ** Github

You can easily create a graph like the one below

image.png (The subject is heavy! (Laughs))

** This package is premised on implementation in Kotlin, If you wish, I will make a Java / Swift (iOS) version. If you have any other questions or requests to add methods, please feel free to comment! ** **

How to make MPAndroidChart easier to use?

The following parts I struggled with when creating an app that included graphs on the MPAndroidChart. ** 1. Creating a time series graph ** ** 2. Format specification (especially color) ** ** 3. Creating tooltips ** ** 4. There are few Japanese documents **

1. Creating a time series graph

[Details will be written in the supplement](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#mpandroidchart%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B% E6% 99% 82% E7% B3% BB% E5% 88% 97% E3% 82% B0% E3% 83% A9% E3% 83% 95% E3% 81% AE% E4% BD% 9C% E3% 82% 8A% E6% 96% B9), It takes a lot of time and effort to store data and format axes to create a time series graph. image.png

Correspondence with this package

We have created a method dedicated to time series graphs so that it can be created with simple operations.

Also, if the time is not evenly spaced as shown below,

        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"),//Only here the time interval is flying
        )

With the above method, it will be plotted at equal intervals based only on the index information. The time interval is not accurately represented on the horizontal axis. image.png

In this package, we also prepared a display mode that plots accurately even if the horizontal axis is not evenly spaced. image.png (Labels are displayed only at the beginning and end)

2. UI format specification

In MPAndroidChart, roughly for graph display ** ① Data input process ** ** ② Process to specify UI format ** Two types are required. From the standpoint of independence, it is desirable to be able to specify these two types of processing separately in the code.

However, when specifying multiple Y-axis values like a line graph, As shown below, it will be a specification method in which processing is complicated in ① and ②.

        //Data storage in Entry → ① Data input processing
        var entryList1 = mutableListOf<Entry>()//1st line
        var entryList2 = mutableListOf<Entry>()//Second line
        for(i in x.indices){
            entryList1.add(
                Entry(x[i], y1[i])
            )
            entryList2.add(
                Entry(x[i], y2[i])
            )
        }

        //X-axis setting → ② UI format specification processing
        lineChart.xAxis.apply {
            isEnabled = true
            textColor = Color.BLACK
        }
        //Left Y-axis setting → ② UI format specification processing
        lineChart.axisLeft.apply {
            isEnabled = true
            textColor = Color.BLACK
        }
        //Right Y-axis setting → ② UI format specification processing
        lineChart.axisLeft.apply {
            isEnabled = false
        }

        //LineDataSet(For each line)Create a list of → ① Data entry process
        val lineDataSets = mutableListOf<ILineDataSet>()
        //Data storage for the first line → ① Data input processing
        val lineDataSet1 = LineDataSet(entryList1, "linear")
        //Color of the first line → ② UI format specification processing
        lineDataSet1.color = Color.BLUE
        //Store in list → ① Data entry processing
        lineDataSets.add(lineDataSet1)
        //Data storage for the second line → ① Data input processing
        val lineDataSet2 = LineDataSet(entryList2, "square")
        //The color of the second line → ② UI format specification processing
        lineDataSet2.color = Color.RED

        //Store LineDataSet list in LineData → ① Data input processing
        lineDataSets.add(lineDataSet2)
        //Data storage in LineChart → ① Data input processing
        lineChart.data = LineData(lineDataSets)

Correspondence with this package

Since the above specification method is not preferable from the viewpoint of independence and code readability, **-UI format specification class (Chart and DataSet. See here for the difference between them](https://qiita.com/c60evaporator/items/14e63d22d860b73e6f22#%E3%83%95%E3%82% A9% E3% 83% BC% E3% 83% 9E% E3% 83% 83% E3% 83% 88% E6% 8C% 87% E5% AE% 9A% E3% 81% 8C2% E3% 81% 8B% E6% 89% 80% E3% 81% AB% E5% 88% 86% E3% 81% 8B% E3% 82% 8C% E3% 82% 8B)) ** **-Data entry method ** **-Method to draw a graph based on the above UI specification & data ** The configuration is such that it can be specified independently in the order of.

Also, ** I don't know how to specify the format! For those who say ** Even if you do not specify the format (do not put an argument in the constructor) I've added a function that makes my subjectivity feel good (abstract expression, but ... lol).

In particular, there are many setting points for color setting, and manual setting is troublesome, so **-Automatically specify the color of lines and bars based on Color Universal Design ** image.png ** ・ When the background is black (brightness is 0.5 or less), the characters are automatically changed to white ** image.png

Added the function

3. Create tooltip

"Tooltip" that displays the details of the data when you tap the data point image.png

If there is, the visibility of the UI will be greatly improved.

However, the Official Documentation does not properly describe the implementation method. It is required to implement by groping while looking at the sample code.

Correspondence with this package

The following can be specified in the format specification class so that tooltips can be displayed easily. ** A. Whether to display tooltips ** Specifies whether to display tooltips

** B. Axial direction of data to be displayed (both X, Y, XY) ** Select the axial direction of the data to be displayed as shown in the figure below (from left: no display, X only, Y only, XY both) toolhint.png

** C. For time series graphs, time display format (eg "M / d HH: mm") ** Enabled to specify the time display format when the X axis is time series. In the figure below, the format "d day H hour" is specified. image.png

** D. Units given to data (eg ° C,%, Pa, etc.) ** Units can be added to the displayed data for both the X-axis and Y-axis. In the figure below, the unit "circle" is added to the Y axis. image.png

4. There are few Japanese documents

It can be said that there are no articles that are comprehensively explained in Japanese. Especially for UI format specification system, there are many method properties whose explanation is not described in the official English document.

Correspondence with this package

In this article, I have provided a list of UI format specification properties and a link that illustrates the changes due to the specification](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#%E3%82%B0%E3%83 % A9% E3% 83% 95% E5% 85% A8% E4% BD% 93chart% E3% 81% AB% E9% 81% A9% E7% 94% A8% E3% 81% 99% E3% 82% 8Bui % E3% 83% 95% E3% 82% A9% E3% 83% BC% E3% 83% 9E% E3% 83% 83% E3% 83% 88% E4% B8% 80% E8% A6% A7) ([Click here for a list of DataSet UI format specification properties](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#%E3%82%AB%E3%83%86%E3%82%B4%E3%83% AA% E3% 81% 94% E3% 81% A8dataset% E3% 81% AB% E9% 81% A9% E7% 94% A8% E3% 81% 99% E3% 82% 8Bui% E3% 83% 95% E3% 82% A9% E3% 83% BC% E3% 83% 9E% E3% 83% 83% E3% 83% 88% E4% B8% 80% E8% A6% A7))

Which property should be set and how the graph shape will change I think you can easily follow it with Japanese + images

Things necessary

Please build the following development environment ** ・ Development PC (Windows 10 is used this time) ** **-Android Studio (4.0.1 is used this time, Android version is recommended after API26) ** **-Android smartphone to operate (Pixel 3a is used this time) **

Introduction method

The procedure is as follows ** 1. Introduction of MPAndroidChart ** ** 2. Introduction of CustomMP Android Chart package **

1. Introduction of MPAndroidChart

Introduce MPAndroidChart, a graph drawing library, in the project

In build.gradle (Project),

allprojects {
    repositories{
        :
        maven { url 'https://jitpack.io' }
        :

I will add the description. 9_mpandroidchart_build.gradle.png

In build.gradle (Module: app),

dependencies {
    :
    implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
    :

I will add the description. 10_mpandroidchart_build.gradle_app.png

If you close and reopen the project with "File → Close Project" on the menu bar, The library is reflected.

2. Introduction of CustomMP Android Chart package

A collection of methods and classes for easily creating the above MPAndroidChart graph, Packaged as "Custom MP Android Chart". Uploaded to Github

Package overview

It consists of the following 6 modules ** ・ LineChartMethods.kt **: Method collection for line graphs ** ・ BarChartMethods.kt **: Method collection for bar graphs ** ・ CandleStickChartMethods.kt **: A collection of methods for candlestick graphs (graphs like stock price charts) ** ・ PieChartMethods.kt **: Method collection for line graphs ** ・ MarkerViews.kt **: Tooltip display class ** ・ ChartFormats.kt **: Module that collects UI format specification classes

Also, the layout file used in the tooltip You also need to install ** simple_marker_view.xml **.

How to install the package

Follow the steps below to install it in the project to create the graph. ** * There are many manual operations, so if you know a better way to provide it, I would appreciate it if you could teach me **

Right-click on the package folder directly under the java folder, select New → Package and name it "chart" 11_chart.png

Creating a module for drawing line graphs

Right-click on the chart folder created above, select New → Kotlin File / Class, name it "LineChartMethods", and select LineChartMethods.kt on GitHub. Copy (/ master / app / src / main / java / com / mongodb / custommpandroidchart / chart).

Creating a module for drawing bar graphs

Right-click on the chart folder created above, select New → Kotlin File / Class, name it "BarChartMethods", and select BarChartMethods.kt on GitHub. Copy (/ master / app / src / main / java / com / mongodb / custommpandroidchart / chart).

Creating a module for drawing candlestick graphs

Right-click on the chart folder you created above, select New → Kotlin File / Class, name it "CandleStickChartMethods", and select CandleStickChartMethods.kt on GitHub. Copy (/ master / app / src / main / java / com / mongodb / custommpandroidchart / chart).

Creating a pie chart drawing module

Right-click on the chart folder you created above, select New → Kotlin File / Class, name it "PieChartMethods", and select PieChartMethods.kt on GitHub. Copy (/ master / app / src / main / java / com / mongodb / custommpandroidchart / chart).

Creating a class for displaying tooltips

Right-click on the chart folder created above, select New → Kotlin File / Class, name it "MarkerViews", and select MarkerViews.kt on GitHub Copy (/ master / app / src / main / java / com / mongodb / custommpandroidchart / chart).

Creating a class for specifying the UI format

Right-click on the chart folder created above, select New → Kotlin File / Class, name it "ChartFormats", and select ChartFormats.kt on GitHub. Copy (/ master / app / src / main / java / com / mongodb / custommpandroidchart / chart).

Creating a layout file for tooltips

Follow the steps below to create a layout file for tooltips when clicking.

Right-click res / layout, select New → Layout Resource File and name it "simple_marker_view" 24_make_fragment_xml1.png

Rewrite the created xml file with the following contents

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>

Add the following contents to res / values / colors.xml. (Please change the color code see here as appropriate)

colors.xml


    :
    <color name="toolTipBgColor">#999999</color>//Background color code
    <color name="toolTipTextColor">#ffffff</color>//Text color code
    :

This completes the package installation.

how to use

For line graphs, bar graphs, candlestick graphs, and pie charts, I will explain the implementation method.

The sample code has been uploaded to GitHub, so it would be easier to understand if you can refer to it as well

1. How to implement a line graph

The implementation method of the line graph is explained separately for the layout (.xml) and the processing section (.kt).

Layout implementation

Include the widget for LineChart in the layout (eg activity_main.xml) as shown below.

activity_main.xml


  :
<com.github.mikephil.charting.charts.LineChart
    android:id="@+id/lineChartExample"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  :

Implementation of line graph creation method call processing

Implement the process of calling the line graph creation method in the Kotlin file (eg MainActivity.kt). ・ When there is only one line ・ When you want to make the horizontal axis time series ・ When there are multiple I will explain each example separately.

When there is one line

As a basic flow **-Chart format specification ** **-Specify DataSet format ** ** ・ Data is stored in Entry with makeLineChartData method ** **-Draw a graph with the setupLineChart method ** It will be.

        //Creation of sample data for display//
        val x = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)//X-axis data
        val y = x.map{it*it}//Y-axis data (X-axis squared)

        //Chart format
        var lineChartFormat = LineChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var lineDataSetFormat =  mapOf(
            "linear" to LineDataSetFormat(/*Specify DataSet format here*/)
        )

        //① Data storage in Entry(Category name Map)
        val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
            "linear" to makeLineChartData(x, y)
        )

        //② ~ ⑦ Graph creation
        setupLineChart(allLinesEntries, findViewById(R.id.lineChartExample), lineChartFormat, lineDataSetFormat, context)

The Chart format and DataSet format specify the UI of the graph. [Details will be described later](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#ui%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3% 83% 83% E3% 83% 88% E3% 81% AE% E6% 8C% 87% E5% AE% 9A% E6% 96% B9% E6% B3% 95) When you run the above code, you will see a graph like the one below image.png

When you want to make the horizontal axis time series

If you want to make the horizontal axis time series, use the method to store data in Entry. makeLineChartData() → makeDateLineChartData() Change to

//Creation of sample data for display//
        //X-axis data(time)
        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-axis data(Numerical value)

        //Chart format
        var lineChartFormat = LineChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var lineDataSetFormat =  mapOf(
            "linear" to LineDataSetFormat(/*Specify DataSet format here*/)
        )

        //① Data storage in Entry(Category name Map)
        val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
            "linear" to makeDateLineChartData(x, y, lineChartFormat.timeAccuracy)
        )

        //② ~ ⑦ Graph creation
        setupLineChart(allLinesEntries, findViewById(R.id.lineChartExample), lineChartFormat, lineDataSetFormat, context)

image.png

When you want to accurately represent non-equidistant times

[As mentioned above](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#%E9%9D%9E%E7%AD%89%E9%96%93%E9%9A%94%E3%81% AA% E6% 99% 82% E9% 96% 93% E3% 82% 92% E6% AD% A3% E7% A2% BA% E3% 81% AB% E8% A1% A8% E7% 8F% BE% E3% 81% 97% E3% 81% 9F% E3% 81% 84% E3% 81% A8% E3% 81% 8D), because the above method plots the data points in the X direction at equal intervals. If the data acquisition interval is not constant, the time will not be represented accurately on the horizontal axis.

If you want to express the time accurately in such a case, use Chart format. timeAccuracy = true Specify.

        //Chart format
        var lineChartFormat = LineChartFormat(
        timeAccuracy = true,
        /*Specify other Chart formats here*/
        )

image.png

When there are multiple lines

The difference from the time of one is -Specify the DataSet format for the number of lines ・ Data is stored in Entry for the number of lines It will be.

        //Creation of sample data for display//
        val x = listOf<Float>(1f, 2f, 3f, 5f, 8f, 13f, 21f, 34f)//X-axis data
        val y1 = x.map{it}//Y-axis data 1 (X-axis squared)
        val y2 = x.map{it*it}//Y-axis data 2 (X-axis squared)

        //Chart format
        var lineChartFormat = LineChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var lineDataSetFormat =  mapOf(
            "linear" to LineDataSetFormat(/*Specify DataSet format here*/),
            "square" to LineDataSetFormat(/*Specify DataSet format here*/)
        )

        //① Data storage in Entry(Category name Map)
        val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
            "linear" to makeLineChartData(x, y1),
            "square" to makeLineChartData(x, y2)
        )

        //② ~ ⑦ Graph creation
        setupLineChart(allLinesEntries, lineChart, lineChartFormat, lineDataSetFormat, context)

image.png If you set the method for storing data to makeDateLineChartData, you can create a graph with multiple lines and time series.

2. How to implement a bar graph

The implementation method of the bar graph is explained separately for the layout (.xml) and the processing part (.kt).

Layout implementation

As with LineChart, embed the widget for BarChart in the layout (eg activity_main.xml).

activity_main.xml


  :
<com.github.mikephil.charting.charts.BarChart
    android:id="@+id/barChartExample"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  :

Execution code implementation

Implement the process of calling the bar chart creation method in your Kotlin file (eg MainActivity.kt). ・ When there is only one stick ・ When you want to make the horizontal axis time series ・ When displaying multiple bars in a stacked manner ・ When displaying multiple bars side by side I will explain each example separately.

When there is one stick

It is almost the same as the line graph. I think you can just change the class name named "Line ~" to "Bar ~".

        //Creation of sample data for display//
        val x = listOf<Float>(1f, 2f, 3f, 4f, 6f, 7f, 8f, 9f)//X-axis data
        val y = x.map{it*it}//Y-axis data (X-axis squared)

        //Chart format
        var barChartFormat = BarChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var barDataSetFormat =  mapOf(
            "square" to BarDataSetFormat(/*Specify DataSet format here*/)
        )

        //① Data storage in Entry(Category name Map)
        val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
            "square" to makeBarChartData(x, y)
        )

        //② ~ ⑦ Graph creation
        setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)

image.png

When you want to make the horizontal axis time series

If you want to make the horizontal axis time series, use the method to store data in Entry. makeBarChartData() → makeDateBarChartData() Change to

        //Creation of sample data for display//
        //X-axis data(time)
        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-axis data(Numerical value)

        //Chart format
        var barChartFormat = BarChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var barDataSetFormat =  mapOf(
            "square" to BarDataSetFormat(/*Specify DataSet format here*/)
        )

        //① Data storage in Entry(Category name Map)
        val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
            "square" to makeDateBarChartData(x, y)
        )

        //② ~ ⑦ Graph creation
        setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)

image.png

When displaying multiple bars in a stacked manner

The difference from the time of one is -The Y-axis data to be stored in Entry is the data that you want to accumulate in List <MutableList >. -The category name for each bar is specified in List in the property stackLabels of DataSet format. -Color specification for each bar needs to be specified in the list in "colors" instead of the property "color" (Reference % 83% 86% E3% 82% B4% E3% 83% AA% E3% 81% 94% E3% 81% A8dataset% E3% 81% AB% E9% 81% A9% E7% 94% A8% E3% 81 % 99% E3% 82% 8Bui% E3% 83% 95% E3% 82% A9% E3% 83% BC% E3% 83% 9E% E3% 83% 83% E3% 83% 88% E4% B8% 80 % E8% A6% A7))) It will be.

        //Creation of sample data for display//
        val x = listOf<Float>(1f, 2f, 3f, 4f, 6f, 7f, 8f, 9f)//X-axis data
        val y = x.map{ mutableListOf(it, it*it)}//Y-axis data (1 item):X-axis 1st power, 2 items:X squared)

        //Chart format
        var barChartFormat = BarChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var barDataSetFormat =  mapOf(
            "stack" to BarDataSetFormat(
                stackLabels = listOf("linear","square"),
                /*Specify other DataSet formats here*/
            )
        )

        //① Data storage in Entry(Category name Map)
        val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
            "stack" to makeStackBarChartData(x, y)
        )

        //② ~ ⑦ Graph creation
        setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)

image.png

When displaying multiple bars side by side

As with multiple polygonal lines, the difference from one line is -Specify the DataSet format for the number of bars ・ Data is stored in Entry for the number of bars It will be. ** * Please note that it can be used only when the horizontal axis spacing is constant **

        //Creation of sample data for display
        val x = listOf<Float>(1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f)//X-axis data
        val y1 = x.map{it}//Y-axis data 1 (X-axis squared)
        val y2 = x.map{it*it}//Y-axis data 2 (X-axis squared)

        //Chart format
        var barChartFormat = BarChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var barDataSetFormat =  mapOf(
            "linear" to BarDataSetFormat(/*Specify DataSet format here*/),
            "square" to BarDataSetFormat(/*Specify DataSet format here*/)
        )

        //① Data storage in Entry(Category name Map)
        val allBarsEntries: MutableMap<String, MutableList<BarEntry>> = mutableMapOf(
            "linear" to makeBarChartData(x, y1),
            "square" to makeBarChartData(x, y2)
        )

        //② ~ ⑦ Graph creation
        setupBarChart(allBarsEntries, barChart, barChartFormat, barDataSetFormat, context)

image.png

3. How to implement a candlestick graph

"Candlestick graph" is a graph used in stock price charts. Can also be used as a substitute for box plots

The implementation method is explained separately for the layout (.xml) and the processing section (.kt).

Layout implementation

As with LineChart, embed the widget for CandleStickChart in the layout (eg activity_main.xml).

activity_main.xml


  :
<com.github.mikephil.charting.charts.CandleStickChart
    android:id="@+id/candleStickChartExample"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  :

Execution code implementation

Implement the process of calling the candlestick graph creation method in the Kotlin file (eg MainActivity.kt). ・ When the horizontal axis is a numerical value ・ When you want to make the horizontal axis time series I will explain each example separately.

When the horizontal axis is a numerical value

Note that when storing data in the Entry, you need to specify five types of arguments: X-axis value, Y maximum value, Y minimum value, Y start value, and Y end value.

        //Creation of sample data for display//
        val x = listOf<Float>(2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f)//X-axis data
        val yHigh = x.map{it * 2}//Y-axis data (maximum value)
        val yLow = x.map{it}//Y-axis data (minimum value)
        val yOpen = x.map{it + 1}//Y-axis data (starting value)
        val yClose = x.map{it + 2}//Y-axis data (end value)

        //Chart format
        var candleChartFormat = CandleChartFormat(/*Chart format specification here*/)
        //DataSet format(Category name Map)
        var candleDataSetFormat = CandleDataSetFormat(/*Specify DataSet format here*/)

        //① Data storage in Entry(Category name Map)
        val candleEntries = makeCandleChartData(x, yHigh, yLow, yOpen, yClose)

        //② ~ ⑦ Graph creation
        setupCandleStickChart(candleEntries, candleStickChart, candleChartFormat, candleDataSetFormat, context)

image.png

When you want to make the horizontal axis time series

If you want to make the horizontal axis time series, use the method to store data in Entry. makeCandleChartData() → makeDateCandleChartData() Change to

        //Creation of sample data for display//
        //X-axis data(time)
        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)//For Y-axis data generation
        val yHigh = ySeed.map{it * 2}//Y-axis data (maximum value)
        val yLow = ySeed.map{it}//Y-axis data (minimum value)
        val yOpen = ySeed.map{it + 1}//Y-axis data (starting value)
        val yClose = ySeed.map{it + 2}//Y-axis data (end value)

        //Chart format
        var candleChartFormat = CandleChartFormat(/*Chart format specification here*/)
        //DataSet format
        var candleDataSetFormat = CandleDataSetFormat(/*Specify DataSet format here*/)

        //① Data storage in Entry
        val candleEntries = makeDateCandleChartData(x, yHigh, yLow, yOpen, yClose)

        //② ~ ⑦ Graph creation
        setupCandleStickChart(candleEntries, candleStickChart, candleChartFormat, candleDataSetFormat, context)

image.png

4. How to implement a pie chart

The implementation method of the pie chart is explained separately for the layout (.xml) and the processing part (.kt or .java).

Layout implementation

As with LineChart, embed the widget for PieChart in the layout (eg activity_main.xml).

activity_main.xml


  :
<com.github.mikephil.charting.charts.PieChart
    android:id="@+id/pieChartExample"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
  :

Execution code implementation

Implement the process of calling the pie chart creation method in your Kotlin file (eg MainActivity.kt).

        //Creation of sample data for display//
        val dimensions = listOf("A", "B", "C", "D")//Name of the split circle(String type)
        val values = listOf(1f, 2f, 3f, 4f)//The size of the dividing circle(Float type)

        //Chart format
        var pieChartFormat = PieChartFormat(/*Chart format specification here*/)
        //DataSet format
        var pieDataSetFormat = PieDataSetFormat(/*Specify DataSet format here*/)

        //① Data storage in Entry
        val pieEntries = makePieChartEntries(dimensions, values)

        //② ~ ⑦ Graph creation
        setupPieChart(pieEntries, pieChart, "PieChart", pieChartFormat, pieDataSetFormat)

image.png

How to specify the UI format

I will explain what is applied to the entire graph (Chart) and what is applied to each category (DataSet) separately.

As for the application method, each property content can be specified by giving an argument to the constructor as shown below. (If not specified, [Previous Chapter "Usage"](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3 The UI will be as shown in% 95))

        //Creation of sample data for display//
        //X-axis data(time)
        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-axis data 1 (USA)
        val y2 = listOf(0.4f, 0.7f, 1.2f, 2.3f, 6.1f, 11.2f, 13.4f)//Y-axis data 2 (China)
        val y3 = listOf(3.1f, 5.4f, 4.8f, 4.8f, 5.7f, 4.4f, 5.0f)//Y-axis data 3 (Japan)
        val y4 = listOf(1.6f, 2.6f, 1.9f, 2.8f, 3.4f, 3.4f, 4.0f)//Y-axis data 4 (Germany)

        ///////////Specify Chart format here///////////
        var lineChartFormat = LineChartFormat(
            legendTextSize = 10f,
            description = "Changes in GDP of major countries",
            descriptionTextSize = 15f,
            descriptionYOffset = -10f,
            bgColor = Color.DKGRAY,
            xAxisDateFormat = SimpleDateFormat("yyyy year"),
            toolTipDateFormat = SimpleDateFormat("yyyy year"),
            toolTipDirection = "xy",
            toolTipUnitY = "Trillion dollars"
        )
        ///////////Specify the DataSet format here(Category name Map)///////////
        var lineDataSetFormat =  mapOf(
            "America" to LineDataSetFormat(//Specify DataSet format for line 1
                lineColor = UNIVERSAL_BLUE,
                lineWidth = 2f
            ),
            "China" to LineDataSetFormat(//Specify the DataSet format for line 2
                lineColor = UNIVERSAL_RED,
                lineWidth = 2f
            ),
            "Japan" to LineDataSetFormat(//Line 3 DataSet format specification
                lineColor = UNIVERSAL_SKYBLUE,
                lineWidth = 2f
            ),
            "Germany" to LineDataSetFormat(//Line 4 DataSet format specification
                lineColor = Color.LTGRAY,
                lineWidth = 2f
            )
        )

        //① Data storage in Entry(Category name Map)
        val allLinesEntries: MutableMap<String, MutableList<Entry>> = mutableMapOf(
            "America" to makeDateLineChartData(x, y1, false),
            "China" to makeDateLineChartData(x, y2, false),
            "Japan" to makeDateLineChartData(x, y3, false),
            "Germany" to makeDateLineChartData(x, y4, false)
        )

        //② ~ ⑦ Graph creation
        setupLineChart(allLinesEntries, lineChart, lineChartFormat, lineDataSetFormat, context)

image.png

There are some implementation examples in GitHub MainActivity.kt ( Especially since the latter 4 methods), I think it will be easier to understand if you refer to this.

List of UI formats applied to the entire graph (Chart)

Below is a list of properties that can be specified in the Chart format.

** The link in the "Property name in MPAndroidChart" column ** illustrates how the UI actually changes (separate article).

Applicable target Items to change Property name in Chart format Type Remarks Property name in MPAndroidChart Line bar graph Candlestick pie chart
Usage Guide Mark shape legendFormat Legend.LegendForm? nullならUsage Guide表示なし .legend.form
Usage Guide Letter color legentTextColor Int? Default if null(black) .legend.textColor
Usage Guide font size legendTextSize Float? Default if null .legend.textSize
Description label Display string description String? nullならDescription labelなし .description.text
Description label Letter color descriptionTextColor Int? Default if null(black) .description.textColor
Description label font size descriptionTextSize Float? Default if null .description.textSize
Description label Horizontal position fine adjustment descriptionXOffset Float? Default if null .description.xOffset
Description label Vertical position fine adjustment descriptionYOffset Float? Default if null .description.yOffset
background background色 bgColor Int? Default if null(White) .setBackgroundColor()
Touch operation Valid Invalid touch Boolean .setTouchEnabled()
X-axis label With or without display xAxisEnabled Boolean .xAxis.isEnabled
X-axis label Letter color xAxisTextColor Int? Default if null(black) .xAxis.textColor
X-axis label font size xAxisTextSize Float? Default if null .xAxis.textSize
X-axis label Time display format xAxisDateFormat SimpleDateFormat? M if null/d H:mm -
Left Y-axis label With or without display yAxisLeftEnabled Boolean .axisLeft.isEnabled
Left Y-axis label Letter color yAxisLeftTextColor Int? Default if null(black) .axisLeft.textColor
Left Y-axis label font size yAxisLeftTextSize Float? Default if null .axisLeft.textSize
Left Y-axis label Display lower limit yAxisLeftMin Float? If null, there is no lower limit .axisLeft.axisMinimum
Left Y-axis label Display upper limit yAxisLeftMax Float? No upper limit if null .axisLeft.axisMaximam
Right Y-axis label With or without display yAxisRightEnabled Boolean .axisRight.isEnabled
Right Y-axis label Letter color yAxisRightTextColor Int? Default if null(black) .axisRight.textColor
Right Y-axis label font size yAxisRightTextSize Float? Default if null .axisRight.textSize
Right Y-axis label Display lower limit yAxisRightMin Float? If null, there is no lower limit .axisRight.axisMinimum
Right Y-axis label Display upper limit yAxisRightMax Float? No upper limit if null .axisRight.axisMaximam
Enlargement operation Expansion direction zoomDirection String? "x", "y", "xy"
If null, expansion is invalid
.isScaleXEnabled
.isScaleYEnabled
.setScaleEnabled()
Enlargement operation Pinch operation effective zoomPinch Boolean .setPinchZoom()
Tooltip Axis to display toolTipDirection String? "x", "y", "xy"
nullならTooltipなし
.marker
Tooltip Format for time series graphs toolTipDateFormat SimpleDateFormat? M if null/d H:mm .marker
Tooltip X-axis unit toolTipUnitX String The default is no unit("") .marker
Tooltip Y-axis unit toolTipUnitY String The default is no unit("") .marker
X-axis display method Time axis scale accuracy timeAccuracy Boolean If True, the time axis is displayed accurately(Labels are maximum and minimum only) -
label Letter color labelColor Int? Default if null(black) .setEntryLabelColor()
label font size labelTextSize Float? Default if null .setEntryLabelTextSize()
Central text Display string centerText String? No central text if null .centerText
Central text Letter color centerTextColor Int? Default if null(black) .setCenterTextColor()
Central text font size centerTextSize Float? Default if null .setCenterTextSize()
Central hole Hole radius holeRadius Float? Default if null .holeRadius
Central hole Width of light-colored area around the hole transparentCircleRadius Float? Default if null .transparentCircleRadius
Central hole Hole fill color holeColor Int? Default if null(black) .setHoleColor()
______________ ____________ ________________

This package original method

xAxisDateFormat Change the X-axis time display format (only for time series graphs, default is "M / d H: mm")

var lineChartFormat = LineChartFormat(xAxisDateFormat = SimpleDateFormat("d day H o'clock"))

image.png

timeAccuracy [Same as above](https://qiita.com/c60evaporator/items/b8f8c83dd3630c0091cf#%E9%9D%9E%E7%AD%89%E9%96%93%E9%9A%94%E3% 81% AA% E6% 99% 82% E9% 96% 93% E3% 82% 92% E6% AD% A3% E7% A2% BA% E3% 81% AB% E8% A1% A8% E7% 8F% BE% E3% 81% 97% E3% 81% 9F% E3% 81% 84% E3% 81% A8% E3% 81% 8D), specifies the accuracy of the timeline scale (time series line graph only)

Accurately display scale, time label only display maximum and minimum


var lineChartFormat = LineChartFormat(timeAccuracy=true)

image.png

Shows all time labels, less accurate


var lineChartFormat = LineChartFormat(timeAccuracy=false)

image.png

toolTipDirection

No tooltip display


var lineChartFormat = LineChartFormat(toolTipDirection=null)

image.png

Show X-axis values in tooltips


var lineChartFormat = LineChartFormat(toolTipDirection="x")

image.png

Show Y-axis values in tooltips


var lineChartFormat = LineChartFormat(toolTipDirection="y")

image.png

Show both X-axis and Y-axis values in tooltips


var lineChartFormat = LineChartFormat(toolTipDirection="xy")

image.png

toolTipDateFormat Change the time display format of the tooltip (only for time series graphs, default is "M / d H: mm")

Tooltip time display format"d day H o'clock"To


var lineChartFormat = LineChartFormat(
            toolTipDirection="xy",
            toolTipDateFormat = SimpleDateFormat("d day H o'clock")
        )

image.png

toolTipUnitX Specifies the unit to add to the X-axis display of the tooltip (default is no unit)

Tooltip X-axis units"Day"To


var lineChartFormat = LineChartFormat(
            toolTipDirection="xy",
            toolTipUnitX = "Day"
        )

image.png

toolTipUnitY Specifies the unit to add to the Y-axis display of the tooltip (default is no unit)

Tooltip Y-axis unit"Circle"To


var lineChartFormat = LineChartFormat(
            toolTipDirection="xy",
            toolTipUnitY = "Circle"
        )

image.png

List of UI formats applied to each category (DataSet)

Below is a list of properties that can be specified in the DataSet format (specified for each line, each bar, etc.).

** The link in the "Property name in MPAndroidChart" column ** illustrates how the UI actually changes (separate article).

Applicable target Items to change Property name in DataSet format Type Remarks Property name in MPAndroidChart Line bar graph Candlestick pie chart
Value display Value displayの有無 drawValue Boolean .setDrawValues()
Value display Value displayの文字色 valueTextColor Int? Default if null(black) .valueTextColor
Value display Value displayの文字サイズ valueTextSize Float? Default if null .valueTextSize
Value display Value displayのフォーマット valueTextFormatter String? Default if null .valueFormatter
axis 左右axisどちらを使用するか axisDependency YAxis.AxisDependency? nullなら左Yaxis .axisDependency
line lineの色 lineColor Int? Default if null(light blue) .color
line width lineWidth Float? Default if null .lineWidth
line Complementary method fittingMode LineDataSet.Mode? Default if null(直line補完) .mode
Data point With or without display drawCircles Boolean .setDrawCircles()
Data point Frame color circleColor Int? Default if null(light blue) .setCircleColor()
Data point Frame radius circleRadius Float? Default if null .circleRadius
Data point Hole fill color circleHoleColor Int? Default if null(White) .circleHoleRadius
Data point Hole radius circleHoleRadius Float? Default if null .circleHoleColor
rod rodの色 barColor Int? Default if null(light blue) .color Other than stacking
rod 各Stackrodの色リスト barColors List .colors Stack
rod Stackrodのカテゴリ名 stackLabels List? Default if null(Fill with label name) .stackLabels Stack
Candle thin line Line color shadowColor Int .shadowColor
Candle thin line width shadowWidth Float? Default if null .shadowWidth
Thick candle line Color when decreasing decreasingColor Int .decreasingColor
Thick candle line Fill format when decreasing decreasingPaint Paint.Style? If null, there is a fill .decreasingPaintStyle
Thick candle line Color when increasing increasingColor Int? No color if null .increasingColor
Thick candle line Fill format when increasing increasingPaint Paint.Style? If null, no fill .increasingPaintStyle
Divided circle Divided circleの色 colors List .colors
______________ ____________ ________________

This package original method

valueTextFormatter Specifies the format of the value display (In MPAndroidChart, specify by overriding in ValueFormatter class, but in this package, specify by String type)

integer(0th place after the decimal point)Display with


var lineDataSetFormat = mapOf(
            "linear" to LineDataSetFormat(
                drawValue = true,
                valueTextSize = 12f,
                valueTextFormatter = "%.0f"
            )
        )

image.png

Displayed in 2 decimal places


var lineDataSetFormat = mapOf(
            "linear" to LineDataSetFormat(
                drawValue = true,
                valueTextSize = 12f,
                valueTextFormatter = "%.2f"
            )
        )

image.png

1st place after the decimal point +"Circle"Display with


var lineDataSetFormat = mapOf(
            "linear" to LineDataSetFormat(
                drawValue = true,
                valueTextSize = 12f,
                valueTextFormatter = "%.1f yen"
            )
        )

image.png

reference

How to make a time series graph in MPAndroidChart

For non-time series graphs

For graphs that are not time series (X-axis is a Float type number)

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]))
}

If you enter the X-axis value in the first term of Entry and the Y-axis value in the second term, it will match the input X-axis value.

val lineDataSets = ListOf<ILineDataSet>(LineDataSet())
lineChart.data = LineData(lineDataSets)

If so, both axes will be plotted correctly as shown in the figure below. image.png

For time series graphs

When the X-axis is a Date (java.util.Date) type number, the X-axis value cannot be entered in the first term of Entry.

        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)
        //Data storage in Entry
        var entryList = mutableListOf<Entry>()
        for(i in x.indices){
            entryList.add(
                Entry(x[i], y[i])//← I get an error here
            )
        }
        val lineDataSets = listOf<ILineDataSet>(LineDataSet(entryList,"label"))
        lineChart.data = LineData(lineDataSets)

Since it is necessary to store the Float type in the first term of Entry, convert the index to the Float type and store it (instead, store the date data in the third term).

          :
        //Data storage in Entry
        var entryList = mutableListOf<Entry>()
        for(i in x.indices){
            entryList.add(
                Entry(i.toFloat(), y[i], x[i])
            )
        }
          :

But as it is image.png!

Like, the X-axis label is just an index and I don't know what time it is. You can display the label by converting the time to a string list and specifying it as the X-axis label, as shown below.

          :
        //Convert X-axis value from date type to character string and specify it as X-axis label
        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)

image.png

Recommended Posts

I made a simple graph library for smartphone apps [MP Android Chart Kai]
I made a library for displaying tutorials on Android.
I made a simple recommendation function.
I made a matching app (Android app)
[Android] I made a pedometer app.
[Ruby] I made a simple Ping client
I made a calculator app on Android
I made a rock-paper-scissors app with android
I made a Diff tool for Java files
I made an Android app for MiRm service
I made a Ruby extension library in C
I made a Docker image of SDAPS for Japanese
I made a simple calculation problem game in Java
I made a check tool for the release module
I made a method to ask for Premium Friday
I made a library that works like a Safari tab !!
I made a simple MVC sample system using Spring Boot
When I made a bar graph with MPAndroidChart, the x-axis label was misaligned for some reason