探讨向
使用方式
- 导入库:
build.gradle中加入compile 'com.github.lecho:hellocharts-library:1.5.8@aar'
- 简单折线图的使用方式
布局:
在布局文件中加入
<lecho.lib.hellocharts.view.LineChartView
android:id="@+id/hello_chart_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
创建一个图表:
实例化对象:
lineChartView = (LineChartView) findViewById(R.id.line_chart_view);
那么LineChartData中的数据该如何添加呢?创建一个LineChartData对象data,如图:
可以发现,有两种方式构建data:
第一种是直接在data的构造方法中添加一个List<Line>对象;
第二种是通过data.setLines(List<Line>)方法设置Lines。
Lines就是图表中的折线对象了!先创建一个Line对象line,再创建一个List<Line> 对象lines,并将line添加到这个List中。然后调用data.setLines(lines)设置折线。
构建Line:
Line的构造方法中可以传入一个List<PointValue>的参数,这个PointValue即是一个点的值了!也就是说,创建一个PointValue对象,就是创建了一个坐标点。而将包括了多个点的List传入Line中,就构建了一条折线!这不就是和手绘折线图的过程一样吗?好,那么创建几个坐标点:
可以看到,PointValue构造方法中有一个PoindValue(float x, float y),参数即是这个点的横纵坐标。创建3个点,并添加到pointValueList中:
PointValue p1 = new PointValue(1, 4); PointValue p2 = new PointValue(2, 3); PointValue p3 = new PointValue(3, 5); List<PointValue> pointValueList = new ArrayList<>(); pointValueList.add(p1); pointValueList.add(p2); pointValueList.add(p3);
好了,现在3个点已经构建完毕。整理一下代码:
void initChart() { // 创建3个坐标点 PointValue p1 = new PointValue(1, 4); PointValue p2 = new PointValue(2, 3); PointValue p3 = new PointValue(3, 5); List<PointValue> pointValueList = new ArrayList<>(); pointValueList.add(p1);
pointValueList.add(p2);
pointValueList.add(p3);
// 通过坐标点创建出一根折线
Line line = new Line(pointValueList);
List<Line> lines = new ArrayList<>();
lines.add(line);
// 创建LineChartData对象,并通过setLines方法将只包含了一根折线的列表传入
LineChartData data = new LineChartData();
data.setLines(lines);
// 实例化LineChartView,并传入数据data
LineChartView lineChartView = (LineChartView) findViewById(R.id.line_chart_view);
lineChartView.setLineChartData(data);
}
最后在onCreate方法中调用initChart()方法进行初始化,看看效果:
发现,只有折线,少了坐标轴啊!那么继续,建立坐标轴:
在HelloCharts中,坐标轴是用Axis对象表示的。
在lineChartView.setLineChartData(data)语句之前,先创建两个Axis对象,axisX和axisY,分别表示X轴和Y轴。然后将其加入到data中。在data中,横纵坐标均有两种添加方式可以选择,分别代表横坐标位于上或下,以及纵坐标位于左或右。这里选择纵坐标在左边,横坐标在下方。
// 创建坐标轴 Axis axisX = new Axis(); Axis axisY = new Axis(); // 将坐标轴传入到data中 data.setAxisXBottom(axisX); data.setAxisYLeft(axisY);
再看看效果:
可以看到,一个折线图已经基本完成了!最后再设置一下坐标的属性:
axisX.setTextColor(Color.BLACK); axisX.setName("横坐标"); axisY.setTextColor(Color.BLACK); axisY.setName("纵坐标");
最终效果:
void initChart() { // 创建3个坐标点 PointValue p1 = new PointValue(1, 4); PointValue p2 = new PointValue(2, 3); PointValue p3 = new PointValue(3, 5); List<PointValue> pointValueList = new ArrayList<>(); pointValueList.add(p1); pointValueList.add(p2); pointValueList.add(p3);
// 通过坐标点创建出一根折线
Line line = new Line(pointValueList);
line.setColor(Color.BLACK);
List<Line> lines = new ArrayList<>();
lines.add(line);
// 创建LineChartData对象,并通过setLines方法将只包含了一根折线的列表传入
LineChartData data = new LineChartData();
data.setLines(lines);
// 创建坐标轴
Axis axisX = new Axis();
Axis axisY = new Axis();
axisX.setTextColor(Color.BLACK);
axisX.setName("横坐标");
axisY.setTextColor(Color.BLACK);
axisY.setName("纵坐标");
// 将坐标轴传入到data中
data.setAxisXBottom(axisX);
data.setAxisYLeft(axisY);
// 实例化LineChartView,并传入数据data
LineChartView lineChartView = (LineChartView) findViewById(R.id.line_chart_view);
lineChartView.setLineChartData(data);
}
- 进阶设置:坐标轴和Viewport
在使用的过程中我发现,如果数据过多,折线会全部在屏幕上挤成一团,非常不美观,需要放大之后才能看清楚。
比如我要做一个x轴为日期,y轴为访问量的图表,一共150个数据,代码和结果如下所示:
void buildChartTotal() {
List<String> dateList = new ArrayList<>();
List<Integer> visitorNumList = new ArrayList<>();
for (String[] strs : dailyDataSplit) {
String sDate = strs[1] + "/" + strs[2];
int sVisitorNum = Integer.parseInt(strs[3]);
if (!dateList.contains(sDate)) {
dateList.add(sDate);
visitorNumList.add(sVisitorNum);
}
}
List<AxisValue> axisXValueList = new ArrayList<>();
List<PointValue> pointValueList = new ArrayList<>();
for (int i = 0; i < dateList.size(); i++) {
axisXValueList.add(new AxisValue(i).setLabel(dateList.get(i)));
pointValueList.add(new PointValue(i, visitorNumList.get(i)));
}
Axis axisX = new Axis(axisXValueList);
axisX.setTextColor(R.color.dark_gray);
axisX.setName("日期");
axisX.setHasLines(true);
Axis axisY = new Axis();
axisY.setValues(axisYValueList);
axisY.setTextColor(R.color.dark_gray);
axisY.setName("访问总人数");
Line line = new Line(pointValueList);
line.setColor(R.color.dark_gray);
line.setHasLabels(true);
List<Line> lines = new ArrayList<>();
lines.add(line);
LineChartData lineChartData = new LineChartData(lines);
lineChartData.setAxisXBottom(axisX);
lineChartData.setAxisYLeft(axisY);
lineChartView.setZoomType(ZoomType.HORIZONTAL);
lineChartView.setInteractive(true);
lineChartView.setLineChartData(lineChartData);
}
显然,这样的显示效果并不好。考虑到以下问题:
1. 同时显示的数据太多以至于产生重叠;
2. 坐标轴上面也不需要这么多的标签;
3. 折线的最底部抵在了x轴上面,顶部顶在图表的最上方,我希望上下有一些空白的空间。
所以,这时候需要设置图表中显示的数据范围,减少坐标轴上Label的个数。
具体的做法是使用Viewport:
先看看Viewport的说明:
一个Viewport对象包含了4个公开的float类型成员:top,bottom,left,right;通过这4个变量可以设定出该Viewport的上下左右边界。
在lineChartView中有以下4个方法:
getCurrentViewport():获取当前图表的Viewport。
setCurrentViewport(Viewport):设置当前图表的Viewport。即是设置图表的可见部分的边界。
getMaximumViewport():设置图表的最大Viewport。
setMaximumViewport(Viewport):设置图表的最大Viewport。即是设置图表在缩放之后可达到的最外层边界,使用setCurrentViewport也无法超过该范围。
通过使用Viewport来设定显示范围。
1. 在lineChartView.setLineChartData(lineChartData)这条语句之后,通过:
Viewport maxViewport = lineChartView.getMaximumViewport(); 来得到maxViewport。
2. 新建一个Viewport对象:
Viewport viewport = new Viewport();
3. 对viewport的上下左右4个成员进行赋值。
注意这个viewport的边界是 不能超过maxViewport的,而因为希望上下有空白空间,所以竖直方向需要增加高度,所以要先改变maxViewport的top和bottom,让最外层的高度增加:
maxViewport.bottom = maxViewport.bottom - 5;
maxViewport.top = maxViewport.top + 5;
然后对viewport的上下边界赋值:
viewport.top = maxViewport.top;
viewport.bottom = maxViewport.bottom;
接下来,假设当前图表要显示最新的8个数据,那么:
viewport.right = maxViewport.right;
viewport.left = maxViewport.right - 7;这样要想要的viewport就设定好了。最后再调用:
lineChartView.setCurrentViewport(viewport);
就设置好了当前图表显示内容了。
设置Y轴Label的个数
因为Axis的实例中有一个setValues(List<AxisValue> values)方法可以设置坐标轴上标记的值,而只要屏幕显示的下,这个values的size就是坐标轴标签的个数。于是通过这个方法就可以减少y轴标签的密度了,非常简单。
/** * 最终代码:创建图表
*/
void buildChartTotal() {
// 初始化数据列表
List<String> dateList = new ArrayList<>();
List<Integer> visitorNumList = new ArrayList<>();
for (String[] strs : dailyDataSplit) {
String sDate = strs[1] + "/" + strs[2];
int sVisitorNum = Integer.parseInt(strs[3]);
if (!dateList.contains(sDate)) {
dateList.add(sDate);
visitorNumList.add(sVisitorNum);
}
}
List<AxisValue> axisXValueList = new ArrayList<>();
List<AxisValue> axisYValueList = new ArrayList<>();
List<PointValue> pointValueList = new ArrayList<>();
for (int i = 0; i < dateList.size(); i++) {
axisXValueList.add(new AxisValue(i).setLabel(dateList.get(i)));
pointValueList.add(new PointValue(i, visitorNumList.get(i)));
}
// 设置y轴的标签
axisYValueList = createYValue(visitorNumList);
// 设置坐标轴属性
Axis axisX = new Axis(axisXValueList);
axisX.setTextColor(R.color.dark_gray);
axisX.setName("日期");
axisX.setHasLines(true);
Axis axisY = new Axis();
axisY.setValues(axisYValueList);
axisY.setTextColor(R.color.dark_gray);
axisY.setName("访问总人数");
// 创建折线
final Line line = new Line(pointValueList);
line.setColor(R.color.dark_gray);
line.setHasLabels(true);
List<Line> lines = new ArrayList<>();
lines.add(line);
// 创建图标数据
final LineChartData lineChartData = new LineChartData(lines);
lineChartData.setAxisXBottom(axisX);
lineChartData.setAxisYLeft(axisY);
lineChartView.setZoomType(ZoomType.HORIZONTAL);
lineChartView.setInteractive(true);
lineChartView.setLineChartData(lineChartData);
// 设置viewport
Viewport maxViewport = lineChartView.getMaximumViewport();
maxViewport.bottom = maxViewport.bottom - 5;
maxViewport.top = maxViewport.top + 5;
maxViewport.right = maxViewport.right + 0.2f;// 解决最右边的数据可能显示不完全的问题
Viewport viewport = new Viewport(maxViewport);
viewport.left = maxViewport.right - 7.8f;
lineChartView.setCurrentViewport(viewport);
// 点击显示/隐藏折线上显示的数据
btnSetVisibility.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (line.hasLabels()) {
line.setHasPoints(false);
line.setHasLabels(false);
} else {
line.setHasLabels(true);
line.setHasPoints(true);
}
// 刷新显示
lineChartView.setCurrentViewport(lineChartView.getCurrentViewport());
}
});
}
List<AxisValue> createYValue(List<Integer> visitNums) {
int max = visitNums.get(0), min = max;
for (int i : visitNums) {
if (i > max) {
max = i;
} else if (min > i) {
min = i;
}
}
max += 5;
min -= 5;
List<AxisValue> axisValueList = new ArrayList<>();
for (int i = min; i < max; i += (max - min) / 20 + 1) {
axisValueList.add(new AxisValue(i).setLabel(String.valueOf(i)));
}
return axisValueList;
}
显示效果: