自定义折线Demo

时间:2023-01-31 03:27:22

测试机一般,效果图可能看不太清,不要紧。后面我会跟上所有源代码,放到自己的demo中就能跑起来

先来个效果图
自定义折线Demo

说明:随着手指的移动,会有一条竖线,竖线和折现交点处,会有小圆圈出现,而且,还有出现水平的横线。最后,会通过一个回调接口,把对应的在Y轴上的刻度值返回。

代码中有详细的注释说明。关于一点细节的东西,会在最后说明

获取交点处高度值的回调接口
GetHeightData

public interface GetHeightData {

/**
* 返回的是百分比
*/

void getHeightData(float f1, float f2);

}

LinePicBean

import java.io.Serializable;

public class LinePicBean implements Serializable{


public LinePicBean(float actualData) {
this.actualData=actualData;
}

//真实数据
private float actualData;
private float actualHeight;

public float getActualData() {
return actualData;
}
public void setActualData(float actualData) {
this.actualData = actualData;
}
public float getActualHeight() {
return actualHeight;
}
public void setActualHeight(float actualHeight) {
this.actualHeight = actualHeight;
}

}

自定义折现
Line


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Shader.TileMode;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

public class Line extends View {
/**
* 控件的高度
*/

private int vHeight;
/**
* 控件的宽度
*/

private int vWidth;
/**
* 第一条折现的路径画笔
*/

private Paint pathPaint;
/**
* 第一条折现的路径
*/

private Path path;
/**
* 第一条折现的闭合区域路径
*/

private Path regionPath;
/**
* 第一条折现的闭合区域的画笔
*/

private Paint regionPaint;
private Paint moveLinePaint;// 跟随手指移动的线的画笔
private Paint circlePaint;// 绘制小圆环画笔
private Paint circlePoint;// 绘制小圆点画笔
/**
* 用于保存传递过来的,第一条折现的数据的集合
*/

private List<LinePicBean> originalData;

/**
* X轴刻度坐标的集合(不包含Y方向坐标)
*/

private List<Float> XList = new ArrayList<Float>();
/**
* 预留高度,用来显示底部刻度
*/

private float surplusHeight;
/**
* 顶部预留高度,为纵轴文字高度的一半
*/

private float topSurplusHeight;
/**
* 代表竖直最大刻度
*/

private float total;
// --------------------------------------------------------
private Paint pathPaint2;// 路径画笔,就是折线
private Path path2;// line路径
private Path regionPath2;// 闭合区域路径
private Paint regionPaint2;// 区域画笔,画渐变区域
private Paint moveLinePaint2;// 跟随手指移动的线的画笔
private Paint circlePaint2;// 绘制小圆环画笔
private Paint circlePoint2;// 绘制小圆点画笔
private List<LinePicBean> originalData2;
private List<Float> XList2 = new ArrayList<Float>();
// ---------------------------------------------------------

/**
* Y轴上的刻度上的文字画笔
*/

private TextPaint verticalTextPaint;
/**
* 水平线画笔
*/

private Paint horiPaint;
/**
* X轴刻度上的文字画笔
*/

private TextPaint horizontalTextPaint;

public Line(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

public Line(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}

public Line(Context context) {
this(context, null);
}

public void setData(List<LinePicBean> list) {
XList.clear();
this.originalData = list;
int length = list.size();
for (int i = 0; i < length; i++) {
/*
vWidth - maxLength,是横坐标(X轴)的实际长度。
如果有5个刻度,需要被分成4份,所以要除以(length - 1)
因为X轴实际开始位置距离控件左边的距离是Y周数据的宽度,所以,最后要加上maxLength
*/

XList.add((vWidth - maxLength) * 1.0f / (length - 1) * i
+ maxLength);
}

LinePicBean linePicBean = null;
for (int i = 0; i < length; i++) {
linePicBean = originalData.get(i);

/*
* 原始数据是5.0、0、-3.0、6.5、4.0。通过下面这句的计算后,结果是10.0、2.0、0.0、11.5、9.0
*
* 这里固定加5,是因为Y周刻度的最低是 -5.0
*
* 这里需要额外注意,不要单纯的去加折线数据中的最小值的绝对值
*
* 以当前demo为例,Y轴刻度最小是 -5,第一条折线的最小数据是 -3
*
* 如果单纯的加折线最小值,就成了 -3+|-3|=0,后面,再除以12.5(7.5+|-5|),结果是0
* 意味着,这个-3,在Y轴的最低点。而Y轴刻度最低是-5,这显然不对
*
* 现在 -3+|-5|=2,2/12.5=0.16,也就是说,-3对应的高度,是12.5(整个Y轴高度)的0.16,这就对了
*
*/

linePicBean.setActualData(linePicBean.getActualData() + 5);

// 这个12.5是纵轴范围总和,到时候根据项目需要动态计算(7.5+5.0)
/*
* topSurplusHeight:顶部预留高度,为纵轴文字高度的一半
* total = vHeight - surplusHeight,控件的总高度减去底部刻度预留高度。表示Y轴的高度
* total - topSurplusHeight表示有刻度的区域。示例中,表示-5.0%--7.5%之间的高度
* 最高是7.5,最低是-5.0,所以,总值是7.5+5.0=12.5
* linePicBean.getActualData() / (12.5f),表示当前坐标点再Y轴的位置比例。如第一个点:10.0/12.5=0.8
*(total - topSurplusHeight) * linePicBean.getActualData() / (12.5f),将比例换算成高度值(像素)
*安卓的坐标系,想下是Y轴正方向,所以,需要total减去上面计算的值,这样,就换成了在屏幕的Y轴的坐标
*/

linePicBean.setActualHeight(total - (total - topSurplusHeight) * linePicBean.getActualData() / (12.5f));
}

//确定折现的起点
path.moveTo(XList.get(0), originalData.get(0).getActualHeight());
//确定折现下方的渐变区域的起点
regionPath.moveTo(XList.get(0), originalData.get(0).getActualHeight());

//确定每个坐标点
for (int i = 1; i < length; i++) {
path.lineTo(XList.get(i), originalData.get(i).getActualHeight());
regionPath.lineTo(XList.get(i), originalData.get(i).getActualHeight());
}

//定位到坐标系的右下角
regionPath.lineTo(XList.get(length - 1), total);

//定位到坐标系的左下角(X轴和Y轴交点处)
regionPath.lineTo(maxLength, total);

//最后需要close,组成一个闭合区域
regionPath.close();

//绘制
invalidate();
}

public void setData2(List<LinePicBean> list) {
//注释详见setData方法

XList2.clear();
this.originalData2 = list;
int length = list.size();
for (int i = 0; i < length; i++) {
XList2.add((vWidth - maxLength) * 1.0f / (length - 1) * i
+ maxLength);
}

LinePicBean linePicBean = null;
for (int i = 0; i < length; i++) {
linePicBean = originalData2.get(i);
linePicBean.setActualData(linePicBean.getActualData() + 5);
linePicBean.setActualHeight(total - (total - topSurplusHeight)
* linePicBean.getActualData() / 12.5f);
}

path2.moveTo(XList2.get(0), originalData2.get(0).getActualHeight());
regionPath2.moveTo(XList2.get(0), originalData2.get(0)
.getActualHeight());
for (int i = 1; i < length; i++) {
path2.lineTo(XList2.get(i), originalData2.get(i).getActualHeight());
regionPath2.lineTo(XList2.get(i), originalData2.get(i)
.getActualHeight());
}
regionPath2.lineTo(XList2.get(length - 1), total);
regionPath2.lineTo(maxLength, total);
regionPath2.close();
invalidate();
}

private GetHeightData listener;

public void setListener(GetHeightData listener) {
this.listener = listener;
}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
vHeight = h;
vWidth = w;

/*
* LinearGradient(线性渐变)是Shader下的一个子类
*详细的请看一位大神的博客
* http://blog.csdn.net/aigestudio/article/details/41799811
*/

LinearGradient gradient = new LinearGradient(0, vHeight, 0, 0,
Color.parseColor("#00ffffff"), Color.parseColor("#6629bbea"),
TileMode.REPEAT);
regionPaint.setShader(gradient);
LinearGradient gradient2 = new LinearGradient(0, vHeight, 0, 0,
Color.parseColor("#00ffffff"), Color.parseColor("#66e44078"),
TileMode.REPEAT);
regionPaint2.setShader(gradient2);
}

private void init() {

verticalLength = 20;
pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
pathPaint.setStyle(Style.STROKE);// 不设置Style为STROKE,DashPathEffect不起作用
regionPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
regionPaint.setStyle(Style.FILL);
circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setStyle(Style.STROKE);
circlePaint.setStrokeWidth(5);
circlePoint = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePoint.setStrokeWidth(10);
horiPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
horiPaint.setStyle(Style.STROKE);

moveLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
moveLinePaint.setColor(Color.RED);
verticalTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
verticalTextPaint.setTextSize(20);
verticalTextPaint.setColor(Color.parseColor("#0000ff"));

path = new Path();
regionPath = new Path();
pathPaint.setColor(Color.parseColor("#29bbea"));
circlePaint.setColor(Color.parseColor("#29bbea"));
circlePoint.setColor(Color.parseColor("#29bbea"));

pathPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
pathPaint2.setStyle(Style.STROKE);
regionPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
regionPaint2.setStyle(Style.FILL);
circlePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint2.setStyle(Style.STROKE);
circlePaint2.setStrokeWidth(5);
circlePoint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
circlePoint2.setStrokeWidth(10);
moveLinePaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
moveLinePaint2.setColor(Color.BLACK);
horizontalTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
horizontalTextPaint.setTextSize(20);
horizontalTextPaint.setColor(Color.parseColor("#00ff00"));
path2 = new Path();
regionPath2 = new Path();
pathPaint2.setColor(Color.parseColor("#e44078"));
circlePaint2.setColor(Color.parseColor("#e44078"));
circlePoint2.setColor(Color.parseColor("#e44078"));
}

private float radius = 10f;

/**
* 手指所在位置的竖线和折线交点处的圆环的半径
*/

private float largeRadius = 15f;

/**
* Y轴上,刻度和刻度之间的距离
*/

private float verticalMeanValue = 0;
/**
* 文字基线和文字正中间的那条横线之间的差值
*/

private float offsetDistance;
/**
* X轴上,文字顶部的小竖线的高度
*/

private float verticalLength;

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

/*
* total - topSurplusHeight表示有刻度的区域
* verticalScaleList.size()表示Y轴上刻度的个数
*
* verticalMeanValue:Y轴上,刻度和刻度之间的距离。就是有刻度的区域,根据刻度个数得到的平均值
*/

verticalMeanValue = (total - topSurplusHeight) / (verticalScaleList.size() - 1);

//根据Y轴的刻度绘制水平的线
for (int i = 0; i < verticalScaleList.size(); i++) {

canvas.drawLine(maxLength, total - verticalMeanValue * i, vWidth, total - verticalMeanValue * i, horiPaint);

//绘制文字。就是在坐标系上写刻度
canvas.drawText(verticalScaleList.get(i), 0, total - verticalMeanValue * i + offsetDistance, verticalTextPaint);
}

//绘制X轴上刻度位置的小竖线和文字

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

canvas.drawLine(XList.get(i), total, XList.get(i), total + verticalLength, horiPaint);

if (i == 0) {
horizontalTextPaint.setTextAlign(Align.LEFT);
} else if (i == horizontalScaleList.size() - 1) {
horizontalTextPaint.setTextAlign(Align.RIGHT);
} else {
horizontalTextPaint.setTextAlign(Align.CENTER);
}

//绘制文字。就是在坐标系上写刻度
canvas.drawText(horizontalScaleList.get(i), XList.get(i), total + verticalLength + (horiTextHeight - horiOffsetDistance), horizontalTextPaint);
}

