2017-01-31 3 views
1

Я использую mpandroid charting library. Я изменил PieChartRenderer и смог получить такой график.Нажмите события на круговой диаграмме - mpandroid

enter image description here

PieChartRenderer

public class ImagePieChartRenderer extends PieChartRenderer { 

    private ArrayList<Bitmap> bitMaps; 
    private Paint mEntryLabelsPaint; 
    public ImagePieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, ArrayList<Bitmap> bitMaps) { 
     super(chart, animator, viewPortHandler); 
     this.bitMaps = bitMaps; 
     mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
     mEntryLabelsPaint.setColor(Color.BLACK); 
     mEntryLabelsPaint.setTextAlign(Paint.Align.CENTER); 
     mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f)); 
    } 

    @Override 
    protected void drawDataSet(Canvas c, IPieDataSet dataSet) { 
     drawImages(c); 
     super.drawDataSet(c, dataSet); 
    } 

    private void drawImages(Canvas c) { 

     MPPointF center = mChart.getCenterCircleBox(); 

     // get whole the radius 
     float radius = mChart.getRadius(); 
     float rotationAngle = mChart.getRotationAngle(); 
     float[] drawAngles = mChart.getDrawAngles(); 
     float[] absoluteAngles = mChart.getAbsoluteAngles(); 

     float phaseX = mAnimator.getPhaseX(); 
     float phaseY = mAnimator.getPhaseY(); 

     final float holeRadiusPercent = mChart.getHoleRadius()/100.f; 
     float labelRadiusOffset = radius/10f * 3.6f; 


     labelRadiusOffset = (radius - (radius * holeRadiusPercent))/2f; 


     final float labelRadius = radius - labelRadiusOffset; 

     PieData data = mChart.getData(); 
     List<IPieDataSet> dataSets = data.getDataSets(); 

     float yValueSum = data.getYValueSum(); 

     float angle; 
     int xIndex = 0; 

     c.save(); 

     float offset = Utils.convertDpToPixel(5.f); 

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


      IPieDataSet dataSet = dataSets.get(i); 

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

      float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") 
        + Utils.convertDpToPixel(4f); 

      IValueFormatter formatter = dataSet.getValueFormatter(); 

      int entryCount = dataSet.getEntryCount(); 

      mValueLinePaint.setColor(dataSet.getValueLineColor()); 
      mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth())); 

      final float sliceSpace = getSliceSpace(dataSet); 

      for (int j = 0; j < entryCount; j++) { 

       PieEntry entry = dataSet.getEntryForIndex(j); 

       if (xIndex == 0) 
        angle = 0.f; 
       else 
        angle = absoluteAngles[xIndex - 1] * phaseX; 

       final float sliceAngle = drawAngles[xIndex]; 
       final float sliceSpaceMiddleAngle = sliceSpace/(Utils.FDEG2RAD * labelRadius); 

       // offset needed to center the drawn text in the slice 
       final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle/2.f)/2.f; 

       angle = angle + angleOffset; 

       final float transformedAngle = rotationAngle + angle * phaseY; 

       float value = mChart.isUsePercentValuesEnabled() ? entry.getY() 
         /yValueSum * 100f : entry.getY(); 

       final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD); 
       final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD); 

       final float valueLineLength1 = dataSet.getValueLinePart1Length(); 
       final float valueLineLength2 = dataSet.getValueLinePart2Length(); 
       final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage()/100.f; 

       float pt2x, pt2y; 
       float labelPtx, labelPty; 
       float percentX, percentY; 

       float line1Radius; 

       line1Radius = (radius - (radius * holeRadiusPercent)) 
         * valueLinePart1OffsetPercentage 
         + (radius * holeRadiusPercent); 

       final float polyline2Width = dataSet.isValueLineVariableLength() 
         ? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin(
         transformedAngle * Utils.FDEG2RAD)) 
         : labelRadius * valueLineLength2; 

       final float pt0x = line1Radius * sliceXBase + center.x; 
       final float pt0y = line1Radius * sliceYBase + center.y; 

       final float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x; 
       final float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y; 

       if (transformedAngle % 360.0 >= 40.0 && transformedAngle % 360.0 <= 130.0) { 
        pt2x = pt1x + polyline2Width; 
        pt2y = pt1y; 

        Log.d(entry.getLabel(), "TWO "+ transformedAngle % 360.0); 

        mValuePaint.setTextAlign(Paint.Align.LEFT); 

        mEntryLabelsPaint.setTextAlign(Paint.Align.LEFT); 

        labelPtx = pt2x; 
        labelPty = pt2y; 

        percentX = labelPtx - 1.5f*offset ; 
        percentY = labelPty + bitMaps.get(j).getHeight() + 2.2f*offset; 
       } else { 
        pt2x = pt1x - polyline2Width; 
        pt2y = pt1y; 

        Log.d(entry.getLabel(), "ONE "+ transformedAngle % 360.0); 

        mValuePaint.setTextAlign(Paint.Align.RIGHT); 

        mEntryLabelsPaint.setTextAlign(Paint.Align.RIGHT); 


        labelPtx = pt2x; 
        labelPty = pt2y; 

        percentX = labelPtx + 1.5f*offset; 
        percentY = labelPty - bitMaps.get(j).getHeight() - 0.5f*offset; 
       } 



       mValueLinePaint.setColor(dataSet.getColor(j)); 

       c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint); 
       c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint); 


       // draw everything, depending on settings 



       drawValue(c, 
         formatter, 
         value, 
         entry, 
         0, 
         percentX, 
         percentY, 
         dataSet.getValueTextColor(j)); 

       Paint paint = new Paint(); 
       paint.setStyle(Paint.Style.FILL); 
       paint.setColor(dataSet.getColor(j)); 
       c.drawCircle(labelPtx, labelPty, bitMaps.get(j).getWidth(), paint); 
       c.drawBitmap(bitMaps.get(j), labelPtx-bitMaps.get(j).getWidth()/2f, labelPty-bitMaps.get(j).getHeight()/2, null); 

