為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地址:戳這裡