自定义折线统计图

前几天有个需求,就是传入一串坐标,然后绘制统计图,经过百度发现,网上的统计图太高大上了,所以自己写了一个简单的统计图。第一次写,不知道是不是合乎规范,以后慢慢调整。

先是效果图:


直接贴代码吧:

package com.wscq.testlinechart;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class LineChartView extends View {
	/** 原点的x,y */
	private int originX, originY;
	/** 箭头终点的x,y */
	private int upperLimitX, upperLimitY;
	/** 最后一个分割线和箭头之间的距离 */
	private int unoccupiedX, unoccupiedY;
	/** 分成几个区域 */
	private int splitXNum, splitYNum;
	/** 每一格的单位 */
	private int splitXLength, splitYLength;
	/** 分割线长度 */
	private int splitLineLength;
	/** 坐标字的大小 */
	private int textSize;
	/** 坐标点 */
	private List<Point> points;
	private Canvas canvas;

	public LineChartView(Context context) {
		super(context);
	}

	public LineChartView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public LineChartView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		initCoord(this.getMeasuredWidth(), this.getMeasuredHeight());
	}

	/**
	 * 传入宽高,用来计算坐标的大体位置
	 * 
	 * @param width
	 * @param height
	 */
	private void initCoord(int width, int height) {
		originX = (int) (0.09375 * width);// 30;
		originY = (int) (0.86 * height);// 350
		upperLimitX = (int) (0.90625 * width);// 290
		upperLimitY = (int) (0.07444 * height);// 30
		unoccupiedX = 15;
		unoccupiedY = 10;
		splitXNum = 10;
		splitYNum = 10;
		splitLineLength = 5;
		splitXLength = splitYLength = 10;
		textSize = 25;
		points = new ArrayList<>();
	}

	public void setPoints(List<Point> points) {
		this.points = points;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		Paint paint = new Paint();
		paint.setColor(Color.BLACK);
		// 设置字体大小
		paint.setTextSize(textSize);
		// 设置画出的线的 粗细程度
		paint.setStrokeWidth(3);
		// 画坐标轴
		drawXY(canvas, paint);
		this.canvas = canvas;
		drawLineChart();
		super.onDraw(canvas);
	}

	public void drawLineChart() {
		Paint paint = new Paint();
		// 设置画出的线的 粗细程度
		paint.setStrokeWidth(3);
		// 画弧线
		paint.setAntiAlias(true);// 设置画笔无锯齿
		paint.setStyle(Paint.Style.STROKE);// 设置空心
		for (int i = 0; i < points.size() - 1; i++) {
			int x1 = computeX(points.get(i).x);
			int y1 = computeY(points.get(i).y);
			int x2 = computeX(points.get(i + 1).x);
			int y2 = computeY(points.get(i + 1).y);
			// 弧线所在矩形,圆弧起始角度,圆弧角度,是否显示半径连线,绘制时候所用的画笔
			canvas.drawLine(x1, y1, x2, y2, paint);
		}
	}

	/**
	 * 计算x坐标的位置
	 * 
	 * @param x
	 *            实际x坐标
	 * @return x在屏幕上的像素位置
	 */
	private int computeX(int x) {
		BigDecimal bigX = new BigDecimal(x);
		BigDecimal bigSplitXLength = new BigDecimal(splitXLength);
		MathContext mc = new MathContext(2, RoundingMode.HALF_DOWN);
		BigDecimal RangeX = new BigDecimal(upperLimitX - unoccupiedX - originX);
		BigDecimal EachX = RangeX.divide(new BigDecimal(splitXNum), mc);
		BigDecimal coordinateValueX = EachX.multiply(bigX
				.divide(bigSplitXLength));
		return coordinateValueX.intValue() + originX;
	}

	/** 计算y坐标的位置 */
	private int computeY(int y) {
		BigDecimal bigY = new BigDecimal(y);
		BigDecimal bigSplitYLength = new BigDecimal(splitYLength);
		// 设置计算精度
		MathContext mc = new MathContext(2, RoundingMode.HALF_DOWN);
		// 整个量程所代表的像素值
		BigDecimal RangeY = new BigDecimal(originY - upperLimitY - unoccupiedY);
		// 每个格子所代表的像素值
		BigDecimal EachY = RangeY.divide(new BigDecimal(splitYNum), mc);
		// 先求出y坐标代表几格,然后用单位长度乘之,求出y轴距离圆点的像素
		BigDecimal coordinateValueY = EachY.multiply(bigY.divide(
				bigSplitYLength, mc));
		// 实际在手机屏幕上的坐标
		return originY - coordinateValueY.intValue();
	}

	/** 画坐标系 */
	private void drawXY(Canvas canvas, Paint paint) {
		// 画纵坐标
		canvas.drawLine(originX, upperLimitY, originX, originY, paint);
		int intervalY = (originY - upperLimitY - unoccupiedY) / 10;
		for (int i = 0; i < splitYNum; i++) {
			// 画刻度标记
			canvas.drawLine(originX, originY - i * intervalY, originX
					+ splitLineLength, originY - i * intervalY, paint);
			// 写量程
			if (i != 0) {
				canvas.drawText(i * 10 + "", originX - textSize - textSize / 3,
						originY - i * intervalY + textSize / 2, paint);
			}
		}
		// 纵坐标尽头的箭头
		canvas.drawLine(originX, upperLimitY, originX - splitLineLength,
				upperLimitY + 2 * splitLineLength, paint);
		canvas.drawLine(originX, upperLimitY, originX + splitLineLength,
				upperLimitY + 2 * splitLineLength, paint);

		// 画横坐标
		canvas.drawLine(originX, originY, upperLimitX, originY, paint);
		int intervalX = (upperLimitX - originX - unoccupiedX) / 10;
		for (int i = 0; i < splitXNum; i++) {
			// 画刻度标记
			canvas.drawLine(originX + i * intervalX, originY, originX + i
					* intervalX, originY - splitLineLength, paint);
			// 写量程
			canvas.drawText(i * 10 + "",
					originX + i * intervalX - textSize / 2, originY
							+ (int) (textSize * 1.3), paint);
		}
		// 横坐标尽头的箭头
		canvas.drawLine(upperLimitX, originY,
				upperLimitX - 2 * splitLineLength, originY - splitLineLength,
				paint);
		canvas.drawLine(upperLimitX, originY,
				upperLimitX - 2 * splitLineLength, originY + splitLineLength,
				paint);
	}
}

上面是一个自定义view,调用时候使用如下方法既可:

package com.wscq.testlinechart;

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

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {
	LineChartView lcv;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		lcv = (LineChartView) findViewById(R.id.lcv);
		Button btn = (Button) findViewById(R.id.btn);
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Random random = new Random();
				List<Point> points = new ArrayList<Point>();
				Point p = null;
				for (int i = 0; i < 90; i++) {
					p = new Point();
					p.x = i;
					p.y = random.nextInt(91);
					points.add(p);
				}
				lcv.setPoints(points);
				lcv.invalidate();
			}
		});
	}
}
视图比较简单,就是一个按钮和一个图,然后这个统计图精度不是很高,整型还行,别的容易有误差。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <com.wscq.testlinechart.LineChartView
        android:id="@+id/lcv"
        android:layout_width="400dp"
        android:layout_height="400dp"
        android:text="@string/hello_world" />

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/lcv" />

</RelativeLayout>



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值