//    if (j < data.getEntryCount() && entry.getLabel() != null) { 
//     drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight); 
//    } 

       xIndex++; 
      } 
     } 
     MPPointF.recycleInstance(center); 
     c.restore(); 
    } 
} 

Теперь мне нужно, чтобы иметь возможность нажмите на маленькие круги вокруг диаграммы. Может кто-то, пожалуйста, укажите мне классы и функции, которые необходимо изменить для достижения этого.

В основном я хочу вызвать onValueSelected(Entry e, Highlight h), когда касаюсь кругов.

ответ

1

Прежде всего, здорово видеть, как люди пишут собственные средства визуализации. Выглядит потрясающе!

Для вашего фактического вопроса, класс, который вы хотите, называется OnChartGestureListener. Исходный код: here.

Прежде всего, вам, вероятно, потребуется кэшировать позиции ваших кругов и растровых изображений внутри вашего рендеринга. Что-то вроде этого:

public ImagePieChartRenderer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler, ArrayList<Bitmap> bitMaps, LabelPointCache labelPointCache) { 
    super(chart, animator, viewPortHandler); 
    this.bitMaps = bitMaps; 
    this.labelPointCache = labelPointCache; 
    mEntryLabelsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 
    mEntryLabelsPaint.setColor(Color.BLACK); 
    mEntryLabelsPaint.setTextAlign(Paint.Align.CENTER); 
    mEntryLabelsPaint.setTextSize(Utils.convertDpToPixel(13f)); 
} 

//snip, then: 

    c.drawCircle(labelPtx, labelPty, bitMaps.get(j).getWidth(), paint); 
    c.drawBitmap(bitMaps.get(j), labelPtx-bitMaps.get(j).getWidth()/2f, labelPty-bitMaps.get(j).getHeight()/2, null); 
//then: 
    float entryX = dataSet.getEntry().getX(); 
    float entryY = dataSet.getEntry().getY(); 
    labelPositionCache.add(entryX, entryY, labelPtx, labelPty); 

Теперь создадим класс, который реализует этот интерфейс, скажем, MyOnChartGestureListener. Теперь установите его на графике:

PieChartRenderer myRenderer = new ImagePieChartRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler(), bitmaps, labelPositionCache); 
mChart.setOnChartGestureListener(new MyOnChartGestureListener(mChart, labelPointCache)); 

Затем вам необходимо переопределить метод для случая движения вы хотите слушать. Допустим, вы хотите слушать одиночными нажатиями Следующий фрагмент кода показывает, как перейти от сырьевой похлопал x и y на графике xValue и yValue на графике:

@Override 
public void onChartSingleTapped(MotionEvent me) { 
    float tappedX = me.getX(); 
    float tappedY = me.getY(); 
    MPPointD point = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(tappedX, tappedY); 
    Log.d(TAG, "tapped at: " + point.x + "," + point.y); 
} 

Вам понадобится какой-нибудь способ, чтобы проверить, что point.x и point.y находятся в пределах вашего круга или растровых изображений. Затем вы можете проверить, является ли point.x и point.y от жеста падения внутри него, как это:

if (labelPositionCache.contains(point.x, point.y)) { 
    float entryX = labelPositionCache.getEntryXForLabelX(point.x); 
    float entryY = labelPositionCache.getEntryYForLabelY(point.y); 
    Highlight highlight = mChart.getHighlightByTouchPoint(entryX, entryY); 
    mChart.highlightValues(new Highlight [] { highlight }); 
} 
+0

Спасибо. Я дам вам шанс и дам вам знать. – amarok

+0

Я обновил свой ответ - то, что вы просите, немного сложнее, чем я думал. Я думаю, вам придется кэшировать значения x и y для среза, когда вы кешируете метки x и y, чтобы вы могли сопоставить их. –

+0

Просто хотелось убедиться, что LabelPointCache является настраиваемым объектом и не принадлежит самой библиотеке. Кроме того, не будет ли эта логика искать одну точку вместо того, чтобы искать всю область круга. – amarok

Смежные вопросы