Android?MPAndroidChart繪制原理
前言
官方demo地址:github.com/PhilJay/MPA…
筆者接下來(lái)的文章里MPChart 代表的就是 MPAndroidChart。
下載后AS里運(yùn)行,可以看到demo里面有 Line Charts, Bar Charts, Pie Charts, Radar Charts, Other Charts.
Demo 本身的內(nèi)容比較詳細(xì),簡(jiǎn)單的圖表繪制直接可以拿來(lái)代碼使用,不做過(guò)多介紹。本文及本系列專(zhuān)題著重剖析MPChart的繪制原理及流程,以及部分自定義的屬性、圖表等內(nèi)容。 例如在生產(chǎn)環(huán)境中通常會(huì)有一些設(shè)計(jì)或者產(chǎn)品上的需求,原本的MPChart無(wú)法滿(mǎn)足時(shí),就需要自定義一些局部的屬性,更甚者會(huì)添加一些型的圖表樣式,比如修改 X軸,Y軸(圖表局部屬性), 添加 睡眠泳道圖,步頻的散點(diǎn)圖等,專(zhuān)題后續(xù)會(huì)涉及。
1. Chart整體結(jié)構(gòu)
Chart下面包含有坐標(biāo)系的BarLineChartBase,以及沒(méi)有坐標(biāo)系的PieRandarBase.

圖 1.0(Chart 整體結(jié)構(gòu))
筆者通常在工程中使用較多的是Bar、Line兩種圖表, 除此之外,還有很多其它的圖表類(lèi)型(見(jiàn)示例圖1.1)

圖 1.1(坐標(biāo)系Chart)
2.Chart 繪制參與的業(yè)務(wù)組件
本節(jié)從 具有坐標(biāo)系的LineBarChartBase入手逐步拆解MPChart,查看Chart內(nèi)部具體的繪制邏輯。
無(wú)論一個(gè)圖表怎么復(fù)雜,依舊沒(méi)有逃離View的整個(gè)繪制邏輯, 廣義上來(lái)說(shuō)自定義View 分 自定義View,自定義ViewGroup, ViewGroup 涉及到parent 跟child , 各child 之間的擺放關(guān)系,需要處理layout相關(guān)的問(wèn)題。而這里的MPChart圖表,絕大多數(shù)涉及到位置的擺放通過(guò) 屏幕像素坐標(biāo)點(diǎn)去定位,然后依照坐標(biāo)點(diǎn)去繪制,所以我們著重需要關(guān)注的是Chart (View) 的 onDraw()方法,這里是BarLineChartBase的onDraw() 方法,可以看到所有的繪制邏輯都在該方法內(nèi)。

圖 1.3(chart的onDraw方法)
Render
可以從上面的圖1.3中可以看到,Chart的繪制分小組件逐個(gè)繪制的,每個(gè)組件定義自己的Render,在對(duì)應(yīng)的drawXXX 方法里進(jìn)行繪制,比如有專(zhuān)門(mén)繪制X軸的XAxisRender, 繪制Y軸的 YAxisRender, 繪制BarChart的BarChartRender, 以及繪制 Line chart的 LineChartRender, 繪制邊框 backGround等等,所以剖開(kāi)整個(gè)Chart的繪制邏輯來(lái)看,我們會(huì)發(fā)現(xiàn)Chart的繪制就是通過(guò)各種Render去 drawLine、drawRect 、不規(guī)則的drawPath, 或者貝塞爾曲線(xiàn)drawCubicPath(其實(shí)也是屬于drawPath的范疇);以及部分輔助,坐標(biāo)軸的label 所需的drawText, 這些各種的小部件的繪制最終完成了 Chart整個(gè)的繪制。
可以參考源碼 : BarChartRender 里的 drawData() , drawDataSet(DataSet dataSet,...) 方法。
圖1.5(Chart 圖表 柱子繪制)
那么問(wèn)題來(lái)了,上邊介紹的Render的各種繪制,這些小部件背后對(duì)應(yīng)的Pixel Point,比如簡(jiǎn)單的BarChart 中的某一個(gè) Item Chart,直接對(duì)應(yīng)一個(gè)RectF (start, top, end, bottom), RectF包含四個(gè)坐標(biāo)點(diǎn); 又比如 簡(jiǎn)單的LineChart,一條折線(xiàn)包含兩個(gè)端點(diǎn),PointA(x1, y1), PointB(x2, y2), 多段折線(xiàn)累加就構(gòu)成了我們所需的線(xiàn)性表, 它們是如何得來(lái)的?
在此, 我們以一個(gè)具體的實(shí)例著手來(lái)分析,比如繪制某一天的步數(shù)BarChart, 要求每半個(gè)小時(shí)一根柱子,所以一共48根柱子,每個(gè)柱子的高度對(duì)應(yīng)的該時(shí)間段的步數(shù)sum,這些我們稱(chēng)之為業(yè)務(wù)數(shù)據(jù),如何將這里的業(yè)務(wù)數(shù)據(jù)轉(zhuǎn)化成Render 繪制 最終所需的Pixel Point呢?

