A Radar Chart using MPAndroidChart

I’m using the MPAndroidChart library (3.0.0) to represent psychometric personality test results in a radar chart (AKA spider chart or cobweb chart). Since the documentation I found on this was either incomplete or obsolete, I’ll briefly document the steps I took.

big_five_350

I’m displaying the chart inside a ConstraintLayout. A fixed height gives satisfactory results on all tested devices.

<com.github.mikephil.charting.charts.RadarChart
            android:id="@+id/chart"
            android:layout_width="0dp"
            android:layout_height="360dp"
            android:layout_marginEnd="24dp"
            android:layout_marginLeft="24dp"
            android:layout_marginRight="24dp"
            android:layout_marginStart="24dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textViewTitle"
            app:layout_constraintVertical_bias="0.0" />

Some fields are needed in our Fragment to hold reference to the RadarChart (and optionally a Typeface), store the variables (factors), the raw data (scores), the List of chart entries and the List of data sets to plot (only one set in this example).

private RadarChart mChart;
private Typeface mTfLight;
private SparseIntArray factors = new SparseIntArray(5);
private SparseIntArray scores = new SparseIntArray(5);
private ArrayList<RadarEntry> entries = new ArrayList<>();
private ArrayList<IRadarDataSet> dataSets = new ArrayList<>();

The factors (‘x-axis’) are defined in onCreate(), as well as a Typeface to be loaded from the assets directory:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // ...
 
        factors.append(1, R.string.extraversion);
        factors.append(2, R.string.agreeableness);
        factors.append(3, R.string.conscientiousness);
        factors.append(4, R.string.emotional_stability);
        factors.append(5, R.string.intellect_imagination);
 
        mTfLight = Typeface.createFromAsset(mActivity.getAssets(), "OpenSans-Light.ttf");
 
        // ...
    }

The strings will be displayed on the chart axes (the ‘x-axis’ of the chart object).

We’ll set the appearance properties of the chart in onCreateView(). We load our data the proper way, asynchronously using a Loader pattern, so we should populate the chart somewhat later in the lifecycle from onLoadFinished. Most properties should be self-explanatory. Use an IAxisValueFormatter to return a formatted String representation of the values to display.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_result, container, false);
        mChart = (RadarChart) rootView.findViewById(R.id.chart);
 
        XAxis xAxis = mChart.getXAxis();
        xAxis.setXOffset(0f);
        xAxis.setYOffset(0f);
        xAxis.setTypeface(mTfLight);
        xAxis.setTextSize(8f);
        xAxis.setValueFormatter(new IAxisValueFormatter() {
 
            private String[] mFactors = new String[]{getString(factors.get(1)), getString(factors.get(2)),
                    getString(factors.get(3)), getString(factors.get(4)), getString(factors.get(5))};
 
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                return mFactors[(int) value % mFactors.length];
            }
 
            @Override
            public int getDecimalDigits() {
                return 0;
            }
        });
 
        YAxis yAxis = mChart.getYAxis();
        yAxis.setAxisMinimum(0f);
        yAxis.setAxisMaximum(50f);
        yAxis.setTypeface(mTfLight);
        yAxis.setTextSize(9f);
        yAxis.setLabelCount(5, false);
        yAxis.setDrawLabels(false);
 
        mChart.getLegend().setEnabled(false);
        mChart.getDescription().setEnabled(false);
        mChart.animateXY(
                1400, 1400,
                Easing.EasingOption.EaseInOutQuad,
                Easing.EasingOption.EaseInOutQuad);
 
        // ...
 
        return rootView;
    }

Start the loader with restartLoader() (not initLoader()) to ensure that the cursor refreshes itself:

       mActivity.getSupportLoaderManager().restartLoader(ANSWERED_ITEMS_LOADER_ID, null, this);

Populate the data array in onLoadFinished():

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        scores.clear();
        // while (cursor.moveToNext()) {
        //     ...
        //     scores.put(..., ...);
        // }
 
        // Or hardcode some test data: 
        scores.append(1, 18);
        scores.append(2, 26);
        scores.append(3, 35);
        scores.append(4, 40);
        scores.append(5, 48);
 
        drawChart();
    }

To draw the chart, first build the list of entries. Then create a RadarDataSet from this list and add it to the list of data sets. Create a RadarData object from this list and add it to the chart.

An IValueFormatter returns a formatted string for the integer values to display.

Finally, invalidate the View to ensure that it gets redrawn when populated.

    private void drawChart() {
 
        entries.clear();
 
        for (int i = 1; i <= 5; i++) {
            entries.add(new RadarEntry(scores.get(i)));
        }
 
        RadarDataSet dataSet = new RadarDataSet(entries, "");
        dataSet.setColor(R.color.colorPrimary);
        dataSet.setDrawFilled(true);
 
        dataSets.add(dataSet);
 
        RadarData data = new RadarData(dataSets);
        data.setValueTypeface(mTfLight);
        data.setValueTextSize(8f);
 
        data.setValueFormatter(new IValueFormatter() {
            @Override
            public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
                return String.valueOf((int) value);
            }
 
        });
 
        mChart.setData(data);
        mChart.invalidate();
    }

You can check out our app to see the chart in action.

One thought on “A Radar Chart using MPAndroidChart”

  1. For newer versions of MPAndroidChart (3.0.3) do not implement getDecimalDigits() in IAxisValueFormatter.

Leave a Reply

Your email address will not be published. Required fields are marked *