android自动画线,Android自定义View——扇形统计图

Android 扇形统计图

先看看效果:

看上去如果觉得还行就继续往下看吧!

9c30e52ae095

效果图1

9c30e52ae095

效果图2

自定义View

定义成员变量

private int mHeight, mWidth;//宽高

private Paint mPaint;//扇形的画笔

private Paint mTextPaint;//画文字的画笔

private int centerX, centerY;//中心坐标

//"其他"的value

//扇形图分成太多快 所以要合并一部分为其他 即图中灰色部分

private double rest;

private int maxNum = 5;//扇形图的最大块数 超过的item就合并到其他

String others = "其他";//“其他”块要显示的文字

double total;//数据的总和

double[] datas;//数据集

String[] texts;//每个数据对应的文字集

//颜色 默认的颜色

private int[] mColors = {

Color.parseColor("#FF4081"), Color.parseColor("#ffc0cb"),

Color.parseColor("#00ff00"), Color.parseColor("#0066ff"), Color.parseColor("#ffee00")

};

private int mTextSize;//文字大小 单位:像素

private int radius = 1000;//半径 在画图时初始化

测量宽高

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

//获取宽高 不要设置wrap_content

mHeight = MeasureSpec.getSize(heightMeasureSpec);

mWidth = MeasureSpec.getSize(widthMeasureSpec);

}

画图

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//无数据 直接返回

if (datas == null || datas.length == 0) return;

centerX = (getRight() - getLeft()) / 2;

centerY = (getBottom() - getTop()) / 2;

int min = mHeight > mWidth ? mWidth : mHeight;

if (radius > min / 2) {

radius = (int) ((min - getPaddingTop() - getPaddingBottom()) / 3.5);

}

//画各个扇形

drawCircle(canvas);

//画线与文字

drawLineAndText(canvas);

}

画扇形

一个圆形统计图是由许多个扇形组成的,我们根据数据计算出每个扇形的角度即可。注意,画弧度的时候,角度是顺时针变大的!即与我们平时的坐标是反过来的

//画扇形

private void drawCircle(Canvas canvas) {

int centerX =( getRight() - getLeft() )/2;//中点

int centerY = ( getBottom() - getTop()) /2;

RectF rect = new RectF((float) (centerX - radius), centerY-radius,

centerX+radius,centerY+radius);//圆形区域

int start = 0;//扇形开始的角度

for (int i = 0; i < (maxNum

float angles = (float) ((datas[i] * 1.0f /total) * 360);//计算扇形的角度

mPaint.setColor(mColors[i%mColors.length]);//颜色

canvas.drawArc(rect,start,angles,true,mPaint);//画扇形

start += angles;//下一个扇形开始的角度

}

//画"其他"部分 即图中灰色的部分

rest =0;//保存其他部分的value

for(int i=maxNum;i

rest+=datas[i];

}

float angles = (float) 360 - start;//角度

mPaint.setColor(Color.GRAY);

canvas.drawArc(rect,start,angles,true,mPaint);

}

画线条和文字

主要是计算各个点的坐标很烦

这里我画出一个图 大家把代码和图对照理解一下

9c30e52ae095

设计图

//画线与文字

private void drawLineAndText(Canvas canvas) {

int start = 0;

//平移画布到中心 所以下面的坐标是从中点开始算起的

canvas.translate(centerX, centerY);

mPaint.setStrokeWidth(4);//线条宽度

//如果数据集过大 那么要合并到其他

for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) {

float angles = (float) ((datas[i] * 1.0f / total) * 360);

//画线条和文字

drawLine(canvas, start, angles, texts[i], mColors[i % mColors.length]);

start += angles;

}

//画其他部分的线条和文字

if (start < 360)//如果start小于360 说明有其他部分

drawLine(canvas, start, 360 - start, others, Color.GRAY);

}

private void drawLine(Canvas canvas, int start, float angles, String text, int color) {

mPaint.setColor(color);

float stopX, stopY;

stopX = (float) ((radius + 40) * Math.cos((2 * start + angles) / 2 * Math.PI / 180));

stopY = (float) ((radius + 40) * Math.sin((2 * start + angles) / 2 * Math.PI / 180));

canvas.drawLine((float) ((radius - 20) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)),

(float) ((radius - 20) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)),

stopX, stopY, mPaint

);

//画横线

int dx;//判断横线是画在左边还是右边

int endX;

if (stopX > 0) {

endX = (centerX - getPaddingRight() - 20);

} else {

endX = (-centerX + getPaddingLeft() + 20);

}

//画横线

canvas.drawLine(stopX, stopY,

endX, stopY, mPaint

);

dx = (int) (endX - stopX);

//测量文字大小

Rect rect = new Rect();

mTextPaint.getTextBounds(text, 0, text.length(), rect);

int w = rect.width();

int h = rect.height();

int offset = 20;//文字在横线的偏移量

//画文字 文字的Y坐标值的是文字底部的Y坐标

canvas.drawText(text, 0, text.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY + h, mTextPaint);

//测量百分比大小

String percentage = angles / 3.60 + "";

percentage = percentage.substring(0, percentage.length() > 4 ? 4 : percentage.length()) + "%";

mTextPaint.getTextBounds(percentage, 0, percentage.length(), rect);

w = rect.width() - 10;

//画百分比

canvas.drawText(percentage, 0, percentage.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY - 5, mTextPaint);

}

这样我们就已经完成了绘制的工作了,接下来就是绑定数据啦!

大家只要把datas和texts填充好数据就可以啦,最好调用一下invalidate()方法请求重绘(如果已经绘制了)。

我使用了一个内部抽象类来实现数据绑定的:

public abstract class ArcViewAdapter {

public void setData(List list) {

datas = new double[list.size()];

texts = new String[list.size()];

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

total += getValue(list.get(i));

datas[i] = getValue(list.get(i));

texts[i] = getText(list.get(i));

}

invalidate();//请求重绘

}

//通过传来的数据集的某个元素 得到具体的数字

public abstract double getValue(T t);

//通过传来的数据集的某个元素 得到具体的描述

public abstract String getText(T t);

}

在布局文件里面引用这个ArcView,然后在MainActivity中绑定数据:

ArcView arcView = (ArcView) findViewById(R.id.arc);

List times = new ArrayList<>();

for (int i = 6; i > 0; i--) {

Times t = new Times();//这个类就只有两个变量

t.hour = i;

t.text = "Number"+i;

times.add(t);

}

//初始化适配器

ArcView.ArcViewAdapter myAdapter = arcView.new ArcViewAdapter(){

@Override

public double getValue(Times times) {

return times.hour;

}

@Override

public String getText(Times times) {

return times.text;

}

};

myAdapter.setData(times);//绑定数据

大家可以设置各种setter以便在Activity中调用,来提高ArcView的灵活性,比如设置颜色、半径、最大块数等等。

具体用法和源码请看GitHub地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值