Krishna
Krishna

Reputation: 143

How to place the text values inside in MPAndroidChart circle?

I am developing a chart based application, I am using MPAndroidChart library, I need to place the text value inside of circle, i tried to display,Thanks for if any suggestions related this,

i attached a screenshot related to that issue. I need to be do like this

enter image description here

but i get like this image:

enter image description here

Thanks Again for helping this issue,

 ArrayList<Entry> e1 = new ArrayList<Entry>();

        float[] values = new float[]{48, 59, 79, 29, 39, 50, 60};
        for (int i = 0; i < values.length; i++) {
            e1.add(new Entry(values[i], i, "line3"));

        }
        int[] color = {Color.parseColor("#D13385"),    Color.parseColor("#37D04E"), Color.parseColor("#33D1D1"),  Color.parseColor("#D1C933")};
        LineDataSet d1 = new LineDataSet(e1, "" + cnt);
        d1.setColors(color);
        d1.setLineWidth(3.0f);
        d1.setCircleSize(7.0f);
        d1.setDrawValues(true);      
        d1.setCircleColor(Color.parseColor("#891e9a"));
        d1.setCircleColorHole(Color.parseColor("#891e9a"));
        d1.setDrawHighlightIndicators(false);
        d1.setDrawFilled(false);
        d1.setFillAlpha(20);     
        d1.setHighlightLineWidth(50f);
        d1.setValueTextSize(10f);

Upvotes: 2

Views: 3084

Answers (5)

Slin
Slin

Reputation: 1

There are two possibilities:

(1) Not so good: Shift the y-value of the label position

  • Two Data sets one for text and one for the line (including circles)
  • Modifiy the y-position for the text value with a constant offset

Pro: Easy

Con: The offset is not always constant (see offset is not always similar)

(2) Better: Override the drawValues method from LineChartRenderer

In LineChartRenderer.java -> drawValues the text is vertically shifted by this line:

drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2));

So to get rid of the "- valOffset":

1.Override the drawValues method

Create a new java file "CenteredTextLineChartRenderer.java" and override method drawValues from LineChartRenderer

2.Modify the y-valOffset to y+textHeight*0.35f

Add float textHeight = dataSet.getValueTextSize();

public class CenteredTextLineChartRenderer extends LineChartRenderer {

public CenteredTextLineChartRenderer(LineDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
    super(chart, animator, viewPortHandler);
}

//Modified drawValues Method
// Center label on coordinate instead of applying a valOffset

@Override
public void drawValues(Canvas c) {

    if (isDrawingValuesAllowed(mChart)) {

        List<ILineDataSet> dataSets = mChart.getLineData().getDataSets();

        for (int i = 0; i < dataSets.size(); i++) {

            ILineDataSet dataSet = dataSets.get(i);

            float textHeight = dataSet.getValueTextSize();

            if (!shouldDrawValues(dataSet) || dataSet.getEntryCount() < 1)
                continue;

            // apply the text-styling defined by the DataSet
            applyValueTextStyle(dataSet);

            Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

            // make sure the values do not interfear with the circles
            int valOffset = (int) (dataSet.getCircleRadius() * 1.75f);

            if (!dataSet.isDrawCirclesEnabled())
                valOffset = valOffset / 2;

            mXBounds.set(mChart, dataSet);

            float[] positions = trans.generateTransformedValuesLine(dataSet, mAnimator.getPhaseX(), mAnimator
                    .getPhaseY(), mXBounds.min, mXBounds.max);
            ValueFormatter formatter = dataSet.getValueFormatter();

            MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
            iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x);
            iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y);

            for (int j = 0; j < positions.length; j += 2) {

                float x = positions[j];
                float y = positions[j + 1];

                if (!mViewPortHandler.isInBoundsRight(x))
                    break;

                if (!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))
                    continue;

                Entry entry = dataSet.getEntryForIndex(j / 2 + mXBounds.min);

                if (dataSet.isDrawValuesEnabled()) {
                    //drawValue(c, formatter.getPointLabel(entry), x, y - valOffset, dataSet.getValueTextColor(j / 2));
                    drawValue(c, formatter.getPointLabel(entry), x, y+textHeight*0.35f, dataSet.getValueTextColor(j / 2));
                }

                if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {

                    Drawable icon = entry.getIcon();

                    Utils.drawImage(
                            c,
                            icon,
                            (int)(x + iconsOffset.x),
                            (int)(y + iconsOffset.y),
                            icon.getIntrinsicWidth(),
                            icon.getIntrinsicHeight());
                }
            }

            MPPointF.recycleInstance(iconsOffset);
        }
    }
}

}

3.Set your own LineChart renderer to your modified drawValues class

