为MPAndroidChart之RadarChart添加lable添加点击事件

关于RadarChart

先贴上MPAndroidChart 的GitHub上地址:https://github.com/PhilJay/MPAndroidChart 

RadarChart的使用这里不做介绍,如有需要请自行查阅!

直奔主题,查看RadarChart的监听方法,仅仅提供了OnChartValueSelectedListener方法,改方法仅能作用于图形上的点击事件,并不能实现lable点击事件(及RadarChart的x轴点击事件),所以需要自己实现其方法;

分析

想要实现lable点击事件,我们可以实现RadarChart的setOnTouchListener()方法,从触摸的位置判断点击区域是否在RadarChart的文字区域,查看RadarChat源码,发现RadarChat的lable绘制主要依靠XAxisRendererRadarChart类实现,

其中drawLable方法则是调用MPAndroidChart中的Util方法

参照其源码的实现方式,可以计算每个lable的绘制区域Rect及起始绘制点,

/**
 * 计算位置
 * @param compositeRadar
 * @return
 */
public static List<RadarPointBean> computePosition(RadarChart compositeRadar) {
    List<RadarPointBean> pointBeans = new ArrayList<>();
    XAxis xAxis = compositeRadar.getXAxis();
    final float labelRotationAngleDegrees = xAxis.getLabelRotationAngle();
    final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f);
    float sliceangle = compositeRadar.getSliceAngle();
    float factor = compositeRadar.getFactor();
    MPPointF center = compositeRadar.getCenterOffsets();
    MPPointF pOut = MPPointF.getInstance(0, 0);

    for (int i = 0; i < compositeRadar.getData().getMaxEntryCountSet().getEntryCount(); i++) {
        String label = xAxis.getValueFormatter().getFormattedValue(i, xAxis);

        float angle = (sliceangle * i + compositeRadar.getRotationAngle()) % 360f;

        Utils.getPosition(center, compositeRadar.getYRange() * factor
                + xAxis.mLabelRotatedWidth / 2f, angle, pOut);

        pointBeans.add(computeStartPoint(label, pOut.x, pOut.y - xAxis.mLabelRotatedHeight / 2.f,
               mAxisLabelPaint, drawLabelAnchor, labelRotationAngleDegrees));
    }
    return pointBeans;
}

computeStartPoint方法:

 /**
     * 计算文字绘制起点
     * @param text
     * @param x
     * @param y
     * @param paint
     * @param anchor
     * @param angleDegrees
     * @return
     */
    private static RadarPointBean computeStartPoint(String text, float x, float y,
                                                    Paint paint,
                                                    MPPointF anchor, float angleDegrees) {
        mDrawTextRectBuffer = new Rect();
        mFontMetricsBuffer = new Paint.FontMetrics();
        float drawOffsetX = 0.f;
        float drawOffsetY = 0.f;
        final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer);
        paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer);

        drawOffsetX -= mDrawTextRectBuffer.left;

        drawOffsetY += -mFontMetricsBuffer.ascent;

        if (angleDegrees != 0.f) {
            drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f;
            drawOffsetY -= lineHeight * 0.5f;
        } else {
            if (anchor.x != 0.f || anchor.y != 0.f) {
                drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x;
                drawOffsetY -= lineHeight * anchor.y;
            }
            drawOffsetX += x;
            drawOffsetY += y;
        }
        return new RadarPointBean(drawOffsetX, drawOffsetY, mDrawTextRectBuffer);
    }



其中RadarPointBean为保存每个lable的起始点及Rect,其属性为:

public class RadarPointBean {
    float startX;
    float startY;
    Rect rect;
    public RadarPointBean(float startX, float startY, Rect rect) {
        this.startX = startX;
        this.startY = startY;
        this.rect = rect;
    }

}

这里需要一个mAxisLabelPaint,继续查看,mAxisLabelPaint是AxisRenderer中的属性,

然而RadarChart中有mXAxisRenderer属性,

并且XAxisRendererRadarChart继承与XAxisRenderer,


