扇形圖-可視化數(shù)據(jù)

可視化數(shù)據(jù)圖 - 自定義扇形餅狀圖

如覺(jué)得文字版本不適合閱讀疮蹦,請(qǐng)點(diǎn)擊這里

組件特性

  1. 根據(jù)傳入的四種數(shù)據(jù)量大小自動(dòng)顯示扇形的弧度大小以及弧度半徑大小
  2. 根據(jù)弧度所在的角度自動(dòng)畫(huà)出橫線和斜線位置并顯示狀態(tài)

效果圖

這里寫(xiě)圖片描述

實(shí)現(xiàn)原理

  1. 將圓弧和線拆分來(lái)看,先畫(huà)圓弧,再畫(huà)線
  2. 弧度的畫(huà)法,利用sdk的new Recf(start_x, start_y, end_x, end_y)來(lái)限定幅度半徑陨舱,在用canvas.drawArc畫(huà)出多少的弧度
  3. 畫(huà)線抑堡,線分為斜線和直線(斜線是半徑方向,直線時(shí)水平方向)

線的畫(huà)法有兩種:

方法一:
根據(jù)已知弧度的角度和半徑枚驻,在坐標(biāo)軸上利用正余弦sin和cos計(jì)算出斜線的終點(diǎn)坐標(biāo),用drawLine畫(huà)出即可株旷,直線部分亦可

方法二:
畫(huà)布canvas有一個(gè)重要的api再登,canvas.rotate(角度,x, y)晾剖,這個(gè)方法就是圍繞著某一個(gè)點(diǎn)進(jìn)行旋轉(zhuǎn)锉矢;這剛好可以利用到我們的斜線畫(huà)法上來(lái),步驟如下:

  1. 畫(huà)斜線 -- 畫(huà)一個(gè)圓弧后齿尽,以圓心為旋轉(zhuǎn)點(diǎn)沽损,旋轉(zhuǎn)一定圓弧角度,屏幕x軸剛好在一般角度上循头,x軸就是我們斜線方向绵估,此時(shí)斜線的終點(diǎn)就是圓弧半徑在加一點(diǎn)距離即可
  2. 畫(huà)直線 -- 斜線畫(huà)完后,我們?cè)诜聪蛐D(zhuǎn)卡骂,之前旋轉(zhuǎn)多少度国裳,我們就反向旋轉(zhuǎn)多少度,反向旋轉(zhuǎn)的點(diǎn)選取上面斜線的終點(diǎn)全跨,此時(shí)直線的起點(diǎn)就是上面斜線的終點(diǎn)缝左,直線終點(diǎn)我們又是x軸上面加一段距離即可
  3. 上面完成后,我們要回復(fù)初始化狀態(tài)canvas.restoreCount()恢復(fù)到最初的狀態(tài)浓若,然后繼續(xù)畫(huà)另一個(gè)圓弧的斜線和直線

很明顯上述方法二不需要特別復(fù)雜的計(jì)算即可完成渺杉,我這里也采用了方法二

使用方法

布局layout

<com.jack.sectorview.SectorView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        app:lost_status="@color/colorLightBlue"
        app:open_status="@color/colorPink"
        app:normal_status="@color/colorGreen"
        app:lowpower_status="@color/colorYellow"
        android:id="@+id/sector"/>

上面用了我自定義的屬性,分別用以標(biāo)記幾個(gè)扇形圖的顏色挪钓,可以自定義是越,也可以使用我默認(rèn)的顏色

代碼java

//模擬數(shù)據(jù)  index 0~3分別對(duì)應(yīng)扇形圖的 狀態(tài)0~3
        ArrayList<Integer> testData = new ArrayList<>();
        testData.add(1511);
        testData.add(1156);
        testData.add(7541);
        testData.add(5330);

        mSectorView = (SectorView) findViewById(R.id.sector);
        mSectorView.setmAngelePerStatus(testData);

如上面注釋,數(shù)據(jù)對(duì)應(yīng)關(guān)系看效果圖就可以了诵原;

mSectorView.setmAngelePerStatus(testData);這句代碼的作用就是講每個(gè)數(shù)據(jù)量轉(zhuǎn)化為角度英妓,方法名可能取得不恰當(dāng)