LineChart mChart = (LineChart) mainActivity.findViewById(R.id.LineChart);

mChart.setRenderer(new CenteredTextLineChartRenderer(mChart,mChart.getAnimator(),mChart.getViewPortHandler()));
  1. Run your code and manually adapt the 0.35f offset in your CenteredTextLineChartRenderer class

Now your text is always vertically centered!

IMPORTANT: With deleting the valOffset your label is not vertically centered as the text anchor is not in the center of your text label. So you have to insert a manual offset "textHeight*0.35f" (just try it out). But the big advantage of method (2) is that the text is always centered with the same offset also for example in landscape mode and on other screen sizes...

Upvotes: 0

Dinith
Dinith

Reputation: 1

there was an easy way to do this

custom maker view

<TextView
    android:id="@+id/tvContent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_marginTop="7dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:text=""
    android:textSize="12sp"
    android:textColor="@android:color/white"
    android:ellipsize="end"
    android:singleLine="true"
    android:textAppearance="?android:attr/textAppearanceSmall" />

Marker view class

public class MyMarkerView extends MarkerView {

private final TextView tvContent;

public MyMarkerView(Context context, int layoutResource) {
    super(context, layoutResource);

    tvContent = findViewById(R.id.tvContent);
}

// runs every time the MarkerView is redrawn, can be used to update the
// content (user-interface)
@SuppressLint("SetTextI18n")
@Override
public void refreshContent(Entry e, Highlight highlight) {

    if (e instanceof CandleEntry) {

        CandleEntry  ce = (CandleEntry) e;

        tvContent.setText(Utils.formatNumber(ce.getHigh(), 0, true)+(ce.getData()));
    } else {

        tvContent.setText(Utils.formatNumber(e.getY(), 0, true)+"\n  "+(e.getData()));
    }

    super.refreshContent(e, highlight);
}

@Override
public MPPointF getOffset() {
    return new MPPointF(-(getWidth() / 2), -getHeight());
}

} and lastly in your activity

 values.add(new Entry(i, val,"Custom message per value"));

Preview be like example screen shot

Upvotes: 0

Dinith
Dinith

Reputation: 1

there was an easy way to do this custom maker view

Marker view

public class MyMarkerView extends MarkerView {

private final TextView tvContent;

public MyMarkerView(Context context, int layoutResource) {
    super(context, layoutResource);

    tvContent = findViewById(R.id.tvContent);
}

// runs every time the MarkerView is redrawn, can be used to update the
// content (user-interface)
@SuppressLint("SetTextI18n")
@Override
public void refreshContent(Entry e, Highlight highlight) {

    if (e instanceof CandleEntry) {

        CandleEntry  ce = (CandleEntry) e;

        tvContent.setText(Utils.formatNumber(ce.getHigh(), 0, true)+(ce.getData()));
    } else {

        tvContent.setText(Utils.formatNumber(e.getY(), 0, true)+"\n  "+(e.getData()));
    }

    super.refreshContent(e, highlight);
}

@Override
public MPPointF getOffset() {
    return new MPPointF(-(getWidth() / 2), -getHeight());
}

} and lastly in your activity

 values.add(new Entry(i, val,"Custom message per value"));

Preview be like example screen shot

Upvotes: 0

Roman T.
Roman T.

Reputation: 393

It is a bit "hacky", but I've managed to achieve such layout You've provided by creating two sets of data and attaching them to same chart. One set (lets call it "dots") contains your data needed to be displayed as dots. The second one ("lines") is a bit offset downwards (y value minus some experimentally picked value). Now you can set no line displaying for "lines" and a lines for "dots", no values labels for "dots" and white labels for "lines" and by experimentally moving your y values back and forth you can achieve overlaying values labels from one chart on top of another ("dots" will be covered by "lines" values).

UPDATE: Actually, I've an answer more elegant, that I've provided! Use Highlight[] and create array of highlights.

   highlihts = new Highlight[values_dots.size()];

  for (int i = 0; i < values_dots.size(); i++) {
    Highlight h = new Highlight(values_dots.get(i).getX(),values_dots.get(i).getY(), 0);
    highlihts[i] = h;
  }
  chart.highlightValues(highlihts);

In CustomMarkerView class position marker like so:

  @Override
    public MPPointF getOffset() {
      return new MPPointF(-(getWidth() / 2), -(getHeight() / 2));
    }

Boom

P.S. Philipp Jahoda, awesome library!

Upvotes: 0

Philipp Jahoda
Philipp Jahoda

Reputation: 51411

Currently it is not possible to change the position where the values are drawn by default. You will have to modify the library to get that behaviour.

Upvotes: 1

Related Questions