//绘制第一条折现
canvas.drawPath(path, pathPaint);
//绘制第一条折现下方的渐变区域
canvas.drawPath(regionPath, regionPaint);
//绘制第二条折现
canvas.drawPath(path2, pathPaint2);
//绘制第二条折现下方的渐变区域
canvas.drawPath(regionPath2, regionPaint2);

if (flag) {
canvas.drawLine(X, topSurplusHeight, X, total, moveLinePaint);
canvas.drawLine(maxLength, Y, vWidth, Y, moveLinePaint);
canvas.drawLine(maxLength, YY2, vWidth, YY2, moveLinePaint);
canvas.drawCircle(X, YY2, largeRadius, circlePaint2);
canvas.drawCircle(X, Y, largeRadius, circlePaint);

if (listener != null) {
listener.getHeightData(f1, f2);
}

} else {
if (listener != null) {
listener.getHeightData(-1, -1);
}
}
for (int i = 0; i < originalData.size(); i++)// 绘制小圆点
{
canvas.drawCircle(XList.get(i), originalData.get(i)
.getActualHeight(), radius, circlePoint);
}
for (int i = 0; i < originalData2.size(); i++)// 绘制小圆点
{
canvas.drawCircle(XList2.get(i), originalData2.get(i)
.getActualHeight(), radius, circlePoint2);
}

}

