PhilJay / MPAndroidChart

A powerful 🚀 Android chart view / graph view library, supporting line- bar- pie- radar- bubble- and candlestick charts as well as scaling, panning and animations.
Other
37.67k stars 9.02k forks source link

Overlaping xAxis labels #3298

Open nartpullif opened 7 years ago

nartpullif commented 7 years ago

Hello.

For my bar chart, my xAxis label display the beginning and ending time of a given task(12:00AM - 1:00AM). However, the label is too long and overlaps with other labels on the axis.

untitled

I tired to use the IndexAxisValueFormatter where I give an Arraylist of String that has String formatted like this 12:00AM - \n1:00AM, so I can stack the time on top of each other. That idea did not work.

Is there a way to get the label to be formatted like that?

diegoacuna commented 7 years ago

I'm looking for something to solve this also, did you manage to solve it?

Rostanxd commented 7 years ago

Have you tried usign html? like this

myTextView.setText(Html.fromHtml("<p>12:00 AM</p><br><p>01:00 am</p>"));

yeluowuhen52 commented 6 years ago

Have you solved this problem?

feihuoliuxinglyy commented 6 years ago

Try this: Matrix m=new Matrix(); m.postScale(1.5f, 1f);//两个参数分别是x,y轴的缩放比例。例如:将x轴的数据放大为之前的1.5倍 barChart.getViewPortHandler().refresh(m, barChart, false);//将图表动画显示之前进行缩放

AndroPlus commented 6 years ago

@feihuoliuxinglyy .. that didn't work great.. any alternative ?

LukaszGrela commented 6 years ago

I've tried to rotate the label xAxis.setLabelRotationAngle(-30f); which helped but for the first render it was cropped, I had to zoom the chart to rerender and then the full label is displayed. Dont know how to force initial rerender .

kbabuszka commented 5 years ago

I've tried to rotate the label xAxis.setLabelRotationAngle(-30f); which helped but for the first render it was cropped, I had to zoom the chart to rerender and then the full label is displayed.

I have the same problem.

yaugenka commented 2 years ago

You may try the following workaround solution. Beware that you may still face first and last label overlapping if isAvoidFirstLastClippingEnabled is true.

The code is in Kotlin.

Basicaly you extend the XAxisRenderer class, override the computeAxisValues method and make sure the interval does not go below the longest (rotated) label width. The Utils.roundToNextSignificant function may round the interval down, so you need to fix that up.

class CustomXAxisRenderer(
    viewPortHandler: ViewPortHandler,
    xAxis: XAxis,
    trans: Transformer,
    private val spacingMin: Float,
): XAxisRenderer(viewPortHandler, xAxis, trans) {

    override fun computeAxisValues(min: Float, max: Float) {
        // Compute label sizes first
        computeSize()

        val labelCount = mAxis.labelCount
        val range = abs(max - min).toDouble()

        if (labelCount == 0 || range <= 0 || range.isInfinite()) {
            mAxis.mEntries = floatArrayOf()
            mAxis.mCenteredEntries = floatArrayOf()
            mAxis.mEntryCount = 0
            return
        }

        // Find out how much spacing (in x value space) between axis values
        var rawInterval = range / labelCount
        var interval = Utils.roundToNextSignificant(rawInterval).toDouble()

        // Do not allow the interval go below the label width with spacing
        val labelMaxWidth = (mXAxis.mLabelRotatedWidth + spacingMin) * range / mViewPortHandler.contentWidth()
        if (interval < labelMaxWidth)
            interval = labelMaxWidth

        // If granularity is enabled, then do not allow the interval to go below specified granularity.
        // This is used to avoid repeated values when rounding values for display.
        if (mAxis.isGranularityEnabled) interval =
            if (interval < mAxis.granularity) mAxis.granularity.toDouble()
            else interval

        // Normalize interval
        rawInterval = interval
        interval = Utils.roundToNextSignificant(rawInterval).toDouble()
        val intervalMagnitude =
            Utils.roundToNextSignificant(10.0.pow(log10(interval).toInt())).toDouble()
        val intervalSigDigit = (interval / intervalMagnitude).toInt()
        if (intervalSigDigit > 5) {
            // Use one order of magnitude higher, to avoid intervals like 0.9 or 90
            // if it's 0.0 after floor(), we use the old value
            interval = if (floor(10.0 * intervalMagnitude) == 0.0) interval
            else floor(10.0 * intervalMagnitude)
            // If rounded down, round up to avoid labels overlapping
        } else if (interval < rawInterval) {
            interval = (intervalSigDigit + 1) * intervalMagnitude
        }

    var n = if (mAxis.isCenterAxisLabelsEnabled) 1 else 0

    /*
     * Copy-paste here the rest of the code from XAxisRenderer.computeAxisValues method
    */
    }
}

And finally attach it to the chart with the appropriate AxisDependency.

binding.chart.setXAxisRenderer(
    CustomXAxisRenderer(
        viewPortHandler, xAxis,
        getTransformer(YAxis.AxisDependency.RIGHT),
        50f
    )
)