android自定义View(仪表盘),多属性可设置


需求:以仪表盘的形式展示数据,要求区间分明,且灵活。


功能(可设置属性):  


                     当前指示值         

                     仪表盘种类是不超过半圆,还是超过半圆

                     大刻度份数和大刻度内小刻度的份数 

                     指针和原点的颜色(默认为指针是红色,圆点是灰色) 

                     外弧的色条,即数据区间

                     刻度和刻度值的颜色(默认为跟随外弧的颜色)         

                     原点上方的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;    //指针的颜色
    //getset忽略

//外弧色带
public class HighlightCR {

    private int mStartAngle;//开始角度
    private int mSweepAngle;//需要画的度数
    private int mColor;//色带颜色
    //getset忽略


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);
}


                          做开发,需要脚踏实地,日积月累,愿你我共勉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章