圖1.6(24小時(shí)步行圖)
Buffer
從圖1.5的源碼里 可以看到Render里 canvas drawRect的數(shù)據(jù)來(lái)自 BarBuffer的數(shù)據(jù)結(jié)構(gòu),首先我們會(huì)將業(yè)務(wù)數(shù)據(jù)放置到一個(gè)Buffer 數(shù)組里,然后通過(guò)一個(gè)工具Transform, 將buffer里的數(shù)據(jù)轉(zhuǎn)化成 pixel point, 繼續(xù)保存在Buffer里, 然后繪制流程中從buffer 里獲取數(shù)據(jù)進(jìn)行繪制。這個(gè)transform的流程,可以拿個(gè)專(zhuān)題來(lái)細(xì)講,自定義一種圖表樣式時(shí)繪制的時(shí)候,筆者通常也是修改buffer 里的相應(yīng)的值,更甚者是自定義自己的一個(gè)Buffer來(lái)專(zhuān)門(mén)處理數(shù)據(jù)的轉(zhuǎn)換關(guān)系,影響這個(gè)transform的因素 1.數(shù)據(jù)源 BarBuffer 2.坐標(biāo)系YAxis、XAxis 具體來(lái)說(shuō) Axis的min/max 值 3、觸摸時(shí)的縮放比例參數(shù) phaseY. 4、Attribute屬性值(比如)
Entry、DataSet
再回到步數(shù)BarChart的繪制,我們知道了如何將業(yè)務(wù)數(shù)據(jù)轉(zhuǎn)化成pixel Point,現(xiàn)在48根柱子具體柱子坐標(biāo)x,每根柱子對(duì)應(yīng)的業(yè)務(wù)值設(shè)為y, 才兩個(gè)數(shù)據(jù),如何對(duì)應(yīng)成RectF所需的 4個(gè) Point值呢,整個(gè)chart/48 就是每個(gè)item的相應(yīng)的坐標(biāo)范圍,但考慮item是緊挨的,真正的柱子Rectf 需要預(yù)留的Space,這個(gè)space的信息會(huì)定義在 Chart 的Attribute 屬性里,這里可以理解成自定義View 的自定義Arrtibute 值。
如何把這些業(yè)務(wù)的數(shù)據(jù)比較優(yōu)雅、合適的方式給到Buffer?首先會(huì)將業(yè)務(wù)數(shù)據(jù)封裝成Entry, 它對(duì)應(yīng)的是每個(gè)item, 基本數(shù)據(jù)包含 x, y 例如Entry的子類(lèi) BarEntry 用x、y 在配合Attribute里的space (每個(gè)Item里空白跟整個(gè)Item寬度的占比,通常給小于1的float型), 將分散的Entry封裝成整個(gè)的DataSet, 統(tǒng)一將這個(gè)DataSet交給 DataBuffer, 結(jié)合Attribute中的space屬性, 給到 Transformer, 最終transform 出 BarChart 所需要的RectF。
Attribute
對(duì)于一些裝飾性的屬性,比如一些設(shè)計(jì)需求的顏色、大小、尺寸、以及上面提到的space等控制每個(gè)組件具體繪制成啥樣的,可以通過(guò)Attribute給到Render,至于每個(gè)item 具體的需求變化,比如不同的值范圍,柱子的顏色要求不一樣,可以通過(guò)擴(kuò)展Entry的屬性,進(jìn)行具體的繪制邏輯, Entry 除了可以跟坐標(biāo)轉(zhuǎn)化相關(guān)的x、y 的信息之外,可以包含其它繪制的附屬信息。
3. 整體Chart繪制流程
通過(guò)上面的示例,參見(jiàn)圖 進(jìn)行梳理一下:首先第一步 獲取業(yè)務(wù)數(shù)據(jù)(對(duì)應(yīng)坐標(biāo)軸數(shù)據(jù)), 創(chuàng)建Entry保存每個(gè)Item值,將這些值保存統(tǒng)一的DataSet, 然后交于Buffer存儲(chǔ),TransFormer 拿到 buffer,最終轉(zhuǎn)化為Render繪制所需的 Pixel Point, 通常會(huì)是Point構(gòu)成的具象的RectF, Line, Path等。

