Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom view of Limit Line in MPAndroidChart

Is it possible to replace LimitLine with custom layout? So it looks something like this:

enter image description here

I see few solutions for that:

  1. Perhaps there're methods inside library for customizations like this, are there any?
  2. Get coordinates of TextView where the value is stored and add custom layout on that place. But how can I reach this TextView?

Perhaps someone faced that problem. Please share your experience.


EDIT: latest partial solution

After long searching of solution I came up to adding custom view programmatically by coordinates of Limit Line.

General layout of screen as follows:

enter image description here

Coordinates are calculated pretty straightforward. X is known from Chart coordinates and Y:

Y = ((max - lastValue) * height) / ((max + min) + y)

So at this point I basically know the position I need. Although I'm not sure it's right due to parent ScrollView.

The next step is to add custom layout at those coordinates (x, y)

enter image description here

And here comes the new problem. I tried to add view to top RelativeLayout. It is added but it doesn't move together with ScrollView. Therefore its needed to add that view exactly on Chart. Look how I tried to achieve this:

private void addCustomLayoutOnLimitLine(final double lastValue) {

    mChart.post(new Runnable() { //check location when view is created
        public void run() {
            int[] chartLocationOnScreen = new int[2];
            mChart.getLocationOnScreen(chartLocationOnScreen);

            int x = chartLocationOnScreen[0];
            int y = chartLocationOnScreen[1];

            int width = mChart.getWidth();
            int height = mChart.getHeight();


            double max = mChart.getYMax();
            double min = mChart.getYMin();

            int limitXPoint = x + width;
            int limitYPoint = (int) ((((max - lastValue) * height) / (max + min))+ y);



            LayoutInflater inflater = (LayoutInflater)   getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            RelativeLayout rlValue = (RelativeLayout) inflater.inflate(R.layout.item_chart_value, null);
            TextView tvValue = (TextView) rlValue.findViewById(R.id.tv_value);
            tvValue.setText(String.valueOf(lastValue));

            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(50, 50);
            params.leftMargin = limitXPoint - 100;
            params.topMargin = limitYPoint;
            mChart.addView(rlValue, params); //this doesn't seem to be working
            rlValue.bringToFront();
        }
    });
}

Perhaps I should reach the parent layout of Chart and inflate my custom layout there. But how?


EDIT 2: added custom view over chart but can't find proper location due to scroll view

Now the situation looks like that :

enter image description here

Perhaps I'm somewhere wrong in calculations. But at least that View changes its position with the new value although it never hits at right coordinations.

private void addCustomLayoutOnLimitLine() {
    if (mChart == null){
        return;
    }

    mChart.post(new Runnable() { //check location when view is created
        public void run() {
            int[] chartLocationOnScreen = new int[2];
            mChart.getLocationOnScreen(chartLocationOnScreen);

            int xChart = chartLocationOnScreen[0];
            int yChart = chartLocationOnScreen[1];

            int chartWidth = mChart.getWidth();
            int chartHeight = mChart.getHeight();

            int rootWidth = rlSvContent.getWidth();
            int rootHeight = rlSvContent.getHeight(); //this is height of ScrollView

            int infoWidth = llInfoWrapper.getWidth(); //width of info panel ABOVE chart
            int infoHeight = llInfoWrapper.getHeight();

            double lastValue = mSingleAsset.getGraph().get(mSingleAsset.getGraph().size() - 1).getValue();
            double maxValue = mChart.getYMax();
            double minValue = mChart.getYMin();

            int limitXPoint = (rootWidth - chartWidth) / 2 + chartWidth;
            int limitYPoint = (int) ((maxValue - lastValue) * chartHeight/(maxValue - minValue)) + yChart;

            tvCustomValue.setText(SingleAsset.round((float) lastValue, 2).toString()); //display last value on custom view

            RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.leftMargin = limitXPoint - xChart - 50; //move custom view. xChart = right margin value and 50 is taken to count values bar to the right of chart
            params.topMargin = limitYPoint;
            rlCustomValue.setLayoutParams(params);
            rlCustomValue.bringToFront();
            rlCustomValue.invalidate();
        }
    });
}
like image 394
AnZ Avatar asked Mar 18 '16 12:03

AnZ


2 Answers

<ScrollView>

    <LinearLayout/>

    <FrameLayout>

        <Chart/>
        <TextView/>

    <FrameLauyout>

</ScrollView>

Use ViewPortHandler to get offset of chart

    float offsetTop = mChart.getViewPortHandler().offsetTop();
    float offsetLeft = mChart.getViewPortHandler().offsetLeft();
    float offsetRight = mChart.getViewPortHandler().offsetRight();
    float chartHeight = mChart.getViewPortHandler().contentHeight();
like image 63
tiny sunlight Avatar answered Nov 02 '22 23:11

tiny sunlight


This is not a good way of doing it. I've done it by extending the YAxisRenderer.java file where the labels are actually drawn. They are not views, they are drawn on canvas. Here is my code for the labels:

`

protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) {

    // draw labels
    for (int i = 0; i < mYAxis.mEntryCount; i++) {

        String text = mYAxis.getFormattedLabel(i);

        if (!mYAxis.isDrawTopYLabelEntryEnabled() && i >= mYAxis.mEntryCount - 1)
            return;

        c.drawText(text, fixedPosition, positions[i * 2 + 1] + offset, mAxisLabelPaint);
    }

    // limitline labels

    List<LimitLine> limitLines = mYAxis.getLimitLines();
    float[] pts = new float[2];
    for (LimitLine l : limitLines) {
        Paint paint = new Paint();
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(l.getTextColor());

        Paint textPaint = mAxisLabelPaint;
        textPaint.setColor(l.getLineLabelTextColor());
        textPaint.setTextSize(mAxisLabelPaint.getTextSize());
        textPaint.setPathEffect(null);
        textPaint.setTypeface(l.getTypeface());
        textPaint.setStrokeWidth(0.5f);
        textPaint.setStyle(l.getTextStyle());

        pts[1] = l.getLimit();
        mTrans.pointValuesToPixel(pts);
        float paddingVert = Utils.convertDpToPixel(3);
        float paddingHoriz = Utils.convertDpToPixel(5);
        float height = Utils.calcTextHeight(textPaint, l.getLabel());
        float width = Utils.calcTextWidth(textPaint, l.getLabel());
        float posY = pts[1] + height / 2;

        c.drawRect(fixedPosition - paddingHoriz, posY - height - paddingVert, fixedPosition + width + paddingHoriz*2, posY + paddingVert, paint);
        c.drawText(l.getLabel(), fixedPosition, posY, textPaint);
    }

}

`

Please note that you must use mTrans.pointValuesToPixel(pts) to convert your Y values to pixels.

like image 27
Stefan Bushev Avatar answered Nov 03 '22 00:11

Stefan Bushev