【www.bbyears.com--js教程】
在前面一篇博文中我们已经制作了折线图,接下来我们来制作柱形图和饼图。
柱形图的绘制流程与折线图比较类似,下面将只贴出代码,不作详细说明。
饼图相对较容易,只需计算每个数据占总比的大小旋转绘制扇形图即可,参见下述代码。
开始
柱形图
由于练手时是重写的,相对上篇博文功能可能较少,请结合上篇博文参考:
package hk.jerry.barchart;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@SuppressLint("InlinedApi")
public class BarChartView extends View {
private Paint paintAxis;
private Paint paintBar;
private Paint paintText;
private Paint paintClear;
private int startX;
private int stopX;
private int startY;
private int stopY;
private int chartWidth;
private int chartHeight;
private List dataSets;
private float max;
private float min;
private int levelsX;
private int levelsY;
private float spaceX;
private float spaceY;
private int offsetLeft = 100;
private float downX;
private String title;
private boolean drew = false;
private int length = 0;
private int maxDataSetsLength = -1;
private boolean scrollabled = true;
public BarChartView(Context context) {
super(context);
}
public BarChartView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setBackgroundColor(getResources().getColor(android.R.color.white));
if (dataSets == null || dataSets.isEmpty()) {
if (drew) {
canvas.drawRect(0, 0, getWidth(), getHeight(), paintClear);
} else {
return;
}
}
drew = true;
init();
//绘制X轴
canvas.drawLine(startX, stopY, stopX, stopY, paintAxis);
//绘制刻度与数据坐标,只画在视域内的11个+左右缓冲2个柱形
int j = (int) Math.ceil(0 - offsetLeft / spaceX) - 1;
for (int i = 0; i <= (levelsX > 11 ? 11 : levelsX) + 1; i++, j++) {
if (j < 0 || j >= dataSets.size()) { //防止数组下标越界
continue;
}
DataSet dataSet = dataSets.get(j);
float x = getPositionX(j);
paintAxis.setTextAlign(Align.CENTER);
canvas.drawLine(x, stopY, x, stopY + 10, paintAxis);
canvas.drawText(dataSet.name, x, stopY + 25, paintAxis);
float y = getPositionY(dataSet.value);
canvas.drawRect(x - 20, y, x + 20, stopY, paintBar);
canvas.drawText(String.valueOf(dataSet.value), x, y - 10, paintBar);
}
canvas.drawRect(0, 0, startX, getHeight(), paintClear);
canvas.drawLine(startX, startY, startX, stopY, paintAxis);
for (int i = 0; i < levelsY; i++) {
float y = getPositionY(min + i * 5);
paintAxis.setTextAlign(Align.RIGHT);
canvas.drawLine(startX - 10, y, startX, y, paintAxis);
canvas.drawText(String.valueOf((int) min + i * 5), startX - 12, y + 5, paintAxis);
}
if (title != null) {
Log.d("test", title);
canvas.drawText(title, getWidth() / 2, 0 + 50, paintText);
}
}
private void init() {
paintAxis = new Paint();
paintAxis.setStrokeWidth(3);
paintAxis.setAntiAlias(true);
paintAxis.setTextSize(16);
paintAxis.setColor(getResources().getColor(android.R.color.secondary_text_dark));
paintBar = new Paint();
paintBar.setStrokeWidth(3);
paintBar.setAntiAlias(true);
paintBar.setTextSize(20);
paintBar.setColor(getResources().getColor(android.R.color.holo_green_dark));
paintBar.setTextAlign(Align.CENTER);
paintText = new Paint();
paintText.setAntiAlias(true);
paintText.setTextSize(35);
paintText.setTextAlign(Align.CENTER);
paintText.setColor(getResources().getColor(android.R.color.holo_green_dark));
paintClear = new Paint();
paintClear.setColor(getResources().getColor(android.R.color.white));
startX = 50;
stopX = getWidth();
startY = 80;
stopY = getHeight() - 50;
chartHeight = stopY - startY;
chartWidth = stopX - startX;
max = min = dataSets.get(0).value;
for (int i = 0; i < dataSets.size(); i++) { DataSet dataSet = dataSets.get(i); if (dataSet.value > max) {
max = dataSet.value;
}
if (dataSet.value < min) { min = dataSet.value; } } levelsX = dataSets.size() + 1; levelsY = (int) Math.ceil((max - min) / 5) + 1; spaceY = chartHeight / levelsY; spaceX = levelsX > 10 ? chartWidth / 11 : chartWidth / (levelsX + 1);
length = (int) (dataSets.size() * spaceX);
}
private float getPositionX(int X) {
return spaceX * X + offsetLeft;
}
private float getPositionY(float value) {
int exceedNum = (int) Math.floor((value - min) / 5) + 1;
return ((float) stopY) - spaceY * exceedNum - ((value - min) % 5 / 5) * spaceY;
}
public void empty() {
dataSets = null;
title = null;
length = 0;
invalidate();
}
public void setDataSets(List dataSets) {
length = 0;
this.dataSets = dataSets;
invalidate();
}
/*
* 添加数据集
*/
public void addDataSet(DataSet dataSet) {
if (maxDataSetsLength != -1 && dataSets.size() > maxDataSetsLength) {
dataSets.remove(0);
}
dataSets.add(dataSet);
scrollToEnd();
}
public void setMaxDataSetsLength(int maxDataSetsLength) {
this.maxDataSetsLength = maxDataSetsLength;
}
public void setScrollable(boolean scrollabled) {
this.scrollabled = scrollabled;
}
public void setTitle(String title) {
this.title = title;
invalidate();
}
public void scrollToStart() {
if (!drew) {
invalidate();
}
offsetLeft = 100;
invalidate();
}
public void scrollToEnd() {
if (!drew) {
invalidate();
}
offsetLeft = chartWidth >= length ? 100 : (0 - (length - chartWidth) - 100);
invalidate();
}
public boolean isScrolledToStart() {
return offsetLeft >= 100;
}
public boolean isScrolledToEnd() {
return chartWidth >= length ? (offsetLeft <= 100) : (offsetLeft < 0 - (length - chartWidth));
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getRawX();
break;
case MotionEvent.ACTION_MOVE:
if (!scrollabled) {
return true;
}
offsetLeft += event.getRawX() - downX;
downX = event.getRawX();
if (isScrolledToStart()) {
scrollToStart();
} else if (isScrolledToEnd()) {
scrollToEnd();
} else {
invalidate();
}
break;
}
return true;
}
public static class DataSet {
public String name;
public float value;
public DataSet(String name, float value) {
this.name = name;
this.value = value;
}
}
}
测试代码是一样的,所以我在此就不贴出来了。
饼图
控件代码:
package hk.jerry.piechart;
import java.util.List;
import java.util.Random;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
@SuppressLint({ "NewApi", "DrawAllocation" })
public class PieChartView extends View {
private Paint paintStroke = new Paint(Paint.ANTI_ALIAS_FLAG); //饼图轮廓画刷
private Paint paintPie = new Paint(Paint.ANTI_ALIAS_FLAG); //饼图扇形画刷
private Paint paintText = new Paint(Paint.ANTI_ALIAS_FLAG); //饼图文字画刷
private float startX, stopX, startY, stopY; //饼图开始、结束位置
private float cx, cy, radius; //圆心X、Y点,半径
private float sum; //数据总和
private List dataSets; //数据集
private Random random = new Random(); //随机类
private String title; //标题
public PieChartView(Context context) {
super(context);
}
public PieChartView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void init() {
//设置饼图轮廓画刷参数
BlurMaskFilter filter = new BlurMaskFilter(1, BlurMaskFilter.Blur.INNER);
paintStroke.setARGB(255, 155, 155, 155);
paintStroke.setStrokeWidth(4);
paintStroke.setStyle(Paint.Style.STROKE);
paintStroke.setMaskFilter(filter);
//设置饼图扇形画刷参数
paintPie.setStyle(Paint.Style.FILL);
paintPie.setMaskFilter(filter);
paintPie.setTextSize(25);
//设置饼图文字画刷参数
paintText.setTextSize(35);
//计算饼图开始与结束位置,为确保饼图为正圆,左右与上下宽度要一致
startX = 10;
startY = 100;
stopX = stopY = (getHeight() < getWidth() ? getHeight() : getWidth()) - 50;
//根据开始与结束位置计算圆心坐标
cx = (stopX - startX) / 2 + startX;
cy = (stopY - startY) / 2 + startY;
//计算半径
radius = (stopX - startX) / 2;
//遍历数据集计算总和
sum = 0;
for (int i = 0; i < dataSets.size(); i++) {
sum += dataSets.get(i).value;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
init();
//生成Path,并绘制饼图的轮廓
Path path = new Path();
path.addCircle(cx, cy, radius, Direction.CW);
canvas.drawPath(path, paintStroke);
//生成RectF,用于后续的扇形绘制
RectF rectF = new RectF(cx - radius, cy - radius, cx + radius, cy + radius);
//扇形偏移角度
float startAngle = 0;
//饼图数据标志偏移值
float offset = 30;
for (int i = 0; i < dataSets.size(); i++) {
DataSet dataSet = dataSets.get(i);
float percentage = dataSet.value / sum * 360; //计算百分比得出角度
paintPie.setARGB(255, random.nextInt(255), random.nextInt(255), random.nextInt(255)); //随机生成颜色
canvas.drawArc(rectF, startAngle, percentage, true, paintPie); //绘制扇形
startAngle += percentage; //将刚才计算到的角度与偏移角度相加,作为下一个扇形的偏移角度,便于绘制
canvas.drawRect(stopX + 60, startY + offset, stopX + 100, startY + offset + 30, paintPie); //绘制数据标志图
canvas.drawText(dataSet.name + "(" + (dataSet.value / sum * 100) + "%)", stopX + 130, startY + offset + 23, paintPie); //绘制数据值
offset += 50;
}
if (title != null) { //绘制标题
canvas.drawText(title, getWidth() / 2, 50, paintText);
}
}
public void setTitle(String title) {
this.title = title;
}
public void setDataSets(List dataSets) {
this.dataSets = dataSets;
invalidate();
}
public void addDataSet(DataSet dataSet) {
dataSets.add(dataSet);
invalidate();
}
public static class DataSet {
public String name;
public float value;
public DataSet(String name, float value) {
this.name = name;
this.value = value;
}
}
}
布局代码我也不上了,跟上篇博文类似,直接上测试代码:
final PieChartView pc = (PieChartView) findViewById(R.id.pc_test);
pc.setTitle("测试图表");
Random random = new Random();
List dataSets = new ArrayList();
for (int i = 0; i <= 5; i++) {
float value = random.nextInt(50);
dataSets.add(new DataSet("第" + (i + 1) + "个数据", value));
}
pc.setDataSets(dataSets);
btnAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
pc.addDataSet(new DataSet(etName.getText().toString(), Float.valueOf(etValue.getText().toString())));
}
});
运行效果