糖尿病康复,内容丰富有趣,生活中的好帮手!
糖尿病康复 > 安卓可上下滑动改变数值的折线图(基于hellochart)

安卓可上下滑动改变数值的折线图(基于hellochart)

时间:2021-12-25 15:00:05

相关推荐

安卓可上下滑动改变数值的折线图(基于hellochart)

需求

接手的公司的一个项目,有一个需求是折线图可以通过上下滑动改变数值。原先的大佬自己从头写的,也能实现功能。大佬后来也把思路和代码分享出来了。大家有兴趣的可以看一下。

手把手教你写一个可以上下滑动点改变值的安卓折线图

因为后来需求有些变动,原来的代码改动起来略显吃力,于是自己又以hellocharts为基础重新封装了一个自定义VIew,以此来实现功能。使用起来也更简洁一些。

需求明确:

多条折线图切换滑动手动上下滑动改变数值

实现效果图

分析

现在最好用的图表库大概就是hellochart了。但是hellochart并没有提供滑动改变数值的接口以及相关内容。因此我们在hellochart的基础上进行功能扩展,通过自定义控件来实现如图效果。

首先需要确定用户点击的是哪一条线,哪一个点。然后对对应的数据进行操作。重新进行赋值改变图表显示。

实现

尺寸计算

首先计算控件宽高,因为要滑动,不宜进行1:1的计算,滑动难度高。因此乘以0.75,以方便响应用户手势操作。

int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();hLength = width * 0.75f;// x轴长度vLength = height * 0.75f; // y轴长度

触摸 - 按下响应事件

lineChartData = vccharts.this.getLineChartData();for (int i = 0; i < lineChartData.getLines().size(); i++) {Line line = lineChartData.getLines().get(i);int pointRadius = ChartUtils.dp2px(density, line.getPointRadius());for (int j = 0; j < line.getValues().size(); j++) {PointValue pointValue = line.getValues().get(j);final float rawValueX = vccharts.this.getChartComputator().computeRawX(pointValue.getX());final float rawValueY = vccharts.this.getChartComputator().computeRawY(pointValue.getY());if (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) {LogUtils.e("xuanzhong xxx " + pointValue.getX() + " yyy " + pointValue.getY() + " ");vccharts.this.lineIndex = i;vccharts.this.pointIndex = j;isMoveChange = true;for (Line line1 : lineChartData.getLines()) {line1.setStrokeWidth(2);}line.setStrokeWidth(4);selectLineNumber = i;}}}

触摸 - 移动响应事件

移动的时候需要判断位移以及差值,重新计算比例,确定新数值,进而更新数据,刷新View显示。

float y_max = 10;float y_min = 0;//如果没有选中任何点if (pointIndex == -1 || lineIndex == -1) {return super.onTouchEvent(event);}lineChartData = vccharts.this.getLineChartData();float y = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getY();float x = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getX();float y_new = y_max - coordinateConversionY(touchY);

数值保护,防止超过数据边界

if (y_new < y_pre) {y_new = y_pre;}if (y_new > y_after) {y_new = y_after;}if (y_new < y_min) {y_new = y_min;}if (y_new > y_max) {y_new = y_max;}

重新设置数据,通知数据变化

lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).set(x, y_new);// 通知数据变化postInvalidate();

触摸 - 抬起事件

抬起的时候,取消选择的线条的状态,并且计算最终数据,进行显示

if (vccharts.this.pointIndex == -1 || vccharts.this.lineIndex == -1) {isMoveChange = false;return true;}//通知数据变化List<PointValue> values = lineChartData.getLines().get(lineIndex).getValues();float[] changeData = new float[values.size()];for (int i = 0; i < values.size(); i++) {changeData[i] = values.get(i).getY();}try {if (vcListener != null) {vcListener.DataChange(lineIndex, new ZuniData(changeData));}} catch (Exception e) {e.printStackTrace();}postInvalidate();isMoveChange = false;vccharts.this.pointIndex = -1;vccharts.this.lineIndex = -1;return true;

判断是否点击对应点

private boolean isInArea(float x, float y, float touchX, float touchY, float radius) {float diffX = touchX - x;float diffY = touchY - y;return Math.pow(diffX, 2) + Math.pow(diffY, 2) <= 2 * Math.pow(radius, 2);}

完整代码:

public class vccharts extends LineChartView {private static final int DEFAULT_TOUCH_TOLERANCE_MARGIN_DP = 4;private final ChartTouchHandler0 touchHandler0;boolean isMoveChange = false; // 是否需要根据移动改变boolean isBetterPreAfter = true; // 是否需要比较前后int pointIndex;int lineIndex;private LineChartData lineChartData;private float vLength;// 竖线长度private float hLength;// 横线长度private float density;private int touchToleranceMargin;private int moveNumber = 0;private int selectLineNumber = -1; // 现在选中的线条private vcDataChangeListener vcListener;public vccharts(Context context, AttributeSet attrs) {super(context, attrs);try {density = context.getResources().getDisplayMetrics().density;touchToleranceMargin = ChartUtils.dp2px(density, DEFAULT_TOUCH_TOLERANCE_MARGIN_DP);LogUtils.e(" touchToleranceMargin " + touchToleranceMargin);} catch (Exception e) {e.printStackTrace();}touchHandler0 = new ChartTouchHandler0(context, this);vccharts.this.post(new Runnable() {@Overridepublic void run() {//在此处调用view.getMeasuredWidth()和getMeasuredHeight()可得到正确值int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();hLength = width * 0.75f;// x轴长度vLength = height * 0.75f; // y轴长度LogUtils.e(" vLength " + vLength + " h " + hLength);}});}public void setVcListener(vcDataChangeListener vcListener) {this.vcListener = vcListener;}@Overridepublic boolean onTouchEvent(MotionEvent event) {float touchY = event.getY();float touchX = event.getX();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:LogUtils.e(" 按下了 ");moveNumber = 0;lineChartData = vccharts.this.getLineChartData();for (int i = 0; i < lineChartData.getLines().size(); i++) {Line line = lineChartData.getLines().get(i);int pointRadius = ChartUtils.dp2px(density, line.getPointRadius());for (int j = 0; j < line.getValues().size(); j++) {PointValue pointValue = line.getValues().get(j);final float rawValueX = vccharts.this.getChartComputator().computeRawX(pointValue.getX());final float rawValueY = vccharts.this.getChartComputator().computeRawY(pointValue.getY());if (isInArea(rawValueX, rawValueY, touchX, touchY, pointRadius + touchToleranceMargin)) {LogUtils.e("xuanzhong xxx " + pointValue.getX() + " yyy " + pointValue.getY() + " ");vccharts.this.lineIndex = i;vccharts.this.pointIndex = j;isMoveChange = true;for (Line line1 : lineChartData.getLines()) {line1.setStrokeWidth(2);}line.setStrokeWidth(4);selectLineNumber = i;}}}return true;case MotionEvent.ACTION_MOVE:if (isMoveChange) {float y_max = 10;float y_min = 0;//如果没有选中任何点if (pointIndex == -1 || lineIndex == -1) {return super.onTouchEvent(event);}lineChartData = vccharts.this.getLineChartData();float y = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getY();float x = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).getX();float y_new = y_max - coordinateConversionY(touchY);if (isBetterPreAfter) {float y_pre = y_min;float y_after = y_max;if (pointIndex < lineChartData.getLines().get(lineIndex).getValues().size() - 1) {y_after = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex + 1).getY();}if (pointIndex > 0) {y_pre = lineChartData.getLines().get(lineIndex).getValues().get(pointIndex - 1).getY();}if (y_new < y_pre) {y_new = y_pre;}if (y_new > y_after) {y_new = y_after;}}if (y_new < y_min) {y_new = y_min;}if (y_new > y_max) {y_new = y_max;}lineChartData.getLines().get(lineIndex).getValues().get(pointIndex).set(x, y_new);//通知数据变化/*List<PointValue> values = lineChartData.getLines().get(lineIndex).getValues();float[] changeData = new float[values.size()];for (int i = 0; i < values.size(); i++) {changeData[i] = values.get(i).getY();}*/postInvalidate();/*if (vcListener != null) {moveNumber++;if (moveNumber % 3 == 0) {vcListener.DataChange(lineIndex, new ZuniData(changeData));}}*/}return super.onTouchEvent(event);case MotionEvent.ACTION_UP:LogUtils.e(" move " + moveNumber);if (isMoveChange) {if (vccharts.this.pointIndex == -1 || vccharts.this.lineIndex == -1) {isMoveChange = false;return true;}//通知数据变化List<PointValue> values = lineChartData.getLines().get(lineIndex).getValues();float[] changeData = new float[values.size()];for (int i = 0; i < values.size(); i++) {changeData[i] = values.get(i).getY();}try {if (vcListener != null) {vcListener.DataChange(lineIndex, new ZuniData(changeData));}} catch (Exception e) {e.printStackTrace();}postInvalidate();isMoveChange = false;vccharts.this.pointIndex = -1;vccharts.this.lineIndex = -1;return true;}return true;case MotionEvent.ACTION_CANCEL:if (isMoveChange) {isMoveChange = false;vccharts.this.pointIndex = -1;vccharts.this.lineIndex = -1;return true;}return true;}return super.onTouchEvent(event);}// 把坐标从 y 轴的位置转换成实际的值private float coordinateConversionY(float y) {LogUtils.i("坐标从 y 轴的位置转换成实际的值" + (10 - 0) / vLength * y);return (10 - 0) / vLength * y;}// 把坐标从 x 轴的位置转换成实际的值private float coordinateConversionX(float x) {LogUtils.i("坐标从 x 轴的位置转换成实际的值" + (11 - 0) / hLength * x);return (11 - 0) / hLength * x;}private boolean isInArea(float x, float y, float touchX, float touchY, float radius) {float diffX = touchX - x;float diffY = touchY - y;return Math.pow(diffX, 2) + Math.pow(diffY, 2) <= 2 * Math.pow(radius, 2);}public int getSelectLineNumber() {return selectLineNumber;}public void setSelectLineNumber(int selectLineNumber) {this.selectLineNumber = selectLineNumber;}public boolean isBetterPreAfter() {return isBetterPreAfter;}public void setBetterPreAfter(boolean betterPreAfter) {isBetterPreAfter = betterPreAfter;}public interface vcDataChangeListener {void DataChange(int line_index, ZuniData ZuniData);}}

如果觉得《安卓可上下滑动改变数值的折线图(基于hellochart)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。