雷達(dá)圖,城里人管她叫戴布拉圖
某殺的個(gè)人信息那里有個(gè)芹缔。一看就是穩(wěn)如狗
某度的個(gè)人圖譜
一看就是不熱愛生活,不關(guān)心政治瓶盛,沒(méi)有文藝細(xì)胞最欠,不愛學(xué)習(xí),偶爾八卦的技術(shù)宅蓬网。
用andorid的自定義控件實(shí)現(xiàn)以下窒所。最終做出的效果是這樣的
準(zhǔn)備
為了重用性,有一些參數(shù)需要用戶可以自定義帆锋。比如:數(shù)據(jù),雷達(dá)圖半徑禽额,半徑長(zhǎng)度和實(shí)際值的比例锯厢,雷達(dá)圖的層數(shù),角的數(shù)量(和數(shù)據(jù)相關(guān))
根據(jù)(1)的數(shù)據(jù)脯倒,運(yùn)用簡(jiǎn)單三角函數(shù)知識(shí)实辑,繪制出基本雷達(dá)圖。
同樣運(yùn)用三角函數(shù)知識(shí)藻丢,繪制出數(shù)據(jù)區(qū)域
注意繪制過(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ù)