圖1.7(Chart繪制流程圖)
到此,整個(gè)Chart 的繪制主體流程基本介紹完了,按照上面的各個(gè)組件負(fù)責(zé)的功能,完全可以自己搭建一套簡(jiǎn)易的圖表繪制的庫(kù)來(lái)。
除了上面介紹的chart 主體的繪制,通常還會(huì)有 例如: XAxis、YAxis,以及外邊框等輔助內(nèi)容的繪制,如何分配給他們繪制的空間呢?很簡(jiǎn)單可以將Chart設(shè)置 padding,在padding 里繪制 XAxis、YAxis等,這些contentPadding 設(shè)置ViewPortHandler,ViewPortHandler同時(shí)包含一些邊界的判斷,后續(xù)會(huì)花時(shí)間介紹自定義XAxis、YAxis的Render過(guò)程。
個(gè)別特殊需求比如極值處,需要繪制MaxPoup, MinPoup等,以及 AverageLine 等 圖表的輔助性的需求。
除此之外還有交互里的一個(gè)繪制,比如按下,HighLight 需要繪制響應(yīng)的poupwindow, 包含一些Item值的提示信息。
對(duì)于部分特殊的圖表比如配速圖表,它的Y軸的值是reversed, 需要進(jìn)行特殊的處理Y值的轉(zhuǎn)化關(guān)系,除了圖表繪制,更多的是數(shù)學(xué)計(jì)算的問(wèn)題,可以后續(xù)單獨(dú)來(lái)講。
后續(xù)的內(nèi)容安排:
- 自定義 XAxis、YAxis
- MaxPoup, MinPoup,AverageLine 等相關(guān)的繪制
- 自定義revert 圖表 例如配速表的繪制
- CubicPath、LineChart 等底部的drawFill 等內(nèi)容,游泳的例如Swolf 的梯度圖表的繪制。
- 步頻散點(diǎn)圖的繪制
- 睡眠泳道圖、SleepBuffer的自定義,轉(zhuǎn)換等邏輯
- SegmentBarChart圖的繪制,一個(gè)Item里面多個(gè)RectF, Barbuffer與之對(duì)應(yīng),Transform轉(zhuǎn)化
- MPChart 整體的例如RTL(部分阿拉伯國(guó)家需要)
到此這篇關(guān)于Android MPAndroidChart繪制原理的文章就介紹到這了,更多相關(guān)Android MPAndroidChart內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android convinientbanner頂部廣告輪播控件使用詳解
這篇文章主要為大家詳細(xì)介紹了Android convinientbanner頂部廣告輪播控件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android Studio Gradle依賴(lài)沖突解決方法
這篇文章主要給大家介紹了關(guān)于Android Studio Gradle依賴(lài)沖突解決的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Android Studio具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
android開(kāi)發(fā)教程之卸載sd卡對(duì)MediaServer的處理
Android中如果MediaServer訪(fǎng)問(wèn)SD卡上的音頻文件,卸載SD卡的時(shí)候,就會(huì)kill掉MediaServer,卸載SD卡上必要條件就是沒(méi)有進(jìn)程訪(fǎng)問(wèn)SD卡上的資源文件。Kill掉MediaServer的進(jìn)程后,MediaServer會(huì)重新啟動(dòng)。2014-02-02
Android巧用ActionBar實(shí)現(xiàn)tab導(dǎo)航效果
這篇文章主要為大家詳細(xì)介紹了Android巧用ActionBar實(shí)現(xiàn)tab導(dǎo)航效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05
Android測(cè)量每秒幀數(shù)Frames Per Second (FPS)的方法
這篇文章主要介紹了Android測(cè)量每秒幀數(shù)Frames Per Second (FPS)的方法,涉及Android針對(duì)多媒體文件屬性操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
Material Design系列之Behavior實(shí)現(xiàn)Android知乎首頁(yè)
這篇文章主要為大家詳細(xì)介紹了Material Design系列之Behavior實(shí)現(xiàn)Android知乎首頁(yè)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
解決genymotion模擬器無(wú)法聯(lián)網(wǎng)的正確方法100%成功
android 5.1版不能聯(lián)網(wǎng),三個(gè)步驟的設(shè)置就可以解決你的genymotion模擬器無(wú)法聯(lián)網(wǎng)的問(wèn)題2018-03-03
android判斷phonegap是否聯(lián)網(wǎng)且加載super.loadUrl網(wǎng)址
android判斷phonegap是否聯(lián)網(wǎng)動(dòng)態(tài)加載super.loadUrl網(wǎng)址,接下來(lái)本文所提供的知識(shí)會(huì)幫助你解決以上問(wèn)題,感興趣的你可不要錯(cuò)過(guò)了哈2013-02-02

