Android Path 最佳實踐之繪制雷達圖

xi4t6w3x 7年前發布 | 13K 次閱讀 安卓開發 Android開發 移動開發

第一步:繪制蜘蛛網絡

private void init() {
    mainPaint=new Paint();
    mainPaint.setColor(Color.BLACK);
    mainPaint.setAntiAlias(true);
    mainPaint.setStrokeWidth(1);
    mainPaint.setStyle(Paint.Style.STROKE);
}

@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { radius=Math.min(w,h)/2*0.9f; centerX=w/2; centerY=h/2; //一旦size發生改變,重新繪制 postInvalidate(); super.onSizeChanged(w, h, oldw, oldh); }

@Override protected void onDraw(Canvas canvas) { drawPolygon(canvas); }

/**

  • 繪制多邊形
  • @param canvas / private void drawPolygon(Canvas canvas){ Path path=new Path(); //1度=1PI/180 360度=2PI 那么我們每旋轉一次的角度為2PI/內角個數 //中心與相鄰兩個內角相連的夾角角度 angle= (float) (2*Math.PI/count); //每個蛛絲之間的間距 float r= radius/(count-1); for (int i = 0; i < count; i++) {
     //當前半徑
     float curR=r*i;
     path.reset();
     for (int j = 0; j < count; j++) {
         if(j==0){
             path.moveTo(centerX+curR,centerY);
         }else {
             //對于直角三角形sin(x)是對邊比斜邊,cos(x)是底邊比斜邊,tan(x)是對邊比底邊
             //因此可以推導出:底邊(x坐標)=斜邊(半徑)*cos(夾角角度)
             //               對邊(y坐標)=斜邊(半徑)*sin(夾角角度)
             float x = (float) (centerX+curR*Math.cos(angle*j));
             float y = (float) (centerY+curR*Math.sin(angle*j));
             path.lineTo(x,y);
         }
     }
     path.close();
     canvas.drawPath(path,mainPaint);
    
    }</code></pre>

    繪制蜘蛛網絡其實就是繪制指定邊數的正多邊形,這一步比較簡單,比較難的可能就是每個頂點的算法,相關注釋我都寫了,還有一張來自互聯網的圖以助于思考,如下:

    多邊形夾角示意圖

    繪制出的多邊形成品如下:

    多邊形效果.gif

    動畫效果只是寫了 set 方法,用 handler 實現,代碼如下:

    //設置數值種類
    public void setCount(int count) {
     this.count = count;
     postInvalidate();
    }

//設置蜘蛛網顏色 public void setMainPaint(Paint mainPaint) { this.mainPaint = mainPaint; postInvalidate(); }</code></pre>

調用方法:

mainPaint=new Paint();
mainPaint.setAntiAlias(true);
mainPaint.setStrokeWidth(1);
mainPaint.setStyle(Paint.Style.STROKE);
Handler handler=new Handler();
for (int i = 3; i < 20; i++) {
final int finalI = i;
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        mRdv.setCount(finalI);
        mainPaint.setStrokeWidth(finalI);
        mRdv.setMainPaint(mainPaint);
    }
},i*300);
}

第二步:繪制對角線

/**

  • 繪制直線 */ private void drawLines(Canvas canvas){ Path path=new Path(); for (int i = 0; i < count; i++) {
     path.reset();
     path.moveTo(centerX,centerY);
     float x = (float) (centerX+radius*Math.cos(angle*i));
     float y = (float) (centerY+radius*Math.sin(angle*i));
     path.lineTo(x,y);
     canvas.drawPath(path,mainPaint);
    
    } }</code></pre>

    這一步比較簡單,就是將中心點和各個頂點連接起來,效果如下:

    多邊形效果.gif

    第三步:繪制標題文字

    /**
  • 繪制標題文字 *
  • @param canvas */ private void drawTitle(Canvas canvas) { if (count != titles.size()) {

     return;
    

    } //相關知識點:http://mikewang.blog.51cto.com/3826268/871765/ Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); float fontHeight = fontMetrics.descent - fontMetrics.ascent; //繪制文字時不讓文字和雷達圖形交叉,加大繪制半徑 float textRadius = radius + fontHeight; double pi = Math.PI; for (int i = 0; i < count; i++) {

     float x = (float) (centerX + textRadius * Math.cos(angle * i));
     float y = (float) (centerY + textRadius * Math.sin(angle * i));
     //當前繪制標題所在頂點角度
     float degrees = angle * i;
     //從右下角開始順時針畫起,與真實坐標系相反
     if (degrees >= 0 && degrees < pi / 2) {//第四象限
         float dis=textPaint.measureText(titles.get(i))/(titles.get(i).length()-1);
         canvas.drawText(titles.get(i), x+dis, y, textPaint);
     } else if (degrees >= pi / 2 && degrees < pi) {//第三象限
         float dis=textPaint.measureText(titles.get(i))/(titles.get(i).length()-1);
         canvas.drawText(titles.get(i), x-dis, y, textPaint);
     } else if (degrees >= pi && degrees < 3 * pi / 2) {//第二象限
         float dis=textPaint.measureText(titles.get(i))/(titles.get(i).length());
         canvas.drawText(titles.get(i), x-dis, y, textPaint);
     } else if (degrees >= 3 * pi / 2 && degrees <= 2 * pi) {//第一象限
         canvas.drawText(titles.get(i), x, y, textPaint);
     }
    
    

    }

}</code></pre>

效果如下:

image.png

第四步:繪制覆蓋區域

要繪制覆蓋區域,首先要指定最大值和每個分類的具體數值,有了這些數值之后,就可以繪制了。

代碼如下:

/**

  • 繪制覆蓋區域 */ private void drawRegion(Canvas canvas){ valuePaint.setAlpha(255); Path path=new Path(); for (int i = 0; i < count; i++) {
     //計算該數值與最大值比例
     Double perCenter = data.get(i)/maxValue;
     //小圓點所在位置距離圓心的距離
     double perRadius=perCenter*radius;
     float x = (float) (centerX + perRadius * Math.cos(angle * i));
     float y = (float) (centerY + perRadius * Math.sin(angle * i));
     if(i==0){
         path.moveTo(x,y);
     }else {
         path.lineTo(x,y);
     }
     //繪制小圓點
     canvas.drawCircle(x,y,10,valuePaint);
    
    } //閉合覆蓋區域 path.close(); valuePaint.setStyle(Paint.Style.STROKE); //繪制覆蓋區域外的連線 canvas.drawPath(path, valuePaint); //填充覆蓋區域 valuePaint.setAlpha(128); valuePaint.setStyle(Paint.Style.FILL); canvas.drawPath(path,valuePaint); }</code></pre>

    看一下效果:

    image.png

    再來看一下動態的效果吧:

    多邊形效果.gif

    總結

    終于完成了,全部代碼在下面:

    Android雷達圖全部代碼

    主要是參考 crazy__chen 大神的博客,鏈接貼在下面,做了一遍其實還蠻簡單的,這個控件還有很多不完善的,如果實際使用需要改善的地方還有很多,如果有不足希望大家可以告訴我,謝謝!!

    參考資料

    Android雷達圖(蜘蛛網圖)繪制

    Path之基本操作

     

    來自:http://www.jianshu.com/p/afe7bfe7a3ee

     

 本文由用戶 xi4t6w3x 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!