需求:以仪表盘的形式展示数据,要求区间分明,且灵活。
功能(可设置属性):
当前指示值
仪表盘种类是不超过半圆,还是超过半圆
大刻度份数和大刻度内小刻度的份数
指针和原点的颜色(默认为指针是红色,圆点是灰色)
外弧的色条,即数据区间
刻度和刻度值的颜色(默认为跟随外弧的颜色)
原点上方的Text字体的大小和颜色(默认为黑色,字体大小默认为半径的一定比例)
原点下方的Text字体的大小和颜色(默认为黑色,字体大小默认为半径的一定比例)
项目地址:https://github.com/AndroidCloud/PieDashboard 如有不足,欢迎各位issues和开支优化
GitHub地址:https://github.com/AndroidCloud
最终实现效果:
技术路线(简要技术思路,具体实现详见GitHub的Demo):
1,封装DashboradBean对象和HighlightCR对象。用于传递属性到View中进行绘制
public class DashboradBean {
private int maxValue; //刻度盘最大值
private int minValue; //刻度盘最小值
private int bigSliceCount; //刻度盘大刻度区间数
private int smallSliceCount; //刻度盘大刻度中小刻度区间数
private int StartAngle; //开始角度
private int allAngle; //需要画的总角度
private List<HighlightCR> highlightCRList; //外弧色带的集合
private boolean isHalf; //是否是半圆以内
private int scaleColor; //刻度值的颜色,默认0时,颜色随色带变化
private int scaleTextColor; //刻度值的读数的颜色,默认0时,颜色随色带变化
private int centerPointColor;//中心原点的颜色
private int pointerColor; //指针的颜色
//get和set忽略
//外弧色带
public class HighlightCR {
private int mStartAngle;//开始角度
private int mSweepAngle;//需要画的度数
private int mColor;//色带颜色
//get和set忽略
2,根据传递的属性对View进行绘制
首先是控制View的大小适配(根据设定的宽来适配高),如果总弧度小于半圆,View的高度取View的宽度的1/2再 多一点,以为画外弧的画笔要设置粗一点,所以为了绘制原点,需要留出部分位置。如果总弧度大于半圆, View的高度取值和宽度一样。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width=MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
if (dashboradBean!=null){
//总弧度是半圆以内
if (dashboradBean.isHalf()){
height =(int) (width/2f+(width/2f/7.5f));
}else{
//总弧度大于半圆
height=width;
}
setMeasuredDimension(width, height);
}
}
3,进行绘制,分别绘制刻度盘的弧形,刻度盘的和刻度值,指针和中心原点,原点上方Text,原点下方Text
@Override
protected void onDraw(Canvas canvas) {
mRadius=getWidth()/2f;
mHighlightRadius=mRadius/7.5f;
mCenterX=mRadius;
mCenterY=mRadius;
super.onDraw(canvas);
if (dashboradBean != null) {
drawMeasures(canvas);/*** 刻度线和刻度值*/
drawStripe(canvas);/**刻度盘的弧形*/
drawTopTexts(canvas);/**画中心原点上方文本*/
drawEndTexts(canvas);/**画中心原点下方文本*/
drawPointer(canvas); /**画指针和中心原点*/
}
}
绘制刻度线和刻度值(刻度线的长度和刻度值的字体大小都是根据View的宽度来按照一定比例换算得来,以达到 适配的目的),原理是旋转画布后画好,在旋转回来,依次把所有刻度线和刻度值画好
/*** 刻度线和刻度值*/
private void drawMeasures(Canvas canvas) {
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(2);
Paint p=new Paint();
p.setStyle(Paint.Style.FILL);
p.setStrokeWidth(1);
p.setTextSize(mHighlightRadius / 1.2f);
p.setAntiAlias(true);
p.setTextAlign(Paint.Align.CENTER);
float averageBigangle=((float)dashboradBean.getAllAngle()) /((float)dashboradBean.getBigSliceCount());
for (int i = 0; i <= dashboradBean.getBigSliceCount(); i++) {
//绘制大刻度
float angle = i *averageBigangle+dashboradBean.getStartAngle() ;
float[] point1 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f, angle);
float[] point2 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f-mHighlightRadius/5f*2.8f, angle);
if ( dashboradBean.getHighlightCRList() != null) {
for (int j = 0; j < dashboradBean.getHighlightCRList().size(); j++) {
HighlightCR highlightCR = dashboradBean.getHighlightCRList().get(j);
if (highlightCR.getColor() == 0 || highlightCR.getSweepAngle() == 0)
continue;
if (angle>=highlightCR.getStartAngle()&&angle <= highlightCR.getStartAngle() + highlightCR.getSweepAngle()) {
paint.setColor(highlightCR.getColor());
p.setColor(highlightCR.getColor());
break;
}
}
} else {
paint.setColor(Color.BLACK);
p.setColor(Color.BLACK);
}
if (dashboradBean.getScaleColor()!=0){
paint.setColor(dashboradBean.getScaleColor());
}
paint.setStrokeWidth(mHighlightRadius / 6f);
canvas.drawLine(point1[0], point1[1], point2[0], point2[1], paint);
//绘制圆盘上的数字
String number =trimFloat(((float) (dashboradBean.getMaxValue()-dashboradBean.getMinValue()))
/((float)dashboradBean.getBigSliceCount())*i+(float)dashboradBean.getMinValue()) +"";
if (dashboradBean.getScaleTextColor()!=0){
p.setColor(dashboradBean.getScaleTextColor());
}
//旋转绘制
canvas.rotate(360f - (float) dashboradBean.getAllAngle() / 2f + (float) i * averageBigangle, mCenterX, mCenterY);
canvas.drawText(number, mCenterX, mHighlightRadius * 2.75f, p);
canvas.rotate(-360f+(float)dashboradBean.getAllAngle()/2f-(float)i*averageBigangle,mCenterX,mCenterY);
}
//绘制小的子刻度
float averageSmallangle=((float)dashboradBean.getAllAngle() )/(float)(dashboradBean.getSmallSliceCount()*dashboradBean.getBigSliceCount());
for (int i = 0; i < dashboradBean.getSmallSliceCount()*dashboradBean.getBigSliceCount(); i++) {
float angle = i * averageSmallangle + dashboradBean.getStartAngle();
float[] point1 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f, angle);
float[] point2 = getCoordinatePoint(mRadius-mHighlightRadius/0.85f-mHighlightRadius/5f*1.4f, angle);
if ( dashboradBean.getHighlightCRList() != null) {
for (int j = 0; j < dashboradBean.getHighlightCRList().size(); j++) {
HighlightCR highlightCR = dashboradBean.getHighlightCRList().get(j);
if (highlightCR.getColor() == 0 || highlightCR.getSweepAngle() == 0)
continue;
if (angle>=highlightCR.getStartAngle()&&angle <= highlightCR.getStartAngle() + highlightCR.getSweepAngle()) {
paint.setColor(highlightCR.getColor());
break;
}
}
} else {
paint.setColor(Color.BLACK);
}
if (dashboradBean.getScaleColor()!=0){
paint.setColor(dashboradBean.getScaleColor());
}
paint.setStrokeWidth(mHighlightRadius/6f/2f);
canvas.drawLine(point1[0], point1[1], point2[0], point2[1], paint);
}
}
绘制刻度盘的外弧,包括粗的外弧和细的外弧(外弧的粗细程度都是根据View的宽度来按照一定比例换算得来, 以达到适配的目的)
/**刻度盘的弧形*/
private void drawStripe(Canvas canvas) {
if (dashboradBean.getHighlightCRList()!=null){
for (int i = 0; i < dashboradBean.getHighlightCRList().size(); i++) {
HighlightCR highlightCR = dashboradBean.getHighlightCRList().get(i);
if (highlightCR.getColor() == 0 || highlightCR.getSweepAngle() == 0)
continue;
paint.setColor(highlightCR.getColor());
paint.setStrokeWidth(mHighlightRadius);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
RectF rectF1=new RectF(mHighlightRadius/2f,mHighlightRadius/2f,getWidth()-mHighlightRadius/2f,getWidth()-mHighlightRadius/2f);
RectF rectF2=new RectF(mHighlightRadius/0.85f,mHighlightRadius/0.85f,getWidth()-mHighlightRadius/0.85f,getWidth()-mHighlightRadius/0.85f);
canvas.drawArc(rectF1, highlightCR.getStartAngle(),
highlightCR.getSweepAngle(), false, paint);
paint.setStrokeWidth(mHighlightRadius/6f);
canvas.drawArc(rectF2, highlightCR.getStartAngle(),
highlightCR.getSweepAngle(), false, paint);
}
}
}
绘制指针和原点,指针为一个封闭的三角形,指针的顶点根据传入的需要显示的当前值来进行计算(计算前先判 断该值是否在有效区间内,超过最大值显示最大值,超过最小值显示最小值)指针长度按照View的宽度的一 定比例换算,达到适配,原点为圆形。
/**画指针和中心原点*/
private void drawPointer(Canvas canvas) {
float degree=0;
if (realTimeValue>=dashboradBean.getMinValue()&&realTimeValue<=dashboradBean.getMaxValue()){
degree=(float)(dashboradBean.getStartAngle())+((realTimeValue-dashboradBean.getMinValue())/
(dashboradBean.getMaxValue()-dashboradBean.getMinValue()))*(float)dashboradBean.getAllAngle();
}else if(realTimeValue<dashboradBean.getMinValue()){
degree=(float)dashboradBean.getStartAngle();
}else{
degree=(float)(dashboradBean.getStartAngle())+(float)dashboradBean.getAllAngle();
}
float point[]=getCoordinatePoint(mRadius-mHighlightRadius*2.4f,degree);
paint.setStyle(Paint.Style.FILL);
Path p=new Path();
p.moveTo(point[0], point[1]);
float point_left[]=getCoordinatePoint(mHighlightRadius / 2f,degree-90);
float point_right[]=getCoordinatePoint(mHighlightRadius / 2f,degree+90);
p.lineTo(point_left[0], point_left[1]);
p.lineTo(point_right[0], point_right[1]);
p.close();
if(dashboradBean.getPointerColor()!=0){
paint.setColor(dashboradBean.getPointerColor());
}else{
paint.setColor(Color.RED);
}
canvas.drawPath(p, paint);
if (dashboradBean.getCenterPointColor()!=0){
paint.setColor(dashboradBean.getCenterPointColor());
} else {
paint.setColor(Color.parseColor("#A9AFAE"));
}
canvas.drawCircle(mCenterX,mCenterY,mHighlightRadius/1.65f,paint);
}
最后绘制原点上方和下方的Text,字体绘制的位置和字体的大小都是根据View的宽度来按照一定比例换算得到的。
/**画中心原点下方文本*/
private void drawEndTexts(Canvas canvas) {
if (EndTextSize==0){
paint.setTextSize(mHighlightRadius / 1.2f);
}else {
paint.setTextSize(EndTextSize);
}
if (EndTextColor==0){
paint.setColor(Color.BLACK);
}else{
paint.setColor(EndTextColor);
}
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(endText,mCenterX,mCenterY+3f*mHighlightRadius,paint);
}
/**画指针和中心原点*/
private void drawTopTexts(Canvas canvas) {
if (TopTextSize==0){
paint.setTextSize(mHighlightRadius / 1.4f);
}else {
paint.setTextSize(TopTextSize);
}
if (TopTextColor==0){
paint.setColor(Color.BLACK);
}else{
paint.setColor(TopTextColor);
}
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.FILL);
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(topText,mCenterX,mCenterY-mHighlightRadius,paint);
}
4,在Activity中使用,Demo的第一组示例布局如下所示,宽度取屏幕的一半
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<com.example.vmmet.mypiedashboard.view.NewDashboardView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/view1"
/>
<com.example.vmmet.mypiedashboard.view.NewDashboardView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/view2"
/>
</LinearLayout>
如图中所示的第一个示例。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
/**大刻度份数和小刻度份数(半圆)*/
setView_1_2();
/**大刻度份数和小刻度份数(半圆)*/
private void setView_1_2() {
List<HighlightCR> highlights = new ArrayList<>();
highlights.add(new HighlightCR(180, 40, Color.parseColor("#4CAF50")));
highlights.add(new HighlightCR(220, 50, Color.parseColor("#EEC900")));
highlights.add(new HighlightCR(270, 90, Color.parseColor("#F44336")));
DashboradBean dashboradBean=new DashboradBean();
dashboradBean.setHighlightCRList(highlights);
dashboradBean.setAllAngle(180);
dashboradBean.setStartAngle(180);
dashboradBean.setBigSliceCount(5);
dashboradBean.setSmallSliceCount(3);
dashboradBean.setMaxValue(100);
dashboradBean.setMinValue(0);
dashboradBean.setIsHalf(true);
view1.setDashboradBean(dashboradBean);
view1.setRealTimeValue(60);
///
DashboradBean dashboradBean2=new DashboradBean();
dashboradBean2.setHighlightCRList(highlights);
dashboradBean2.setAllAngle(180);
dashboradBean2.setStartAngle(180);
dashboradBean2.setBigSliceCount(10);
dashboradBean2.setSmallSliceCount(5);
dashboradBean2.setMaxValue(200);
dashboradBean2.setMinValue(100);
dashboradBean2.setIsHalf(true);
view2.setDashboradBean(dashboradBean2);
view2.setRealTimeValue(120);
}
做开发,需要脚踏实地,日积月累,愿你我共勉