MPAndroidChart: Have one graph mirror the zoom/swipes on a sister graph
Asked Answered
X

3

7

I have two line graphs that show complementary data over the same time period. Is there any way to send any touch events received by one graph to the other? I essentially want it so that the graphs always show the same viewing rectangle (at least on horizontally). So if a user swipes left 'n' units on the top graph, the bottom graph will automatically scroll left 'n' units to match.

Xenophobe answered 14/2, 2015 at 22:26 Comment(2)
I have the same problem have you got the answer?Montano
Nope - unfortunately, I tried fiddling with the ontouchlisteners but it didn't work. I might have to look more closely into how touch events are processed later - but for now I decided it wasn't worth it.Xenophobe
J
19

Zooms can be done with the current MPAndroidChart release, for scrolling check out or wait for my extension to be merged: https://github.com/PhilJay/MPAndroidChart/pull/545

You need to set up an OnChartGestureListener on the master chart that copies the translation matrix values to the slave charts:

public class CoupleChartGestureListener implements OnChartGestureListener {

    private Chart srcChart;
    private Chart[] dstCharts;

    public CoupleChartGestureListener(Chart srcChart, Chart[] dstCharts) {
        this.srcChart = srcChart;
        this.dstCharts = dstCharts;
    }

    [...other overrides...]

    @Override
    public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
        //Log.d(TAG, "onChartScale " + scaleX + "/" + scaleY + " X=" + me.getX() + "Y=" + me.getY());
        syncCharts();
    }

    @Override
    public void onChartTranslate(MotionEvent me, float dX, float dY) {
        //Log.d(TAG, "onChartTranslate " + dX + "/" + dY + " X=" + me.getX() + "Y=" + me.getY());
        syncCharts();
    }

    public void syncCharts() {
        Matrix srcMatrix;
        float[] srcVals = new float[9];
        Matrix dstMatrix;
        float[] dstVals = new float[9];

        // get src chart translation matrix:
        srcMatrix = srcChart.getViewPortHandler().getMatrixTouch();
        srcMatrix.getValues(srcVals);

        // apply X axis scaling and position to dst charts:
        for (Chart dstChart : dstCharts) {
            if (dstChart.getVisibility() == View.VISIBLE) {
                dstMatrix = dstChart.getViewPortHandler().getMatrixTouch();
                dstMatrix.getValues(dstVals);
                dstVals[Matrix.MSCALE_X] = srcVals[Matrix.MSCALE_X];
                dstVals[Matrix.MTRANS_X] = srcVals[Matrix.MTRANS_X];
                dstMatrix.setValues(dstVals);
                dstChart.getViewPortHandler().refresh(dstMatrix, dstChart, true);
            }
        }
    }

}

Then set up your master/slave connections for example like this:

//
// Couple chart viewports:
//

tripChart.setOnChartGestureListener(new CoupleChartGestureListener(
        tripChart, new Chart[] { powerChart, energyChart }));
powerChart.setOnChartGestureListener(new CoupleChartGestureListener(
        powerChart, new Chart[] { tripChart, energyChart }));
energyChart.setOnChartGestureListener(new CoupleChartGestureListener(
        energyChart, new Chart[] { tripChart, powerChart }));
Jennajenne answered 12/4, 2015 at 18:22 Comment(3)
This solution works like charm, but the only problem is that I am getting different values for both charts for : mChart.getLowestVisibleX() & mChart.getHighestVisibleX() functions.Report
When I zoom the chart zoom is not syncing for multiple charts. Any help on that ?Nurseryman
This works fine to sync translationSnowberry
O
2

You need add this to CoupleChartGestureListener

    @Override
public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {
    ((BarLineChartTouchListener) srcChart.getOnTouchListener()).stopDeceleration();
    for (Chart dstChart : dstCharts) {
        if (dstChart.getVisibility() == View.VISIBLE) {
            ((BarLineChartTouchListener) dstChart.getOnTouchListener()).stopDeceleration();
        }
    }
}
Oneiric answered 11/9, 2018 at 6:5 Comment(0)
S
0

Kotlin

Set you charts drag deceleration to false:

chart1.setDragDecelerationEnabled(false)
chart2.setDragDecelerationEnabled(false)
chart3.setDragDecelerationEnabled(false)

Create custom listener:

class YourSyncHorizontalScrollListener(
    private val sourceChart: BarLineChartBase<*>,
    private val destinationCharts: Array<BarLineChartBase<*>>
) : OnChartGestureListener {

    override fun onChartGestureStart(me: MotionEvent?, lastPerformedGesture: ChartTouchListener.ChartGesture?) = Unit

    override fun onChartGestureEnd(me: MotionEvent?, lastPerformedGesture: ChartTouchListener.ChartGesture?) = Unit

    override fun onChartLongPressed(me: MotionEvent?) = Unit

    override fun onChartDoubleTapped(me: MotionEvent?) = Unit

    override fun onChartSingleTapped(me: MotionEvent?) = Unit

    override fun onChartFling(me1: MotionEvent?, me2: MotionEvent?, velocityX: Float, velocityY: Float) = Unit

    override fun onChartScale(me: MotionEvent?, scaleX: Float, scaleY: Float) = Unit

    override fun onChartTranslate(me: MotionEvent?, dX: Float, dY: Float) {
        destinationCharts.forEach { destinationChart ->
            if (destinationChart.visibility == View.VISIBLE) {
                destinationChart.moveViewToX(sourceChart.lowestVisibleX)
            }
        }
    }
}

Add listeners to your charts:

private fun setupListeners() {
    chart1.onChartGestureListener = YourSyncHorizontalScrollListener(
        sourceChart = chart1,
        destinationCharts = arrayOf(chart2, chart3)
    )
    chart2.onChartGestureListener = YourSyncHorizontalScrollListener(
        sourceChart = chart2,
        destinationCharts = arrayOf(chart1, chart3)
    )
    chart3.onChartGestureListener = YourSyncHorizontalScrollListener(
        sourceChart = chart3,
        destinationCharts = arrayOf(chart1, chart2)
    )
}
Snowberry answered 24/8, 2023 at 17:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.