/**
* 手指按下未位置的X坐标
*/

private float X;
/**
* 标记是否出现竖直线
*/

private boolean flag;

@Override
public boolean onTouchEvent(MotionEvent event) {
X = event.getX();

getRegionY(X);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:

if (X < maxLength) {
flag = false;
} else {
flag = true;
}

break;
case MotionEvent.ACTION_MOVE:
if (X < maxLength) {
flag = false;
} else {
flag = true;
}
break;
case MotionEvent.ACTION_UP:
//如果想松开手,线还在,就注释掉这句话
//flag = false;
break;
default:
break;
}

invalidate();
return true;
}

private float Y;
private float YY2;

private float f1;
private float f2;

//获取圆环的Y坐标值
private void getRegionY(float x) {
for (int i = 0; i < XList.size() - 1; i++) {
if (x == XList.get(i) || x == XList.get(i + 1) || (x > XList.get(i) && x < XList.get(i + 1))) {
//X坐标在X轴的2个连续的刻度之间

float X1 = XList.get(i);
float Y1 = total - originalData.get(i).getActualHeight();
float X2 = XList.get(i + 1);
float Y2 = total - originalData.get(i + 1).getActualHeight();

/*
* 这里的要算出一个随手指移动的垂直的线和折现的焦点的Y坐标值(X坐标就是手指按下的位置),计算涉及方程
*
* 现在,在X轴上,相邻的2个刻度之间的区域(包括区域边界),肯定有2个点(X1,Y1)、(X2,Y2)
* 直线方程基础表达式:y=ax+b
* 斜率 a=(Y2 - Y1) / (X2 - X1)
* 所以,这个区间的直线方程就是:y=(Y2 - Y1) / (X2 - X1)x+b
* 随便代入一个点的坐标:如:(X1,Y1)
* 得到b=(Y1 * X2-Y2 * X1)/ (X2 - X1)
* 所以,折线在这个区间的线性方程就是:y=(Y2 - Y1) / (X2 - X1) * x + (Y1 * X2-Y2 * X1) / (X2 - X1)
*
* 另一条直线,就是手指所在位置的一条垂直的线,方程是:x=手指按下的位置,就是方法上传进来的值
*
* 代入方程就得到了y的值。
* 但是要注意,这个y值,是数学上坐标系的Y坐标的值,安卓是屏幕左上角为原点,向下为Y周正方向
* 所以,在这个控件中,这个点的Y方向的高度,实际是:total-y
*
*/

f1 = ((Y2 - Y1) / (X2 - X1) * x + (Y1 * X2 - Y2 * X1) / (X2 - X1)) / (total - topSurplusHeight);
Y = total - (Y2 - Y1) / (X2 - X1) * x + (Y2 * X1 - Y1 * X2) / (X2 - X1);
}
}

for (int i = 0; i < XList2.size() - 1; i++) {
if (x == XList2.get(i) || x == XList2.get(i + 1) || (x > XList2.get(i) && x < XList2.get(i + 1))) {
float X1 = XList2.get(i);
float Y1 = total - originalData2.get(i).getActualHeight();
float X2 = XList2.get(i + 1);
float Y2 = total - originalData2.get(i + 1).getActualHeight();
f2 = ((Y2 - Y1) / (X2 - X1) * x + (Y1 * X2 - Y2 * X1) / (X2 - X1)) / (total - topSurplusHeight);
YY2 = total - (Y2 - Y1) / (X2 - X1) * x + (Y2 * X1 - Y1 * X2) / (X2 - X1);
}
}
}

