分析實(shí)現(xiàn)Android自定義View之扇形圖

繼承View基類,畫了這樣的扇形圖

粗糙的樣子^_^

直接來(lái)步驟吧

(參考了GcsSloop的教程)

1.分析

自定義View需要認(rèn)真的分析下,里面還是會(huì)用到一些數(shù)學(xué)知識(shí)

  • 首先是扇形該怎么表現(xiàn)
    1. 扇形的外觀是個(gè)圓弧式曲,而且是圍繞一個(gè)中心點(diǎn)旋轉(zhuǎn)一定角度構(gòu)成的
    2. View主要的繪制都是通過(guò)畫布(canvas)進(jìn)行囚枪,canvas里提供了許多繪制的方法。其中有一個(gè)
    drawArc()畫圓弧的方法谊娇。

      public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter,@NonNull Paint paint) {
              drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter,paint);
      }   
    

這個(gè)方法就給畫扇形提供了捷徑肺孤。

  • 發(fā)現(xiàn)里面有個(gè)RectF參數(shù)罗晕,隱約有點(diǎn)是矩形的感覺(jué),網(wǎng)查一下:
    這個(gè)類包含一個(gè)矩形的四個(gè)單精度浮點(diǎn)坐標(biāo)赠堵。矩形通過(guò)上下左右4個(gè)邊的坐標(biāo)來(lái)表示一個(gè)矩形

參數(shù)說(shuō)明一下:

  • oval :指定圓弧的外輪廓矩形區(qū)域
  • startAngle: 圓弧起始角度小渊,單位為度。
  • sweepAngle: 圓弧掃過(guò)的角度茫叭,順時(shí)針?lè)较虺晏耄瑔挝粸槎取?/li>
  • useCenter: 如果為True時(shí),在繪制圓弧時(shí)將圓心包括在內(nèi)揍愁,通常用來(lái)繪制扇形呐萨。
  • paint: 繪制圓弧的畫筆屬性,如顏色莽囤,是否填充等谬擦。
cu cao的分析^_^

1.1 分析需要哪些數(shù)據(jù)

參照上面的畫圓弧函數(shù)的參數(shù)需求和上面那個(gè)結(jié)(cu)果(cao)圖

要使用canvas畫扇形需要這些參數(shù)吧

  • 外輪廓矩形
  • 起始角度
  • 掃過(guò)的角度
  • 畫筆

再看上面那個(gè)結(jié)(cu)果(cao)圖,還需要

  • 顏色
  • 文字

這樣就可以定義一個(gè)數(shù)據(jù)結(jié)構(gòu)了

public class ViewData {
        public String name; //名字
        public int value;   //數(shù)值

        public int color;   //顏色
        public float percentage; //百分比
        public float angle; //角度

        public ViewData(int value, String name) {
        this.value = value;
        this.name = name;
        }
}

1.2 角度計(jì)算

扇形圖應(yīng)該反應(yīng)的是里面每塊扇形占總數(shù)的百分比朽缎,在我們的數(shù)據(jù)結(jié)構(gòu)里應(yīng)該是通過(guò)數(shù)值(value)計(jì)算出扇形區(qū)域占總體的比例和掃過(guò)的角度

  • 比例 = 數(shù)值 / 各塊數(shù)值之和
  • 角度 = 比例 x 圓周(360度)

1.3 文字位置確定

文字不像扇形區(qū)域可以直接調(diào)用畫布的圓弧方法繪制惨远,文字使用畫布的drawText()方法

public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
        ......
    }

查看源碼可知需要傳遞字符內(nèi)容射众、x坐標(biāo)沙兰、y坐標(biāo)和畫筆,字符內(nèi)容和畫筆容易了袁,就是坐標(biāo)位置我們要好好想想

文字位置

需要注意這里的文字的位置坐標(biāo)都是基于canvas為父元素的

回到以前的數(shù)學(xué)應(yīng)用題最筒,先擺出我們有哪些已知量

  • 扇形圓的半徑
  • 每塊扇形的掃過(guò)的角度
  • 起始的角度
文字位置
new

想想根據(jù)這些數(shù)據(jù)贺氓,應(yīng)該可以求出文字位置(上圖黑圓點(diǎn))的坐標(biāo)

  • 文字位置角度 = 起始角度 + 扇形塊角度/2
  • 文字x坐標(biāo) = 半徑/2 x cos(文字位置角度)
  • 文字y坐標(biāo) = 半徑/2 x sin(文字位置角度)

分析差不多了,開始實(shí)踐了

2 實(shí)踐

2.1 數(shù)據(jù)結(jié)構(gòu)

public class ViewData {
    public String name; //名字
    public int value;   //數(shù)值

    public int color;   //顏色
    public float percentage; //百分比
    public float angle; //角度

    public ViewData(int value, String name) {
    this.value = value;
    this.name = name;
    }
}

2.2 View實(shí)現(xiàn)

先來(lái)一張圖鎮(zhèn)店