部分代碼解釋

成員

    private int[]  mAngelePerStatus;   //每個(gè)狀態(tài)對(duì)應(yīng)的角度
    private int[]  mAngeleSort;        //對(duì)上面的角度進(jìn)行排序挽放,此數(shù)組存放了上面每個(gè)角度排在第幾位
    /**
     * 圓心類绍赛,標(biāo)注圓心的位置
     */
    private class CirclePoint{
        int x;
        int y;              //圓心的坐標(biāo)
        int radius;
        int[] radiusLevel = new int[4];                 //因?yàn)橛兴膫€(gè)圓弧蔓纠,所以設(shè)置了四個(gè)等級(jí)的圓弧半徑
    }

畫(huà)圓弧

/**
     * 畫(huà)基礎(chǔ)的扇形,必須先畫(huà)扇形后面再畫(huà)橫線斜線等
     * @param canvas
     */
    private void drawSector(Canvas canvas){

        RectF rectF;
        int startAngle = 0;
        for(int i = 0; i < mAngelePerStatus.length; i++){
            if(mAngelePerStatus[i] <= 0){                      //小于等于0說(shuō)明這部分沒(méi)有的
                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 - currentAngleRadius;
            int endX   = mCircle.x + currentAngleRadius;
            int endY   = mCircle.y + currentAngleRadius;
            rectF = new RectF(startX, startY, endX, endY);
            mPaint.setColor(mColorPerStatus[i]);
            canvas.drawArc(rectF, startAngle, sweep, true, mPaint);

            startAngle += mAngelePerStatus[i];
        }


    }

圓弧這個(gè)好理解吗蚌,拿到圓弧角度腿倚,拿到其圓弧半徑,在canvas上畫(huà)出即可蚯妇;在計(jì)算角度的時(shí)候可能出現(xiàn)了1度左右的誤差敷燎,所以為了防止最后一個(gè)角度沒(méi)有畫(huà)到360度的線上,我做了一定的角度補(bǔ)償

畫(huà)斜線和直線


    /**
     * 畫(huà)線和字符
     * @param canvas
     */
    private void 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 currentAngleLevel = mAngeleSort[i];
            int rotateAngle =  baseAngle + mAngelePerStatus[i] / 2;             //需要旋轉(zhuǎn)的角度
            //剛好在坐標(biāo)軸上的情況箩言,畫(huà)斜線會(huì)變成垂直水平的線  不好看  稍微偏移一下
            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);        //畫(huà)斜線
            canvas.rotate(-rotateAngle, slashLineEndX, mCircle.y);


            int horizonLineEndX;
            int textStartY;
            int textStartX;
            //畫(huà)水平直線時(shí)要考慮在左邊還是右邊硬贯,兩邊起點(diǎn)和終點(diǎn)是有區(qū)別的
            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);  //畫(huà)直線

            //畫(huà)字時(shí)考慮上半圓和瞎下半圓時(shí),上半圓字在上面陨收,下半圓字在下面
            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);

        }

    }

思路就是我最上面的原理一樣饭豹,旋轉(zhuǎn)和反向旋轉(zhuǎn),旋轉(zhuǎn)的時(shí)候如果剛好旋轉(zhuǎn)到90务漩、180拄衰、370這樣的角度上,斜線會(huì)出現(xiàn)垂直和水平的斜線饵骨,這樣不美觀翘悉,所以當(dāng)出現(xiàn)這些情況又做了一些角度補(bǔ)償

相鄰角度太小的情況

