自定義控件之雷達(dá)圖

雷達(dá)圖,城里人管她叫戴布拉圖

某殺的個(gè)人信息那里有個(gè)芹缔。一看就是穩(wěn)如狗

某度的個(gè)人圖譜

一看就是不熱愛生活,不關(guān)心政治瓶盛,沒(méi)有文藝細(xì)胞最欠,不愛學(xué)習(xí),偶爾八卦的技術(shù)宅蓬网。

用andorid的自定義控件實(shí)現(xiàn)以下窒所。最終做出的效果是這樣的

準(zhǔn)備

  1. 為了重用性,有一些參數(shù)需要用戶可以自定義帆锋。比如:數(shù)據(jù),雷達(dá)圖半徑禽额,半徑長(zhǎng)度和實(shí)際值的比例锯厢,雷達(dá)圖的層數(shù),角的數(shù)量(和數(shù)據(jù)相關(guān))

  2. 根據(jù)(1)的數(shù)據(jù)脯倒,運(yùn)用簡(jiǎn)單三角函數(shù)知識(shí)实辑,繪制出基本雷達(dá)圖。

  3. 同樣運(yùn)用三角函數(shù)知識(shí)藻丢,繪制出數(shù)據(jù)區(qū)域

  4. 注意繪制過(guò)程中的一些細(xì)節(jié)

開始擼代碼

1.讓SpiderView繼承View類剪撬,在構(gòu)造方法中調(diào)用init()初始化畫筆等信息
//初始化畫筆  正式使用的時(shí)候可以在這里獲取一些 用戶自定義的attrs參數(shù)
private void init() {
    mSpiderPaint = new Paint();
    mSpiderPaint.setAntiAlias(true);
    mSpiderPaint.setColor(Color.GRAY);
    mSpiderPaint.setStyle(Paint.Style.STROKE);

    mDataPaint = new Paint();
    mDataPaint.setAntiAlias(true);
    mDataPaint.setColor(Color.BLUE);
    mDataPaint.setStyle(Paint.Style.FILL_AND_STROKE);


    mTextPaint = new Paint();
    mTextPaint.setTextSize(20);
    mTextPaint.setStyle(Paint.Style.FILL);
    mTextPaint.setColor(Color.BLACK);
}
2.在onMeasure中簡(jiǎn)單處理view默認(rèn)大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //可以在這里處理View自身的測(cè)量情況  wrap
    int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
    int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

    if(widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode ==MeasureSpec.AT_MOST){
        heightSpecSize = widthSpecSize = DEFAULTSIZE;
    }else{
        heightSpecSize = widthSpecSize = Math.min(heightSpecSize,widthSpecSize);
    }
    setMeasuredDimension(widthSpecSize,heightSpecSize);
}
3.onSizeChandged方法中確定中心位置,方便后面的平移
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mCenterW = w/2;
    mCenterH = h/2;
    mDefaultMaxR = Math.min(w,h)/2f-30;//隨便計(jì)算一下 最大值
    Log.e("SpiderView",String.format("控件默認(rèn)的最大的R為%f",mDefaultMaxR));
    //防止用戶設(shè)置的值 突破天際
    if(mMaxR >0f){
        mMaxR = Math.min(mDefaultMaxR,mMaxR);
    }else{
        mMaxR = mDefaultMaxR;
    }
}
4.在onDraw方法中利用用戶設(shè)置參數(shù)悠反,畫出雷達(dá)圖
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if(spiders==null||spiders.size()<3) return; //沒(méi)有值或者值小于3 直接不畫
    mSizeCount = spiders.size();
    mR = mMaxR / (mCount-1);
    mAngle = (float) Math.PI*2/mSizeCount;
    canvas.translate(mCenterW,mCenterH);  //將原點(diǎn)移動(dòng)至中心
    //分別繪制
    drawSpiderBase(canvas);
    drawSpiderSupport(canvas);
    drawSpiderData(canvas);
    drawSpiderText(canvas);
}
5.分別繪制 基本上注釋已經(jīng)很清楚了
/**
 * 畫出蛛網(wǎng)
 * @param canvas
 */
