Androidアプリ開発

MPAndroidChart 棒グラフの実装

MPAndroidChartでリッチなグラフを表示する
この記事は約25分で読めます。
記事内に広告が含まれています。
スポンサーリンク

この記事は Android スマホ用のアプリ開発の中で、
今後の開発で再使用性が高いと思われるコーディングをまとめたものです。
Java での開発経験、XML 構文規則、Android のアプリ開発経験がある方を対象としています。
Android のアプリ開発でお役にたててれば、嬉しいです。
(これから Android のアプリ開発や Java での開発を始めたい方への案内は、
記事の最後で紹介します)

この記事のテーマ


 MPAndroidChart を使用して、棒グラフ(BarChart)を実装をする

MPAndroidChart は、Androidアプリでグラフを作るためのオープンソースライブラリです。
Andorid アプリでグラフを描画する場合のベストプラクティスといっても過言ではありません。
非常に多機能で細かい部分まで制御できる素晴らしいライブラリですが、使用方法について、詳しい日本語ドキュメントがなく、機能の一部しか利用されていない、残念な状況です。

MPAndroidChart は、さまざまなグラフを扱うことが可能です。
今回は、棒グラフ(
BarChart )にフォーカスを当てて、開発したアプリのソースを参考に、使用方法について説明したいと思います。

折れ線グラフ(LineChart)については、こちらです↓↓↓

グラフ(PieChart)については、こちらです↓↓↓

散布図(ScatterChart)については、こちらです↓↓↓

MPAndroidChart を使用するための準備

MPAndroidChart を使用するには、プロジェクトおよび、モジュールの build.gradle ファイルに定義の追加が必要です。

◎build.gradle(プロジェクト)

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

◎build.gradle(モジュール)
2022年11月現在の最新バージョンは 3.1.0 です。

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

◎ライセンス表記
MPAndroidChart は Apache License, Version 2.0 です。
アプリで使用する場合、ライセンス表記が必要です。

ソース提供ではないため、ライセンス条文の記載がある公式サイトのリンクをアプリに実装しています

棒グラフ( BarChart )

棒グラフは、同じ観点から複数のデータを比較する際に使用するグラフです。
データをX軸に観点、Y軸に測定値を配置して、それぞれのデータを棒の長さで大きさを表現するグラフです。
MPAndroidChart では、棒グラフ以外に積み上げグラフや集合棒グラフを作成することが可能です。

グラフ表示

MPAndroidChart のグラフ表示は、グラフに表示する棒をデータセット( BarDataSet )として作成、グラフ( BarChart )に棒データ( BarData )を介して、セットします。
グラフの背景、表示範囲、振る舞いなどの属性は、グラフに定義します。
凡例、軸ラベル、線の太さや色などの属性は、データセットに定義します。
データセットのエントリで配列をセットすることで積み上げグラフ、複数のデータセットを棒データにセットすることで集合棒グラフを表示できます。

MPAndroidChartでBarChartで日付を表示する
グラデーション表示とX値を日付で表示。棒グラフの角が気になります
        :
        ArrayList<BarEntry> barEntry = new ArrayList<>();
        for (int i = 0 ; i < 7 ; i++) {
            barEntry.add(new BarEntry(i , count[i]));
        }
        List<IBarDataSet> barDataSetList = new ArrayList<>();
        BarDataSet        barDataSet = new BarDataSet(barEntry, context.getString(R.string.tab_history));
        barDataSet.setHighlightEnabled(false);                              // ハイライト表示しない
        barDataSet.setValueTextColor(context.getColor(R.color.black));      // 値表示の色  
        barDataSet.setValueTextSize(12);                                    // 値表示のテキストサイズ   
        barDataSet.setGradientColor(0, context.getColor(R.color.cyan700));  // グラデーション
        barDataSet.setValueFormatter(new ValueFormatter() {
            @SuppressLint("DefaultLocale")
            @Override
            public String getFormattedValue(float value) {
                return value> 0 ? String.format("×%.0f", value) : "";
            }
        });
        barDataSetList.add(barDataSet);
        BarData  barData = new BarData(barDataSetList);
        BarChart barChart = view.findViewById(R.id.barchart);
        barChart.setScaleEnabled(false);                                    // Zoomしない
        barChart.setDoubleTapToZoomEnabled(false);                          // ダブルタップ拡大しない
        barChart.getLegend().setEnabled(false);                             // 凡例(非表示)
        barChart.getAxisLeft().setAxisMinimum(0);                           // 0以上
        barChart.getDescription().setEnabled(false);                        // 説明(非表示)
        barChart.getAxisLeft().setEnabled(false);                           // 右側は非表示
        barChart.getAxisRight().setEnabled(false);                          // 右側は非表示
        barChart.getXAxis().setTextSize(12);                                // テキストサイズ
        barChart.getXAxis().setTextColor(context.getColor(R.color.black));  // テキスト色
        barChart.getXAxis().setPosition(XAxis.XAxisPosition.BOTTOM);        // 下側に表示
        barChart.getXAxis().setYOffset(-3);                                 // 上方向にオフセット
        barChart.getXAxis().setDrawGridLines(false);                        // グリッド線なし
        barChart.getXAxis().setDrawAxisLine(false);                         // 枠線なし
        barChart.getXAxis().setValueFormatter(new ValueFormatter() {
            @Override
            public String getFormattedValue(float value) {
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("M/d", Locale.getDefault());
                return simpleDateFormat.format(timeStamp.get((int)value));
            }
        });
        barChart.getXAxis().setEnabled(true);
        barChart.setData(barData);
        barChart.setVisibility(View.VISIBLE);
        barChart.notifyDataSetChanged();
        :