測(cè)試的時(shí)候發(fā)現(xiàn)一個(gè)問(wèn)題,相鄰兩個(gè)角度太小如3和6度居触,這種情況導(dǎo)致斜線或直線或漢子會(huì)出現(xiàn)重合的現(xiàn)象妖混,這又是不好的,所以這個(gè)方法是就是用來(lái)處理這個(gè)問(wèn)題的轮洋;采取的策略就是:

  1. 當(dāng)檢測(cè)出角度小于5度并相鄰角度差也小于5制市,會(huì)強(qiáng)制后面一個(gè)角度變大一點(diǎn),這樣增大了角度間距砖瞧,寫(xiě)字時(shí)就不會(huì)重合
  2. 最后一個(gè)角度和第一個(gè)角度出現(xiàn)了上述情況后息堂,就不能茫然的把第一個(gè)角度變大,變大可能會(huì)導(dǎo)致第一個(gè)和第二個(gè)又重合了块促,所以就只能把第一個(gè)加的值加小一點(diǎn)荣堰,這樣就可以解決了
  3. 上述的做法會(huì)帶來(lái)一個(gè)問(wèn)題,就是四個(gè)角度和會(huì)超過(guò)360竭翠;這需要把多出來(lái)的角度算出來(lái)振坚,從四個(gè)角度中最大角度減去多出來(lái)的就可以了

    /**
     * 角度小于5度的情況,并且相鄰角度相差不大的要相鄰狀態(tài)其中一個(gè)要加一個(gè)角度,
     * 否則寫(xiě)字時(shí)文字會(huì)重合在一起
     * @param array
     */
    private int angleBuChang(int[] array){

        int length = array.length - 1;

        if(length <= 2){
            return 360;
        }
        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){      //這兩個(gè)加數(shù)就是調(diào)整角度差的
                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;
    }

綜上斋扰,基本上解決了所有的問(wèn)題了渡八,如還有不正確的啃洋,可以給我指正并修改;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屎鳍,一起剝皮案震驚了整個(gè)濱河市宏娄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逮壁,老刑警劉巖孵坚,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窥淆,居然都是意外死亡卖宠,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)忧饭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扛伍,“玉大人,你說(shuō)我怎么就攤上這事词裤〈倘鳎” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵亚斋,是天一觀的道長(zhǎng)作媚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)帅刊,這世上最難降的妖魔是什么纸泡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮赖瞒,結(jié)果婚禮上女揭,老公的妹妹穿的比我還像新娘。我一直安慰自己栏饮,他們只是感情好吧兔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著袍嬉,像睡著了一般境蔼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伺通,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天箍土,我揣著相機(jī)與錄音,去河邊找鬼罐监。 笑死吴藻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弓柱。 我是一名探鬼主播沟堡,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侧但,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了航罗?” 一聲冷哼從身側(cè)響起禀横,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伤哺,沒(méi)想到半個(gè)月后燕侠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體者祖,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡立莉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了七问。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜓耻。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖械巡,靈堂內(nèi)的尸體忽然破棺而出刹淌,到底是詐尸還是另有隱情,我是刑警寧澤讥耗,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布有勾,位于F島的核電站,受9級(jí)特大地震影響古程,放射性物質(zhì)發(fā)生泄漏蔼卡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一挣磨、第九天 我趴在偏房一處隱蔽的房頂上張望雇逞。 院中可真熱鬧,春花似錦茁裙、人聲如沸塘砸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)掉蔬。三九已至,卻和暖如春矾瘾,著一層夾襖步出監(jiān)牢的瞬間女轿,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工霜威, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谈喳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓戈泼,卻偏偏與公主長(zhǎng)得像婿禽,于是被迫代替她去往敵國(guó)和親赏僧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 一:canvas簡(jiǎn)介 1.1什么是canvas扭倾? ①:canvas是HTML5提供的一種新標(biāo)簽 ②:HTML5 ...
    GreenHand1閱讀 4,691評(píng)論 2 32
  • 標(biāo)簽:canvas 簡(jiǎn)單示例 入門(mén)知識(shí)作者: 張耀國(guó) ( IgorZhang )E-mail: igorzhang...
    IgorZhang閱讀 2,780評(píng)論 4 7
  • 轉(zhuǎn)載請(qǐng)聲明 原文鏈接 關(guān)注公眾號(hào)獲取更多資訊 這篇文章主要總結(jié)H5的一些新增的功能以及一些基礎(chǔ)歸納淀零,這里只是一個(gè)提...
    程序員poetry閱讀 9,073評(píng)論 22 225
  • 一、canvas簡(jiǎn)介 1.1 什么是canvas膛壹?(了解) 是HTML5提供的一種新標(biāo)簽 Canvas是一個(gè)矩形區(qū)...
    Looog閱讀 3,942評(píng)論 3 40
  • 睡不著 難受
    vcayer閱讀 228評(píng)論 0 0