23

I am using a MarkerView class to show markerviews in the chart. The markerview layout I have created contains two textview, one below the other.

The problem I am facing is that the markerview for the last point on the chart is half within the chart and half outside the chart. The two images below state the problem clearly :

The first image shows a markerview for a point in the center of the chart which shows without any problem :

enter image description here

The second image, as shown below, shows the markerview for the last point of the chart, which is half within the chart.

enter image description here

How do I get this markerview to adjust so that it shows within the chart area.

The wiki does not state any customizations for the markerview. Are there any more customizations?

Also, in case of a multiple line chart, if I click only on one of the line points, the markerview comes without a problem. But if I click for the markerview on any point on the other line, the application fails. Any idea why this happens.

The code for the markerview class is given below :

public class TooltipView extends MarkerView {

private BaseGraphMetadata mBaseGraphMetadata;

private TextView mTooltipDate, mTooltipValue;

public TooltipView(Context context, int layoutResource, BaseGraphMetadata baseGraphMetadata) {
    super(context, layoutResource);
    mTooltipDate = (TextView)findViewById(R.id.tooltip_date);
    mTooltipValue = (TextView)findViewById(R.id.tooltip_value);
    mBaseGraphMetadata = baseGraphMetadata;
}

@Override
public void refreshContent(Entry entry, int i) {
    List<DrillDownInfo> drillDownInfoList = (List<DrillDownInfo>) entry.getData();
    DrillDownInfo drillDownInfo = drillDownInfoList.get(i);
    Map<String, String> group = drillDownInfo.getGroupByNameVsGroupByValue();
    Iterator iterator = group.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry pair = (Map.Entry)iterator.next();
        if(pair.getKey()!=null && pair.getValue()!=null) {
            String key = (String) pair.getKey();
            key = key.toUpperCase();
            Double value = Double.parseDouble((String) pair.getValue());
            String formattedValue = mBaseGraphMetadata.getDataFormatter().getFormattedValue(value);
            mTooltipDate.setText(key + " : " + formattedValue);
        }
        iterator.remove();
    }
    mTooltipValue.setText(String.valueOf("VALUE : "+entry.getVal()));
}

@Override
public int getXOffset() {
    return -(getWidth() / 2);
}

@Override
public int getYOffset() {
    return -getHeight();
}
}
Arjun Issar
  • 672
  • 4
  • 13
  • 32

8 Answers8

28

I have found a simple solution to this issue. You need to set chart in your custom marker view as below:

setChartView(chart);

..and override the draw method as below:

@Override
public void draw(Canvas canvas, float posX, float posY) {
    super.draw(canvas, posX, posY);
    getOffsetForDrawingAtPoint(posX, posY);
}
9

Since

android:clipChildren="false"
android:clipToPadding="false"

did not work for me, I ended up creating 3 different drawables. Depending on the Entry index, I use one or the other. And, in order to keep the arrow centered, the XOffset changes accordingly. I guess it's a very specific solution, but the idea is exportable

  @Override
  public void refreshContent(Entry e, int dataSetIndex) {
    if (e.getXIndex() < 4) {
      xOffsetMultiplier = 3.2f;
      tvContent.setBackgroundResource(R.drawable.stats_marker_left_side);
    } else if (e.getXIndex() > mTimestamps.length - 6) {
      //timestamps is an array containing xValues timestamps
      xOffsetMultiplier = 1.12f;
      tvContent.setBackgroundResource(R.drawable.stats_marker_right_side);
    } else {
      xOffsetMultiplier = 2;
      tvContent.setBackgroundResource(R.drawable.stats_marker);
    }

    tvContent.setText(createMarkerViewText(e));
  }

  @Override
  public int getXOffset() {
    // this will center the marker-view horizontally
    return (int) -(getWidth() / xOffsetMultiplier);
  }

This is the result

enter image description here enter image description here enter image description here

The drawables are copied from MPAndroidChart samples, transformed to 9.patch and adapted to my needs. Place them inside drawable-nodpi