private void drawSpiderBase(Canvas canvas) {
    Path path = new Path();
    for (int i = 1;i<mCount;i++){  //最中心的點(diǎn)不用畫出
        path.reset();
        for(int j=0;j<mSizeCount;j++){
            if(j==0){
                path.moveTo(mR*i,0);
            }else{
                //利用三角函數(shù)計(jì)算 坐標(biāo)x, y的值
                path.lineTo((float) (mR*i*Math.cos(mAngle*j)),(float) (mR*i*Math.sin(mAngle*j)));
                //Log.e("SpiderView",String.format("x,y的坐標(biāo)分別為:%f,%f",(float) (mR*i*Math.cos(mAngel*j)),(float) (mR*i*Math.sin(mAngel*j))));
            }
        }
        path.close();
        canvas.drawPath(path,mSpiderPaint);
    }
}

層數(shù)循環(huán)中嵌套角數(shù)循環(huán)残黑,通過(guò)角數(shù)的循環(huán)馍佑,運(yùn)用三角函數(shù)知識(shí)通過(guò)弧度和偽半徑確定多邊形各個(gè)頂點(diǎn)的位置,然后使用path鏈接梨水,最后通過(guò)canvas繪制出

/**
 * 鏈接蛛網(wǎng)的線
 * @param canvas
 */
private void drawSpiderSupport(Canvas canvas) {
    for(int i=0;i<mSizeCount;i++){
        canvas.drawLine(0f,0f,(float)( mMaxR*Math.cos(mAngle*i)),(float) (mMaxR*Math.sin(mAngle*i)),mSpiderPaint);
    }
}

運(yùn)用三角函數(shù)知識(shí)確定頂點(diǎn)位置拭荤,然后使用canvas繪制中心點(diǎn)和頂點(diǎn)的連線

/**
 * 為每一個(gè)點(diǎn)增加名字說(shuō)明
 * @param canvas
 */
private void drawSpiderText(Canvas canvas){
    Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
    float fontHeight = fontMetrics.descent - fontMetrics.ascent;
    mTextPaint.setTextSize(10);
    for(int i=0;i<mSizeCount;i++){
        //計(jì)算說(shuō)明文字的坐標(biāo)
        float x = (float)( (mMaxR + fontHeight /2)*Math.cos(mAngle*i));
        float y = (float) ((mMaxR + fontHeight /2)*Math.sin(mAngle*i));
        //記錄畫布位置
        canvas.save();
        canvas.translate(x,y);
        float curAngle = mAngle * i;
        //Log.e("SpiderView","curAngle"+curAngle);
        //旋轉(zhuǎn)角度  擺正說(shuō)明文字的位置
        canvas.rotate(-curAngle);
        float dis = mTextPaint.measureText(spiders.get(i).name);//文本長(zhǎng)度

        //在第三象限    android上面的坐標(biāo)系是順時(shí)針計(jì)算角度和弧度。  數(shù)學(xué)的坐標(biāo)系是逆時(shí)針計(jì)算角度和弧度
        if(Math.PI/2f < curAngle && curAngle < Math.PI){
            canvas.drawText(spiders.get(i).name,-dis,0,mTextPaint);

        //在第二象限
        }else if(Math.PI < curAngle &&curAngle < 3f / 2f* Math.PI){
            canvas.drawText(spiders.get(i).name,-dis,0,mTextPaint);
        }else{
            canvas.drawText(spiders.get(i).name,0,0,mTextPaint);
        }
        //恢復(fù)畫布
        canvas.restore();
    }
}

這里要注意:android上面的坐標(biāo)系是順時(shí)針計(jì)算角度和弧度疫诽。 數(shù)學(xué)的坐標(biāo)系是逆時(shí)針計(jì)算角度和弧度