グラフ描画の実装方法は、基本的に折れ線グラフ( LineChart )と同じです。
しかし、
MPAndroidChart の棒グラフ( BarChart )では、注意すべき点があります。
棒グラフの棒の太さに関する設定がないので、X軸の値を連番にする必要があります。
グラフに表示する棒の数と、X値と隣り合うX値との差の割合が、棒の太さになっているようです。
棒の色は標準では、単色(
setColor )と2色のグラデーション( setGradientColor )が使用できます。
棒の値表示(Y軸の値)は、
setValueFormatter を使用して、修飾できます。
X値を連番以外の値で表示する場合は少し工夫が必要です。
X値を連番に割り当てた配列にセットし、
setValueFormatter を使って表示します。

Layoutリソース

        :
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            tools:ignore="UselessParent">
        :   
            <com.github.mikephil.charting.charts.BarChart
                android:id="@+id/barchart"
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:layout_margin="4dp"
                android:background="@android:color/transparent"
                android:visibility="invisible"/>
            :
        </LinearLayout>
        :

Fragment の画面遷移で onCreateView を実行しないケースでは、前回表示したグラフの表示が残っています。
このため グラフの描画前と描画中は
visibility 属性を invisible にしておき、グラフ描画後に visible にすると見た目がよくなります。

アニメーション表示

Webページでは CCS や JS を使用して、グラフをアニメーション表示できますが、MPAndroidChart も同様にグラフをアニメーション表示できます。

棒グラフのアニメーション表示
        :    
        barChart.setData(barData);
        barChart.setVisibility(View.VISIBLE);
        barChart.animateY(1500, Easing.EaseInBack);
        :

barChart.notifyDataSetChanged()barChart.animateY() に変更するだけです。
最初の引数がアニメーションする時間(ミリ秒)、第2引数がイージング(時間の経過に伴うパラメータの変化率)です。

指定できるイージングの種類はこちらです↓↓↓

棒グラフの角を丸くする

MPAndroidChart の棒グラフ( BarChart )の角を丸くするには、すこし工夫が必要です。
棒グラフを描画する
BarChartRenderer を継承したカスタムクラスを作成する必要があります。

MPAndroidChartのBarChartで角が丸い棒グラフを表示する
棒グラフの角が丸くなるとカジュアルなグラフになります
public class CustomBarChartRender extends BarChartRenderer {
    private final RectF rectF = new RectF();
    private final int   radius;

    public CustomBarChartRender(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, int radius) {
        super(chart, animator, viewPortHandler);
        this.radius = radius;
    }