/**
* 存放Y轴刻度上的值
*/

private List<String> verticalScaleList = new ArrayList<String>();
/**
* 记录纵坐标上的最长的数字的长度
*/

private float maxLength = 0;

/**
* 设置Y轴相关
*
* @param verticalScaleList Y轴刻度的集合
*/

public void setVerticalAbout(List<String> verticalScaleList) {
this.verticalScaleList = verticalScaleList;
//通过下面的for循环,拿到最长的那个数字值
String maxString = "";
for (int i = 0; i < verticalScaleList.size(); i++) {
if (verticalScaleList.get(i).length() > maxString.length()) {
maxString = verticalScaleList.get(i);
}
}
maxLength = verticalTextPaint.measureText(maxString);

/*
* 下坡度-上坡度,就是文字的整个高度,因为预留高度是一半,所以除以2
* 从基线开始,向上到文字最高处,是上坡度,因为和安卓屏幕坐标系Y方向相反,所以是一个负数值。其绝对值表示上坡度大小
* 从基线开始,向下到文字最底部,是下坡度,因为和安卓屏幕坐标系Y方向相同,所以是一个正数值。其值表示下坡度大小
*
* 如果不理解文字的上下坡度等熟悉,详见一位大神博客
* http://blog.csdn.net/aigestudio/article/details/41447349
*/

topSurplusHeight = (verticalTextPaint.descent() - verticalTextPaint.ascent()) / 2;

total = vHeight - surplusHeight;

/*
* ascent(上坡度)<0,descent(下坡度)>0,|上坡度|>下坡度
* -verticalTextPaint.descent() - verticalTextPaint.ascent() = -下坡度+|上坡度|
* 除以2,就是文字基线和文字正中间的那条横线之间的差值
*/

offsetDistance = (-verticalTextPaint.descent() - verticalTextPaint.ascent()) / 2;
}