圖片來(lái)自@GcsSloop
public class MyView extends View {
        private int[] mColors = {Color.BLUE, Color.DKGRAY, Color.CYAN, Color.RED, Color.GREEN};
        private Paint paint;    //畫筆
        private ArrayList<ViewData> viewDatas;    //數(shù)據(jù)集
        private int w;          //View寬高
        private int h;
        private RectF rectF;    //矩形

    public MyView(Context context) {
        super(context);
        initPaint();    //設(shè)置畫筆
    }

    //設(shè)置數(shù)據(jù)
    public void setData(ArrayList<ViewData> viewDatas) {
        this.viewDatas = viewDatas;
        initData();     //設(shè)置數(shù)據(jù)的百分度和角度
        invalidate();   //刷新UI
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
    }

    //初始化畫筆
    private void initPaint() {
        paint = new Paint();
        //設(shè)置畫筆默認(rèn)顏色
        paint.setColor(Color.WHITE);
        //設(shè)置畫筆模式:填充
        paint.setStyle(Paint.Style.FILL);
        //
        paint.setTextSize(30);
        //初始化區(qū)域
        rectF = new RectF();
    }

    //確定View大小
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        this.w = w;     //獲取寬高
        this.h = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(w / 2, h / 2);             //將畫布坐標(biāo)原點(diǎn)移到中心位置
        float currentStartAngle = 0;                //起始角度
        float r = (float) (Math.min(w, h) / 2);     //餅狀圖半徑(取寬高里最小的值)
        rectF.set(-r, -r, r, r);                    //設(shè)置將要用來(lái)畫扇形的矩形的輪廓
        for (int i = 0; i < viewDatas.size(); i++) {
            ViewData viewData = viewDatas.get(i);
            paint.setColor(viewData.color);
            //繪制扇形(通過(guò)繪制圓弧)
            canvas.drawArc(rectF, currentStartAngle, viewData.angle, true, paint);
            //繪制扇形上文字
            float textAngle = currentStartAngle + viewData.angle / 2;    //計(jì)算文字位置角度
            paint.setColor(Color.BLACK);
            float x = (float) (r / 2 * Math.cos(textAngle * Math.PI / 180));    //計(jì)算文字位置坐標(biāo)
            float y = (float) (r / 2 * Math.sin(textAngle * Math.PI / 180));
            paint.setColor(Color.YELLOW);        //文字顏色
            canvas.drawText(viewData.name, x, y, paint);    //繪制文字

            currentStartAngle += viewData.angle;     //改變起始角度
        }
    }

    private void initData() {
        if (null == viewDatas || viewDatas.size() == 0) {
            return;
        }

        float sumValue = 0;                 //數(shù)值和
        for (int i = 0; i < viewDatas.size(); i++) {
                ViewData viewData = viewDatas.get(i);
                sumValue += viewData.value;
                int j = i % mColors.length;     //設(shè)置顏色
                viewData.color = mColors[j];
        }

        for (ViewData data : viewDatas) {
            float percentage = data.value / sumValue;    //計(jì)算百分比
            float angle = percentage * 360;           //對(duì)應(yīng)的角度
            data.percentage = percentage;
            data.angle = angle;
        }
    }
}

最終實(shí)現(xiàn)效果就是開(cu)頭(cao)那張了床蜘,自定義View還有很長(zhǎng)路要走

完整代碼請(qǐng)移步這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辙培,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子邢锯,更是在濱河造成了極大的恐慌虏冻,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弹囚,死亡現(xiàn)場(chǎng)離奇詭異厨相,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)鸥鹉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門蛮穿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人毁渗,你說(shuō)我怎么就攤上這事践磅。” “怎么了灸异?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵府适,是天一觀的道長(zhǎng)羔飞。 經(jīng)常有香客問(wèn)我,道長(zhǎng)檐春,這世上最難降的妖魔是什么逻淌? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮疟暖,結(jié)果婚禮上卡儒,老公的妹妹穿的比我還像新娘。我一直安慰自己俐巴,他們只是感情好骨望,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欣舵,像睡著了一般擎鸠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缘圈,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天糠亩,我揣著相機(jī)與錄音,去河邊找鬼准验。 笑死,一個(gè)胖子當(dāng)著我的面吹牛廷没,可吹牛的內(nèi)容都是我干的糊饱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼颠黎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼另锋!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起狭归,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤夭坪,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后过椎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體室梅,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年疚宇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亡鼠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敷待,死狀恐怖间涵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榜揖,我是刑警寧澤勾哩,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布抗蠢,位于F島的核電站,受9級(jí)特大地震影響思劳,放射性物質(zhì)發(fā)生泄漏迅矛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一敢艰、第九天 我趴在偏房一處隱蔽的房頂上張望诬乞。 院中可真熱鬧,春花似錦钠导、人聲如沸震嫉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)票堵。三九已至,卻和暖如春逮栅,著一層夾襖步出監(jiān)牢的瞬間悴势,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工措伐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留特纤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓侥加,卻偏偏與公主長(zhǎng)得像捧存,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子担败,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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