Android自定義View——扇形統(tǒng)計(jì)圖的實(shí)現(xiàn)代碼
Android 扇形統(tǒng)計(jì)圖
先看看效果:
看上去如果覺得還行就繼續(xù)往下看吧!
自定義View
定義成員變量
private int mHeight, mWidth;//寬高 private Paint mPaint;//扇形的畫筆 private Paint mTextPaint;//畫文字的畫筆 private int centerX, centerY;//中心坐標(biāo) //"其他"的value //扇形圖分成太多快 所以要合并一部分為其他 即圖中灰色部分 private double rest; private int maxNum = 5;//扇形圖的最大塊數(shù) 超過的item就合并到其他 String others = "其他";//“其他”塊要顯示的文字 double total;//數(shù)據(jù)的總和 double[] datas;//數(shù)據(jù)集 String[] texts;//每個(gè)數(shù)據(jù)對應(yīng)的文字集 //顏色 默認(rèn)的顏色 private int[] mColors = { Color.parseColor("#FF4081"), Color.parseColor("#ffc0cb"), Color.parseColor("#00ff00"), Color.parseColor("#0066ff"), Color.parseColor("#ffee00") }; private int mTextSize;//文字大小 單位:像素 private int radius = 1000;//半徑 在畫圖時(shí)初始化
測量寬高
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //獲取寬高 不要設(shè)置wrap_content mHeight = MeasureSpec.getSize(heightMeasureSpec); mWidth = MeasureSpec.getSize(widthMeasureSpec); }
畫圖
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //無數(shù)據(jù) 直接返回 if (datas == null || datas.length == 0) return; centerX = (getRight() - getLeft()) / 2; centerY = (getBottom() - getTop()) / 2; int min = mHeight > mWidth ? mWidth : mHeight; if (radius > min / 2) { radius = (int) ((min - getPaddingTop() - getPaddingBottom()) / 3.5); } //畫各個(gè)扇形 drawCircle(canvas); //畫線與文字 drawLineAndText(canvas); }
畫扇形
一個(gè)圓形統(tǒng)計(jì)圖是由許多個(gè)扇形組成的,我們根據(jù)數(shù)據(jù)計(jì)算出每個(gè)扇形的角度即可。注意,畫弧度的時(shí)候,角度是順時(shí)針變大的!即與我們平時(shí)的坐標(biāo)是反過來的
//畫扇形 private void drawCircle(Canvas canvas) { int centerX =( getRight() - getLeft() )/2;//中點(diǎn) int centerY = ( getBottom() - getTop()) /2; RectF rect = new RectF((float) (centerX - radius), centerY-radius, centerX+radius,centerY+radius);//圓形區(qū)域 int start = 0;//扇形開始的角度 for (int i = 0; i < (maxNum<datas.length?maxNum:datas.length); i++) { float angles = (float) ((datas[i] * 1.0f /total) * 360);//計(jì)算扇形的角度 mPaint.setColor(mColors[i%mColors.length]);//顏色 canvas.drawArc(rect,start,angles,true,mPaint);//畫扇形 start += angles;//下一個(gè)扇形開始的角度 } //畫"其他"部分 即圖中灰色的部分 rest =0;//保存其他部分的value for(int i=maxNum;i<datas.length;i++){ rest+=datas[i]; } float angles = (float) 360 - start;//角度 mPaint.setColor(Color.GRAY); canvas.drawArc(rect,start,angles,true,mPaint); }
畫線條和文字
主要是計(jì)算各個(gè)點(diǎn)的坐標(biāo)很煩
這里我畫出一個(gè)圖 大家把代碼和圖對照理解一下
//畫線與文字 private void drawLineAndText(Canvas canvas) { int start = 0; //平移畫布到中心 所以下面的坐標(biāo)是從中點(diǎn)開始算起的 canvas.translate(centerX, centerY); mPaint.setStrokeWidth(4);//線條寬度 //如果數(shù)據(jù)集過大 那么要合并到其他 for (int i = 0; i < (maxNum < datas.length ? maxNum : datas.length); i++) { float angles = (float) ((datas[i] * 1.0f / total) * 360); //畫線條和文字 drawLine(canvas, start, angles, texts[i], mColors[i % mColors.length]); start += angles; } //畫其他部分的線條和文字 if (start < 360)//如果start小于360 說明有其他部分 drawLine(canvas, start, 360 - start, others, Color.GRAY); } private void drawLine(Canvas canvas, int start, float angles, String text, int color) { mPaint.setColor(color); float stopX, stopY; stopX = (float) ((radius + 40) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)); stopY = (float) ((radius + 40) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)); canvas.drawLine((float) ((radius - 20) * Math.cos((2 * start + angles) / 2 * Math.PI / 180)), (float) ((radius - 20) * Math.sin((2 * start + angles) / 2 * Math.PI / 180)), stopX, stopY, mPaint ); //畫橫線 int dx;//判斷橫線是畫在左邊還是右邊 int endX; if (stopX > 0) { endX = (centerX - getPaddingRight() - 20); } else { endX = (-centerX + getPaddingLeft() + 20); } //畫橫線 canvas.drawLine(stopX, stopY, endX, stopY, mPaint ); dx = (int) (endX - stopX); //測量文字大小 Rect rect = new Rect(); mTextPaint.getTextBounds(text, 0, text.length(), rect); int w = rect.width(); int h = rect.height(); int offset = 20;//文字在橫線的偏移量 //畫文字 文字的Y坐標(biāo)值的是文字底部的Y坐標(biāo) canvas.drawText(text, 0, text.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY + h, mTextPaint); //測量百分比大小 String percentage = angles / 3.60 + ""; percentage = percentage.substring(0, percentage.length() > 4 ? 4 : percentage.length()) + "%"; mTextPaint.getTextBounds(percentage, 0, percentage.length(), rect); w = rect.width() - 10; //畫百分比 canvas.drawText(percentage, 0, percentage.length(), dx > 0 ? stopX + offset : stopX - w - offset, stopY - 5, mTextPaint); }
這樣我們就已經(jīng)完成了繪制的工作了,接下來就是綁定數(shù)據(jù)啦!
大家只要把datas和texts填充好數(shù)據(jù)就可以啦,最好調(diào)用一下invalidate()方法請求重繪(如果已經(jīng)繪制了)。
我使用了一個(gè)內(nèi)部抽象類來實(shí)現(xiàn)數(shù)據(jù)綁定的:
public abstract class ArcViewAdapter<T> { public void setData(List<T> list) { datas = new double[list.size()]; texts = new String[list.size()]; for (int i = 0; i < list.size(); i++) { total += getValue(list.get(i)); datas[i] = getValue(list.get(i)); texts[i] = getText(list.get(i)); } invalidate();//請求重繪 } //通過傳來的數(shù)據(jù)集的某個(gè)元素 得到具體的數(shù)字 public abstract double getValue(T t); //通過傳來的數(shù)據(jù)集的某個(gè)元素 得到具體的描述 public abstract String getText(T t); }
在布局文件里面引用這個(gè)ArcView,然后在MainActivity中綁定數(shù)據(jù):
ArcView arcView = (ArcView) findViewById(R.id.arc); List<Times> times = new ArrayList<>(); for (int i = 6; i > 0; i--) { Times t = new Times();//這個(gè)類就只有兩個(gè)變量 t.hour = i; t.text = "Number"+i; times.add(t); } //初始化適配器 ArcView.ArcViewAdapter myAdapter = arcView.new ArcViewAdapter<Times>(){ @Override public double getValue(Times times) { return times.hour; } @Override public String getText(Times times) { return times.text; } }; myAdapter.setData(times);//綁定數(shù)據(jù)
大家可以設(shè)置各種setter以便在Activity中調(diào)用,來提高ArcView的靈活性,比如設(shè)置顏色、半徑、最大塊數(shù)等等。
demo下載地址:SectorDiagram_jb51.rar
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android自定義環(huán)形統(tǒng)計(jì)圖動(dòng)畫
- Android自定義條形對比統(tǒng)計(jì)圖
- Android自定義View實(shí)現(xiàn)多邊形統(tǒng)計(jì)圖示例代碼
- Android編程實(shí)現(xiàn)canvas繪制餅狀統(tǒng)計(jì)圖功能示例【自動(dòng)適應(yīng)條目數(shù)量與大小】
- Android編程實(shí)現(xiàn)canvas繪制柱狀統(tǒng)計(jì)圖功能【自動(dòng)計(jì)算寬高及分度值、可左右滑動(dòng)】
- Android 實(shí)現(xiàn)會(huì)旋轉(zhuǎn)的餅狀統(tǒng)計(jì)圖實(shí)例代碼
- Android自定義控件橫向柱狀統(tǒng)計(jì)圖
相關(guān)文章
Android實(shí)現(xiàn)短信驗(yàn)證碼自動(dòng)填寫功能
這篇文章主要介紹了Android實(shí)現(xiàn)短信驗(yàn)證碼自動(dòng)填寫功能,感興趣的小伙伴們可以參考一下2015-12-12Android App在線程中創(chuàng)建handler的方法講解
這篇文章主要介紹了Android App在線程中創(chuàng)建handler的方法講解,文中同時(shí)講解了handler和線程的關(guān)系以及使用Handler時(shí)一些需要注意的地方,需要的朋友可以參考下2016-03-03Flutter實(shí)現(xiàn)簡單的下載按鈕動(dòng)畫
我們在app的開發(fā)過程中經(jīng)常會(huì)用到一些表示進(jìn)度類的動(dòng)畫效果,比如一個(gè)下載按鈕,那么在flutter中一個(gè)下載按鈕的動(dòng)畫應(yīng)該如何制作呢,一起來看看吧2023-05-05Android衛(wèi)星菜單效果的實(shí)現(xiàn)方法
這篇文章主要介紹了Android衛(wèi)星菜單效果的實(shí)現(xiàn)方法,需要的朋友可以參考下2017-05-05Flutter自定義下拉刷新時(shí)的loading樣式的方法詳解
Flutter中的下拉刷新,我們通常RefreshIndicator,可以通過color或strokeWidth設(shè)置下拉刷新的顏色粗細(xì)等樣式,但如果要自定義自己的widget,RefreshIndicator并沒有暴露出對應(yīng)的屬性,那如何修改呢,文中給大家介紹的非常詳細(xì),需要的朋友可以參考下2024-01-01android 開發(fā)教程之日歷項(xiàng)目實(shí)踐(三)
決定開始學(xué)習(xí) Android 平臺(tái)下的軟件開發(fā),以日歷作為實(shí)踐項(xiàng)目,進(jìn)行一周后,基本完成,有需要的朋友可以參考下2013-01-01Android列表組件ListView使用詳解之動(dòng)態(tài)加載或修改列表數(shù)據(jù)
今天小編就為大家分享一篇關(guān)于Android列表組件ListView使用詳解之動(dòng)態(tài)加載或修改列表數(shù)據(jù),小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03