    protected void drawDataSet(Canvas canvas, IBarDataSet dataSet, int index) {
        Transformer transformer = mChart.getTransformer(dataSet.getAxisDependency());
        float       phaseX = mAnimator.getPhaseX();
        float       phaseY = mAnimator.getPhaseY();
        BarData     barData = mChart.getBarData();
        float       barWidth = barData.getBarWidth();
        BarBuffer   barBuffer = mBarBuffers[index];
        Path        path;
        mBarBorderPaint.setColor(dataSet.getBarBorderColor());
        mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth()));
        mShadowPaint.setColor(dataSet.getBarShadowColor());
        if (mChart.isDrawBarShadowEnabled()) {
            mShadowPaint.setColor(dataSet.getBarShadowColor());
            double count = Math.min(Math.ceil((int) (double) ((float) dataSet.getEntryCount() * phaseX)), dataSet.getEntryCount());
            int    i = 0;
            while (i < count) {
                BarEntry e = dataSet.getEntryForIndex(i);
                float    f = e.getX();
                rectF.left  = f - (barWidth / 2);
                rectF.right = f + (barWidth / 2);
                transformer.rectValueToPixel(rectF);
                if (!mViewPortHandler.isInBoundsLeft(rectF.right)) {
                    i++;
                    continue;
                }
                if (!mViewPortHandler.isInBoundsRight(rectF.left)) break;
                rectF.top = mViewPortHandler.contentTop();
                rectF.bottom = mViewPortHandler.contentBottom();
                canvas.drawRoundRect(mBarRect, radius, radius, mShadowPaint);
                i++;
            }
        }
        barBuffer.setPhases(phaseX, phaseY);
        barBuffer.setDataSet(index);
        barBuffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
        barBuffer.setBarWidth(mChart.getBarData().getBarWidth());
        barBuffer.feed(dataSet);
        transformer.pointValuesToPixel(barBuffer.buffer);
        boolean color = dataSet.getColors().size() == 1;
        if (color) mRenderPaint.setColor(dataSet.getColor());
        int j = 0;
        while (j < barBuffer.size()) {
            if (!mViewPortHandler.isInBoundsLeft(barBuffer.buffer[j + 2])) {
                j += 4;
                continue;
            }
            if (!mViewPortHandler.isInBoundsRight(barBuffer.buffer[j])) break;
            if (!color) mRenderPaint.setColor(dataSet.getColor(j / 4));
            if (dataSet.getGradientColor() != null) {
                GradientColor gradientColor = dataSet.getGradientColor();
                mRenderPaint.setShader(new LinearGradient(barBuffer.buffer[j], barBuffer.buffer[j + 3], barBuffer.buffer[j], barBuffer.buffer[j + 1],
                        gradientColor.getStartColor(), gradientColor.getEndColor(), android.graphics.Shader.TileMode.MIRROR));
            }
            if (dataSet.getGradientColors() != null) {
                mRenderPaint.setShader(new LinearGradient(
                        barBuffer.buffer[j], barBuffer.buffer[j + 3], barBuffer.buffer[j], barBuffer.buffer[j + 1],
                        dataSet.getGradientColor(j / 4).getStartColor(), dataSet.getGradientColor(j / 4).getEndColor(), Shader.TileMode.MIRROR));
            }
            if (dataSet.getBarBorderWidth() > 0f) {
                path = roundRect(new RectF(barBuffer.buffer[j], barBuffer.buffer[j + 1], barBuffer.buffer[j + 2], barBuffer.buffer[j + 3]), radius, radius);
                canvas.drawPath(path, mBarBorderPaint);
            } else {
                path = roundRect(new RectF(barBuffer.buffer[j], barBuffer.buffer[j + 1], barBuffer.buffer[j + 2], barBuffer.buffer[j + 3]), radius, radius);
                canvas.drawPath(path, mRenderPaint);
            }
            j += 4;
        }
    }

    private Path roundRect(RectF rect, float rx, float ry) {
        Path  path   = new Path();
        float top    = rect.top;
        float left   = rect.left;
        float right  = rect.right;
        float bottom = rect.bottom;
        float width  = right - left;
        float height = bottom - top;
        if (rx < 0) rx = 0;
        if (ry < 0) ry = 0;
        if (rx > width / 2)  rx = width / 2;
        if (ry > height / 2) ry = height / 2;
        float widthMinusCorners  = (width - (2 * rx));
        float heightMinusCorners = (height - (2 * ry));
        path.moveTo(right, top + ry);
        path.rQuadTo(0, -ry, -rx, -ry);
        path.rLineTo(-widthMinusCorners, 0);
        path.rQuadTo(-rx, 0, -rx, ry);
        path.rLineTo(0, heightMinusCorners);
        path.rLineTo(0, ry);
        path.rLineTo(rx, 0);
        path.rLineTo(widthMinusCorners, 0);
        path.rLineTo(rx, 0);
        path.rLineTo(0, -ry);
        path.rLineTo(0, -heightMinusCorners);
        path.close();
        return path;
    }
}

コンストラクタで BarDataProviderChartAnimatorViewPortHandler、角丸の半径をセット。
描画自体は
Canvas を使用し、drawDataSet でデータセットの内容で角丸の棒グラフを描画します。

        :
        BarChart barChart = view.findViewById(R.id.barchart);
        CustomBarChartRender barChartRender = new CustomBarChartRender(barChart, barChart.getAnimator(), barChart.getViewPortHandler(), 8);
        barChart.setRenderer(barChartRender);
        :

インスタンス化した CustomBarChartRender BarChart
アタッチ(
setRenderer )するだけです。

MPAndroidChart を使用して棒グラフを実装 している Androidアプリです。

今回は、ここまでです。

誤字脱字、意味不明でわかりづらい、
もっと詳しく知りたいなどのご意見は、
このページの最後にある
コメントか、
こちら
から、お願いいたします♪

ポチッとして頂けると、
次のコンテンツを作成する励みになります♪

ブログランキング・にほんブログ村へ

これからAndroidのアプリ開発やJavaでの開発を始めたい方へ

初めての Android のアプリ開発では、アプリケーション開発経験がない方や、
アプリケーション開発経験がある方でも、Java や C# などのオブジェクト指向言語が初めての方は、
書籍などによる独学ではアプリ開発できるようになるには、
かなりの時間がかかりますので、オンラインスクールでの習得をおススメします。

未経験者からシステムエンジニアを目指すのに最適かと、
まずは無料相談から♪

未経験者からプログラマーを目指すのに最適かと、
まずは無料カウンセリングから♪

カリキュラムとサポートがしっかりしています、
お得なキャンペーンとかいろいろやっています♪