private List<String> horizontalScaleList = new ArrayList<String>();
private float horiTextHeight;
private float horiOffsetDistance;

/**
* 设置X轴相关
*
* @param horizontalScaleList X轴刻度集合
*/

public void setHorizontalAbout(List<String> horizontalScaleList) {
this.horizontalScaleList = horizontalScaleList;
horiTextHeight = (horizontalTextPaint.descent() - horizontalTextPaint.ascent());
horiOffsetDistance = (-horizontalTextPaint.descent() - horizontalTextPaint.ascent()) / 2;

surplusHeight = horiTextHeight * 2;
}

}

在布局中引用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">


<com.line.demo.Line
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
android:background="#55ff0000"/>


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="20dp"
android:orientation="horizontal">


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="蓝线"
android:textSize="20sp"/>


<TextView
android:id="@+id/line_data_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textSize="20sp"/>


</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="20dp"
android:orientation="horizontal">


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="红线"
android:textSize="20sp"/>


<TextView
android:id="@+id/line_data_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:textSize="20sp"/>


</LinearLayout>

</LinearLayout>

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.ViewTreeObserver;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity implements GetHeightData {
private Line line;
private List<LinePicBean> lists;
private List<LinePicBean> lists1;
/**
* Y轴的刻度
*/

private List<String> verticalScale;
/**
* 水平X轴的刻度
*/

private List<String> horizontalScale;

private TextView line_data_1;
private TextView line_data_2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

line = findViewById(R.id.line);
line_data_1 = findViewById(R.id.line_data_1);
line_data_2 = findViewById(R.id.line_data_2);

lists = new ArrayList<LinePicBean>();
lists.clear();
lists.add(new LinePicBean(5.0f));
lists.add(new LinePicBean(0f));
lists.add(new LinePicBean(-3.0f));
lists.add(new LinePicBean(3f));
lists.add(new LinePicBean(4.0f));
lists1 = new ArrayList<LinePicBean>();
lists1.clear();
lists1.add(new LinePicBean(2.5f));
lists1.add(new LinePicBean(0f));
lists1.add(new LinePicBean(2.5f));
lists1.add(new LinePicBean(6.0f));
lists1.add(new LinePicBean(7.5f));

//Y轴的标识
verticalScale = new ArrayList<String>();
verticalScale.add("-5.0%");
verticalScale.add("-2.5%");
verticalScale.add("0%");
verticalScale.add("2.5%");
verticalScale.add("5.0%");
verticalScale.add("7.5%");

//横轴的标识
horizontalScale = new ArrayList<String>();
horizontalScale.add("02/05");
horizontalScale.add("02/06");
horizontalScale.add("02/07");
horizontalScale.add("02/08");
horizontalScale.add("02/09");

line.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {// 想办法只让执行一次

@Override
public void onGlobalLayout() {

line.getViewTreeObserver().removeGlobalOnLayoutListener(this);

//设置X轴相关
line.setHorizontalAbout(horizontalScale);
//设置Y轴相关
line.setVerticalAbout(verticalScale);
line.setData(lists);
line.setData2(lists1);
line.setListener(MainActivity.this);
}
});

}


@Override
public void getHeightData(float f1, float f2) {



Log.e("f1", f1 + "");
Log.e("f2", f2 + "");

if (f1 == -1 || f2 == -1) {
line_data_1.setText("");
line_data_2.setText("");
} else {

// line_data_1.setText(f1 * 12.5 - 5.0f + "");
// line_data_2.setText(f2 * 12.5 - 5.0f + "");
line_data_1.setText(f1 * 12.5f - 5.0f + "");
line_data_2.setText(f2 * 12.5f - 5.0f + "");

}

}
}

说明:
大家应该注意到了,在最后展示返回值的时候,我写了2种,第一种是

    line_data_1.setText(f1 * 12.5 - 5.0f + "");
line_data_2.setText(f2 * 12.5 - 5.0f + "");

有个特殊点,当竖线移动到Y为0的时候,
自定义折线Demo

出现的是这样的很小的一个值。

但是,当我把展示方法换成第二种

 line_data_1.setText(f1 * 12.5f - 5.0f + "");
line_data_2.setText(f2 * 12.5f - 5.0f + "");

展示就成了
自定义折线Demo

所以,为展示数据准确,最好全部用同一种数据类型。