enter image description here enter image description here enter image description here

Maragues
  • 37,861
  • 14
  • 95
  • 96
  • There are tools to create 9 patch images. But how do they function? Also how do i convert a normal image to 9 patch? – Arjun Issar May 19 '15 at 11:31
7

A bit late, but here is my easy solution for this problem.

You need to Override the draw method of your custom MarkerView.

You just have then to control that you posx is not going to close from one of your border (from 0 for the right side to location depending on your screen size, as an example 300 for my own screen).

A simple example:

@Override
        public void draw(Canvas canvas, float posx, float posy)
        {
            // take offsets into consideration
            posx += getXOffset();
            posy=0;

            // AVOID OFFSCREEN
            if(posx<45)
                posx=45;
            if(posx>265)
                posx=265;

            // translate to the correct position and draw
            canvas.translate(posx, posy);
            draw(canvas);
            canvas.translate(-posx, -posy);
        }

Here, I can control my x position while ensuring that the marker remain always on the top of the graph (If you dont want it, use posy += getYOffset(); instead)

Virthuss
  • 3,142
  • 1
  • 21
  • 39
5

Just put some logic in your MarkerView. If the last value is highlighted, the getXOffset() method should return something different.

What is returned, is calculated in the refreshContent(...) method.

Also, you can take a look at the following xml properties:

android:clipChildren="false"
android:clipToPadding="false"

Which should be set for the chart-view.

Kaushik
  • 6,150
  • 5
  • 39
  • 54
Philipp Jahoda
  • 50,880
  • 24
  • 180
  • 187
4

I faced the same problem. I rectified it by making my own customMarkerview class particularly the below method:

@Override
public int getXOffset(float xpos) {

    // this will center the marker-view horizontally
   int min_offset = 50;
    if (xpos < min_offset)
        return 0;

    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics metrics = new DisplayMetrics();
    wm.getDefaultDisplay().getMetrics(metrics);
    //For right hand side
    if (metrics.widthPixels - xpos < min_offset)
        return -getWidth();
            //For left hand side
    else if (metrics.widthPixels - xpos < 0)
        return -getWidth();
    return -(getWidth() / 2);
}
Anshul Tyagi
  • 2,076
  • 4
  • 34
  • 65
4

try this:

    private val uiScreenWidth = resources.displayMetrics.widthPixels

    override fun draw(canvas: Canvas?, posX: Float, posY: Float) {
        var newPosX = posX
        if (uiScreenWidth - posX < width) {
            newPosX -= width
        }
        super.draw(canvas, newPosX, posY)
    }
1

You can rectify this by adding chart.setDragOffsetX(50); . This will add padding on x-axis. Thus, giving space for the markerview to be displayed.

Twinkle
  • 37
  • 11
0

The best answer lies in the library itself. Check the method getOffsetForDrawingAtPoint() from MarkerView . It has defined the perfect way to calculate the offset. Use that logic from there and use in onDraw method of your custom marker as following:

 @Override
    public void draw(Canvas canvas, float posX, float posY) {
        // take offsets into consideration
        int lineChartWidth = 0;
        float offsetX = getOffset().getX();
        posY=0;//fix at top
        float width = getWidth();

        if(lineChart != null) {
            lineChartWidth = lineChart.getWidth();
        }
        if(posX + offsetX < 0) {
            offsetX = - posX;
        } else if(posX + width + offsetX > lineChartWidth) {
            offsetX = lineChartWidth - posX - width;
        }
        posX += offsetX;
        // translate to the correct position and draw
        canvas.translate(posX, posY);
        draw(canvas);
        canvas.translate(-posX, -posY);
    }
Amit
  • 3,422
  • 3
  • 28
  • 39
  • 5
    You don't need to override draw, if you call setChartView(chart) then getOffsetForDrawingAtPoint() will calculate the offset correctly – Jake Walsh Oct 24 '17 at 06:38