3C科技 娛樂遊戲 美食旅遊 時尚美妝 親子育兒 生活休閒 金融理財 健康運動 寰宇綜合

Zi 字媒體

2017-07-25T20:27:27+00:00
加入好友
限時乾貨下載:添加微信公眾號「數據玩家「fbigdata」」回復【2】免費獲取「完整數據分析資料,包括SPSS\SAS\SQL\EXCEL\Project!」(pre-ipo新三板企業投資機會,請聯繫微.信.號:6048856)組件特性根據傳入的四種數據量大小自動顯示扇形的弧度大小以及弧度半徑大小根據弧度所在的角度自動畫出橫線和斜線位置並顯示狀態效果圖實現原理將圓弧和線拆分來看,先畫圓弧,再畫線弧度的畫法,利用sdk的new Recf(start_x, start_y, end_x, end_y)來限定幅度半徑,在用canvas.drawArc畫出多少的弧度畫線,線分為斜線和直線(斜線是半徑方向,直線時水平方向)線的畫法有兩種:方法一: 根據已知弧度的角度和半徑,在坐標軸上利用正餘弦sin和cos計算出斜線的終點坐標,用drawLine畫出即可,直線部分亦可方法二: 畫布canvas有一個重要的api,canvas.rotate(角度,x, y),這個方法就是圍繞著某一個點進行旋轉;這剛好可以利用到我們的斜線畫法上來,步驟如下:畫斜線 -- 畫一個圓弧后,以圓心為旋轉點,旋轉一定圓弧角度,屏幕x軸剛好在一般角度上,x軸就是我們斜線方向,此時斜線的終點就是圓弧半徑在加一點距離即可畫直線 -- 斜線畫完后,我們在反向旋轉,之前旋轉多少度,我們就反向旋轉多少度,反向旋轉的點選取上面斜線的終點,此時直線的起點就是上面斜線的終點,直線終點我們又是x軸上面加一段距離即可上面完成後,我們要回復初始化狀態canvas.restoreCount恢復到最初的狀態,然後繼續畫另一個圓弧的斜線和直線很明顯上述方法二不需要特別複雜的計算即可完成,我這裡也採用了方法二使用方法布局layout上面用了我自定義的屬性,分別用以標記幾個扇形圖的顏色,可以自定義,也可以使用我默認的顏色代碼java//模擬數據 index 0~3分別對應扇形圖的 狀態0~3 int data =newint{1111, 2222, 7431, 5330}; mSectorView = (SectorView) findViewById(R.id.sector); mSectorView.setmAngelePerStatus(data); mSectorView = (SectorView) findViewById(R.id..setmAngelePerStatus(data);如上面註釋,數據對應關係看效果圖就可以了;mSectorView.setmAngelePerStatus(testData);這句代碼的作用就是講每個數據量轉化為角度,方法名可能取得不恰當部分代碼解釋成員privateint mAngelePerStatus; //每個狀態對應的角度 privateint mAngeleSort; //對上面的角度進行排序,此數組存放了上面每個角度排在第幾位 /** * 圓心類,標註圓心的位置 */ privateclassCirclePoint{ int x; int y; //圓心的坐標int radius; int radiusLevel =newint[4]; //因為有四個圓弧,所以設置了四個等級的圓弧半徑 }畫圓弧/** * 畫基礎的扇形,必須先畫扇形後面再畫橫線斜線等 * @param canvas */ privatevoid drawSector(Canvas canvas){ RectF rectF; int startAngle =0; for(int i =0; i < mAngelePerStatus.length; i++){ if(mAngelePerStatus[i] <=0){ //小於等於0說明這部分沒有的 continue;} int sweep = mAngelePerStatus[i]; if(i == mAngelePerStatus.length -1) { sweep =360- startAngle; } int currentAngleLevel = mAngeleSort[i]; int currentAngleRadius = mCircle.radiusLevel[currentAngleLevel]; int startX = mCircle.x - currentAngleRadius; int startY = mCircle.y -int endX = mCircle.x +int endY = mCircle.y + currentAngleRadius; rectF =newRectF(startX, startY, endX, endY); mPaint.setColor(mColorPerStatus[i]); canvas.drawArc(rectF, startAngle, sweep, true, mPaint); startAngle += mAngelePerStatus[i]; } }圓弧這個好理解,拿到圓弧角度,拿到其圓弧半徑,在canvas上畫出即可;在計算角度的時候可能出現了1度左右的誤差,所以為了防止最後一個角度沒有畫到360度的線上,我做了一定的角度補償畫斜線和直線/** * 畫線和字元 * @param canvas */ privatevoid drawLineAndText(Canvas canvas){ int baseAngle =0; int src; for(int i =0; i < mAngelePerStatus.length; i++){ src = canvas.save; if(mAngelePerStatus[i] <=0){ continue; } int rotateAngle = baseAngle + mAngelePerStatus[i] /2; //需要旋轉的角度 //剛好在坐標軸上的情況,畫斜線會變成垂直水平的線 不好看 稍微偏移一下if(rotateAngle%90==0){ rotateAngle = rotateAngle +3; } int slashLineEndX = mCircle.x + mCircle.radiusLevel[currentAngleLevel] + mSlashLineLength; canvas.rotate(rotateAngle, mCircle.x, mCircle.y); mPaint.setColor(mColorPerStatus[i]); canvas.drawLine(mCircle.x, mCircle.y, slashLineEndX, mCircle.y, mPaint); //畫斜線 canvas.rotate(-rotateAngle, slashLineEndX, mCircle.y); int horizonLineEndX; int textStartY; int textStartX; //畫水平直線時要考慮在左邊還是右邊,兩邊起點和終點是有區別的if(rotateAngle 90&& rotateAngle <270){ horizonLineEndX = mCircle.x + mCircle.radiusLevel[currentAngleLevel] - mHorizonLineLength; if(horizonLineEndX <0){ //防止其超出左邊邊界 horizonLineEndX =0; } textStartX = horizonLineEndX; }else{ horizonLineEndX = mCircle.x + mCircle.radiusLevel[currentAngleLevel] + mHorizonLineLength; if(horizonLineEndX mViewWidth){ //防止超出右邊界 horizonLineEndX = mViewWidth; } textStartX = slashLineEndX +5; } Log.i(TAG, "horizontal start "+ slashLineEndX +" "+ horizonLineEndX); canvas.drawLine(slashLineEndX, mCircle.y, horizonLineEndX, mCircle.y, mPaint); //畫直線 //畫字時考慮上半圓和瞎下半圓時,上半圓字在上面,下半圓字在下面 if(rotateAngle <180){ textStartY = mCircle.y +40; }else{ textStartY = mCircle.y -20; } canvas.drawText(mStatusInfo[i], textStartX, textStartY, mTextPaint); canvas.restoreToCount(src); baseAngle += mAngelePerStatus[i]; Log.i(TAG, "baseAngle "+ baseAngle +" rotateAngle "+ rotateAngle); } }思路就是我最上面的原理一樣,旋轉和反向旋轉,旋轉的時候如果剛好旋轉到90、180、370這樣的角度上,斜線會出現垂直和水平的斜線,這樣不美觀,所以當出現這些情況又做了一些角度補償相鄰角度太小的情況測試的時候發現一個問題,相鄰兩個角度太小如3和6度,這種情況導致斜線或直線或漢子會出現重合的現象,這又是不好的,所以這個方法是就是用來處理這個問題的;採取的策略就是:當檢測出角度小於5度並相鄰角度差也小於5,會強制後面一個角度變大一點,這樣增大了角度間距,寫字時就不會重合最後一個角度和第一個角度出現了上述情況后,就不能茫然的把第一個角度變大,變大可能會導致第一個和第二個又重合了,所以就只能把第一個加的值加小一點,這樣就可以解決了上述的做法會帶來一個問題,就是四個角度和會超過360;這需要把多出來的角度算出來,從四個角度中最大角度減去多出來的就可以了/** * 角度小於5度的情況,並且相鄰角度相差不大的要相鄰狀態其中一個要加一個角度, * 否則寫字時文字會重合在一起 * @param array */ privateint angleBuChang(int array){ int length = array.length -1; if(length <=2){ return360; } int angle0, angle1; for(int i =0; i< length +1; i++){ angle0 = array[i]; if(i +1 length){ angle1 = array[0]; }else{ angle1 = array[i+1]; } int angleRemain =Math.abs(angle1 - angle0); if(angle0 <5&& angleRemain <5){ //這兩個加數就是調整角度差的 if(i+1 length){ array[0] += angleRemain +10; }else { array[i+1] += angleRemain +15; } } } int totalAngle =0; for(int i =0; i < length +1; i++){ totalAngle += array[i]; } return totalAngle; }綜上,基本上解決了所有的問題了,如還有不正確的,可以給我指正並修改。

本文由yidianzixun提供 原文連結

寫了 5860316篇文章,獲得 23313次喜歡
精彩推薦