為了擺正文字舅世,需要將canvas(畫布)平移后在旋轉(zhuǎn)相應(yīng)的角度。并且在第二三象限的時(shí)候奇徒,文字會(huì)和雷達(dá)圖重合雏亚,所以需要增加相應(yīng)的長(zhǎng)度單位

同樣注意:canvas的平移和旋轉(zhuǎn)是影響后面操作并且連續(xù)的。所以使用了 canvas 的save和restore方法摩钙。

/**
 * 繪制數(shù)據(jù)區(qū)域
 * @param canvas
 */
private void drawSpiderData(Canvas canvas){
    //防止用戶設(shè)置的值太小  數(shù)據(jù)區(qū)域大于雷達(dá)區(qū)
    for(Spider spider:spiders){
        mMaxNumber = Math.max(spider.val,mMaxNumber);
    }
    Path path = new Path();
    //連接整個(gè)數(shù)據(jù)區(qū)域
    for(int i=0; i<mSizeCount; i++){
        float curR = spiders.get(i).val/mMaxNumber*mMaxR ;
        float x = (float) (curR*Math.cos(mAngle*i));
        float y = (float) (curR*Math.sin(mAngle*i));
        if(i==0){
            path.moveTo(curR,0);
        }else{
            path.lineTo(x,y);
        }
        //繪制小圓點(diǎn)
        canvas.drawCircle(x,y,2,mDataPaint);
    }
    path.close();
    mDataPaint.setStyle(Paint.Style.STROKE);
    canvas.drawPath(path, mDataPaint);
    mDataPaint.setAlpha(127);
    mDataPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    canvas.drawPath(path,mDataPaint);
}

待完善的地方

onDraw方法里面處理padding參數(shù)

下載查看全部代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末评凝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子腺律,更是在濱河造成了極大的恐慌奕短,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,865評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件匀钧,死亡現(xiàn)場(chǎng)離奇詭異翎碑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)之斯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門日杈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人佑刷,你說(shuō)我怎么就攤上這事莉擒。” “怎么了瘫絮?”我有些...
    開封第一講書人閱讀 169,631評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵涨冀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我麦萤,道長(zhǎng)鹿鳖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,199評(píng)論 1 300
  • 正文 為了忘掉前任壮莹,我火速辦了婚禮翅帜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘命满。我一直安慰自己涝滴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,196評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歼疮,像睡著了一般杂抽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腋妙,一...
    開封第一講書人閱讀 52,793評(píng)論 1 314
  • 那天默怨,我揣著相機(jī)與錄音,去河邊找鬼骤素。 笑死匙睹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的济竹。 我是一名探鬼主播痕檬,決...
    沈念sama閱讀 41,221評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼送浊!你這毒婦竟也來(lái)了梦谜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,174評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袭景,失蹤者是張志新(化名)和其女友劉穎唁桩,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耸棒,經(jīng)...
    沈念sama閱讀 46,699評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荒澡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,770評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了与殃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单山。...
    茶點(diǎn)故事閱讀 40,918評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖幅疼,靈堂內(nèi)的尸體忽然破棺而出米奸,到底是詐尸還是另有隱情,我是刑警寧澤爽篷,帶...
    沈念sama閱讀 36,573評(píng)論 5 351
  • 正文 年R本政府宣布悴晰,位于F島的核電站,受9級(jí)特大地震影響狼忱,放射性物質(zhì)發(fā)生泄漏膨疏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,255評(píng)論 3 336
  • 文/蒙蒙 一钻弄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧者吁,春花似錦窘俺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)灶泵。三九已至,卻和暖如春对途,著一層夾襖步出監(jiān)牢的瞬間赦邻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工实檀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留惶洲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,364評(píng)論 3 379
  • 正文 我出身青樓膳犹,卻偏偏與公主長(zhǎng)得像恬吕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子须床,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,926評(píng)論 2 361

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