Lukas
Lukas

Reputation: 829

MpAndroidChart: Add an Arrow to the End of an Axis

I would like to place an arrow at the end of the axis, but I don't know how to achieve this in MPAndroidCharts library. It should look like this at the end.Chart

My current styling:

  val xAxis = chart.xAxis
  xAxis.setDrawLabels(false)
  xAxis.setDrawAxisLine(true)
  xAxis.setDrawGridLines(false)
  xAxis.position = XAxis.XAxisPosition.BOTTOM
  xAxis.axisLineWidth = 1.5f

  val axisLeft = chart.axisLeft
  axisLeft.setDrawGridLines(false)
  axisLeft.setDrawZeroLine(false)
  axisLeft.setDrawLabels(false)
  axisLeft.axisLineWidth = 1.5f
  axisLeft.setDrawTopYLabelEntry(true)

  val axisRight = chart.axisRight
  axisRight.setDrawGridLines(false)
  axisRight.setDrawLabels(false)
  axisRight.setDrawZeroLine(false)
  axisRight.setDrawAxisLine(false)

Upvotes: 3

Views: 385

Answers (2)

MariosP
MariosP

Reputation: 9113

To add an arrow on the end of X and Y axis you have to make a custom XAxisRenderer and YAxisRenderer. All you have to do is to make a subclass for each renderer and override the public method renderAxisLine(c: Canvas) which is responsible to draw the axis line and modify the code of base class by adding also the arrow icon at the end of each axis line. An example is like the below:

For ArrowXAxisRenderer:

class ArrowXAxisRenderer(context: Context, viewPortHandler: ViewPortHandler?, xAxis: XAxis?, trans: Transformer?)
    : XAxisRenderer(viewPortHandler, xAxis, trans) {

    private val arrowRightIcon: Drawable?
    private val arrowWidth: Int
    private val arrowHeight: Int

    init {
        arrowRightIcon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_right)
        arrowWidth = arrowRightIcon!!.intrinsicWidth
        arrowHeight = arrowRightIcon.intrinsicHeight
    }

    override fun renderAxisLine(c: Canvas) {

        if (!mXAxis.isDrawAxisLineEnabled || !mXAxis.isEnabled) return

        mAxisLinePaint.color = mXAxis.axisLineColor
        mAxisLinePaint.strokeWidth = mXAxis.axisLineWidth
        mAxisLinePaint.pathEffect = mXAxis.axisLineDashPathEffect

        if (mXAxis.position == XAxis.XAxisPosition.TOP ||
            mXAxis.position == XAxis.XAxisPosition.TOP_INSIDE ||
            mXAxis.position == XAxis.XAxisPosition.BOTH_SIDED) {

            //draw the top X axis line
            c.drawLine(
                mViewPortHandler.contentLeft(),
                mViewPortHandler.contentTop(),
                mViewPortHandler.contentRight(),
                mViewPortHandler.contentTop(),
                mAxisLinePaint
            )

            //draw the arrowRightIcon on the right side of top X axis
            arrowRightIcon!!.bounds =
                Rect(
                    mViewPortHandler.contentRight().toInt() - arrowWidth / 2,
                    mViewPortHandler.contentTop().toInt() - arrowHeight / 2,
                    mViewPortHandler.contentRight().toInt() + arrowWidth / 2,
                    mViewPortHandler.contentTop().toInt() + arrowHeight / 2
                )
            arrowRightIcon.draw(c)
        }

        if (mXAxis.position == XAxis.XAxisPosition.BOTTOM ||
            mXAxis.position == XAxis.XAxisPosition.BOTTOM_INSIDE ||
            mXAxis.position == XAxis.XAxisPosition.BOTH_SIDED) {

            //draw the bottom X axis line
            c.drawLine(
                mViewPortHandler.contentLeft(),
                mViewPortHandler.contentBottom(),
                mViewPortHandler.contentRight(),
                mViewPortHandler.contentBottom(),
                mAxisLinePaint
            )

            //draw the arrowRightIcon on the right side of bottom X axis
            arrowRightIcon!!.bounds =
                Rect(
                    mViewPortHandler.contentRight().toInt() - arrowWidth / 2,
                    mViewPortHandler.contentBottom().toInt() - arrowHeight / 2,
                    mViewPortHandler.contentRight().toInt() + arrowWidth / 2,
                    mViewPortHandler.contentBottom().toInt() + arrowHeight / 2
                )
            arrowRightIcon.draw(c)
        }
    }
}