因此只要回去到RadarChart中的,便能够获取到Paint,但是mXAxisRenderer是protected属性,而且RadarChart中没有提供外部调用方法,我们可以通过反射或者定义RadarChart子类的方法获取到XAxisRendererChart,这里我采用反射获取,从效率来讲,定义RadarChart子类比反射获取更好;

/**
 * 反射获取XAxisRendererRadarChart
 * @param compositeRadar
 * @return
 */
private static XAxisRendererRadarChart getXAxisRendererRadarChart(RadarChart compositeRadar) {
    try {
        Field field = compositeRadar.getClass().getDeclaredField("mXAxisRenderer");
        field.setAccessible(true);
        XAxisRendererRadarChart mXAxisRenderer = (XAxisRendererRadarChart) field.get(compositeRadar);
        return mXAxisRenderer;
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;

}

然后修改computePosition方法:

/**
 * 计算位置
 * @param compositeRadar
 * @return
 */
public static List<RadarPointBean> computePosition(RadarChart compositeRadar) {
    List<RadarPointBean> pointBeans = new ArrayList<>();
    XAxis xAxis = compositeRadar.getXAxis();
    final float labelRotationAngleDegrees = xAxis.getLabelRotationAngle();
    final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f);
    XAxisRendererRadarChart mXAxisRenderer = getXAxisRendererRadarChart(compositeRadar);
    float sliceangle = compositeRadar.getSliceAngle();
    float factor = compositeRadar.getFactor();
    MPPointF center = compositeRadar.getCenterOffsets();
    MPPointF pOut = MPPointF.getInstance(0, 0);

    for (int i = 0; i < compositeRadar.getData().getMaxEntryCountSet().getEntryCount(); i++) {
        String label = xAxis.getValueFormatter().getFormattedValue(i, xAxis);

        float angle = (sliceangle * i + compositeRadar.getRotationAngle()) % 360f;

        Utils.getPosition(center, compositeRadar.getYRange() * factor
                + xAxis.mLabelRotatedWidth / 2f, angle, pOut);

        pointBeans.add(computeStartPoint(label, pOut.x, pOut.y - xAxis.mLabelRotatedHeight / 2.f,
                mXAxisRenderer.getPaintAxisLabels(), drawLabelAnchor, labelRotationAngleDegrees));
    }
    return pointBeans;
}

这样便获取到了每个lable的绘制起点及绘制区域,然后添加事件:

float x = 0;//临时保存点击坐标
float y = 0;//临时保存点击坐标
        compositeRadar.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {

                List<RadarPointBean> pointBeans = RadarUtil.computePosition(compositeRadar);

                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        x = event.getX();
                        y = event.getY();
                        break;
                    case MotionEvent.ACTION_UP:
                        for (int i = 0; i < pointBeans.size(); i++) {
                            RadarPointBean pointBean = pointBeans.get(i);
                            if (pointBean.isIn(x, y)) {
                                if (radarDatas != null && radarDatas.size() >= i) {
                                    ToastUtils.showShort("点击第" + i + "个lable");
                                    LogUtils.i("点击第" + i + "个lable");
                                    StudentDimensionBean bean = radarDatas.get(i);
                                    gotoCompositeDetail(bean);
                                }
                                return true;
                            }
                        }
                        break;
                    default:

                        break;
                }
                return false;
            }
        });

RadarPointBean中的isIn方法为:

private static final int DEF_PADDING = 25;//为文字增加点击区域 相当于padding

public boolean isIn(float x, float y) {
    float endX = startX + Math.abs(rect.right - rect.left) + DEF_PADDING;
    float endY = startY - Math.abs(rect.bottom - rect.top) - DEF_PADDING;
    float startX = getStartX() - DEF_PADDING;
    float startY = getStartY() + DEF_PADDING;
    return startX < x && x < endX && startY > y && y > endY;
}

效果

结语

获取XAxisRendererRadarChart从效率上建议使用定义RadarChart子类方式:

public class CustomRadarChart extends RadarChart {
    public CustomRadarChart(Context context) {
        super(context);
    }
    public XAxisRendererRadarChart getXAxisRenderer(){
        return mXAxisRenderer;
    }
}

如有其它更好的方法,欢迎探讨.

demo地址:戳这里

猜你喜欢

转载自blog.csdn.net/jy494495991/article/details/80809949