For ArrowYAxisRenderer:

class ArrowYAxisRenderer(context: Context, viewPortHandler: ViewPortHandler?, yAxis: YAxis?, trans: Transformer?)
    : YAxisRenderer(viewPortHandler, yAxis, trans) {

    private val arrowUpIcon: Drawable?
    private val arrowWidth: Int
    private val arrowHeight: Int

    init {
        arrowUpIcon = ContextCompat.getDrawable(context, R.drawable.ic_arrow_up)
        arrowWidth = arrowUpIcon!!.intrinsicWidth
        arrowHeight = arrowUpIcon.intrinsicHeight
    }

    override fun renderAxisLine(c: Canvas) {

        if (!mYAxis.isEnabled || !mYAxis.isDrawAxisLineEnabled) return

        mAxisLinePaint.color = mYAxis.axisLineColor
        mAxisLinePaint.strokeWidth = mYAxis.axisLineWidth

        if (mYAxis.axisDependency == YAxis.AxisDependency.LEFT) {

            //draw the left Y line axis
            c.drawLine(
                mViewPortHandler.contentLeft(),
                mViewPortHandler.contentTop(),
                mViewPortHandler.contentLeft(),
                mViewPortHandler.contentBottom(),
                mAxisLinePaint
            )

            //draw the arrowUp on top of the left Y axis
            arrowUpIcon!!.bounds =
                Rect(mViewPortHandler.contentLeft().toInt() - arrowWidth / 2,
                    mViewPortHandler.contentTop().toInt() - arrowHeight / 2,
                    mViewPortHandler.contentLeft().toInt() + arrowWidth / 2,
                    mViewPortHandler.contentTop().toInt() + arrowHeight / 2
                )
            arrowUpIcon.draw(c)
        }
        else {

            //draw the right Y line axis
            c.drawLine(
                mViewPortHandler.contentRight(),
                mViewPortHandler.contentTop(),
                mViewPortHandler.contentRight(),
                mViewPortHandler.contentBottom(),
                mAxisLinePaint
            )

            //draw the arrowUp on top of the right Y axis
            arrowUpIcon!!.bounds =
                Rect(
                    mViewPortHandler.contentRight().toInt() - arrowWidth / 2,
                    mViewPortHandler.contentTop().toInt() - arrowHeight / 2,
                    mViewPortHandler.contentRight().toInt() + arrowWidth / 2,
                    mViewPortHandler.contentTop().toInt() + arrowHeight / 2
                )
            arrowUpIcon.draw(c)
        }
    }
}

In the above samples i have used the below vector drawables where you can change based on your needs:

R.drawable.ic_arrow_right:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@android:color/darker_gray">
    <path
        android:fillColor="@android:color/darker_gray"
        android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z"/>
</vector>

R.drawable.ic_arrow_up:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@android:color/darker_gray">
    <path
        android:fillColor="@android:color/darker_gray"
        android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
</vector>

Usage for X axis:

chart.setXAxisRenderer(ArrowXAxisRenderer(this, chart.getViewPortHandler(), chart.getXAxis(), chart.getTransformer(YAxis.AxisDependency.LEFT)))

Usage for Y (Left or Right) axis:

chart.setRendererLeftYAxis(ArrowYAxisRenderer(this, chart.getViewPortHandler(), chart.getAxisLeft(), chart.getTransformer(YAxis.AxisDependency.LEFT)))
chart.setRendererRightYAxis(ArrowYAxisRenderer(this, chart.getViewPortHandler(), chart.getAxisRight(), chart.getTransformer(YAxis.AxisDependency.RIGHT)))

Result:

axis_arrows

Note: This was tested with 'com.github.PhilJay:MPAndroidChart:v3.1.0'

Upvotes: 4

Thomas Meinhart
Thomas Meinhart

Reputation: 689

I have checked the documentation and it seems that there is no option to render an arrow or something else on the end of one of the axis.

Maybe you can create an overlay for your chart, but i guess placing the arrows on the correct place is not a good idea.

Upvotes: 0

Related Questions