MPAndroidChart繪制自定義運(yùn)動(dòng)數(shù)據(jù)圖表示例詳解
引言
聲明:文中的MPChart代指MPAndroidChart.
本系列之前的文章介紹的MPChart中BarChart相關(guān)的一些繪制,接下來(lái)我們看看LineChart相關(guān)的繪制。
這里以實(shí)際的運(yùn)動(dòng)相關(guān)的圖表數(shù)據(jù)做業(yè)務(wù)支撐來(lái)講解。MPChart圖表支持多指觸控方法,這里所有的圖表自定義都關(guān)掉了這個(gè)屬性,這樣就減少Transformer,以及繪制過(guò)程中的更多的變動(dòng),相當(dāng)于一個(gè)靜態(tài)的圖。
通常圖表在放大的過(guò)程中,坐標(biāo)軸也會(huì)隨之展現(xiàn)更小的刻度,復(fù)雜度就變高了,具體的顯示的刻度就可能出現(xiàn)小數(shù)之類的等情況。
這里我們關(guān)掉觸摸放大后,相當(dāng)于一個(gè)靜態(tài)的圖。這時(shí)候,產(chǎn)品以及設(shè)計(jì)可能需要我們的X軸坐標(biāo)、Y軸坐標(biāo)等刻度盡可能地為整數(shù),這樣看起來(lái)比較美觀。
靜態(tài)的情況下,因?yàn)闆]法移動(dòng),即便可以移動(dòng),首次展現(xiàn)也希望圖表的數(shù)據(jù)能夠比較居中,這就涉及到Y(jié)Axis的自定義如何影響控制 Chart的相關(guān)展示邏輯的內(nèi)容,本章節(jié)首先從這入手,講解運(yùn)動(dòng)數(shù)據(jù)圖表的繪制。
TimeAxis
這里自定義X軸TimeAxis,實(shí)際意義是一次運(yùn)動(dòng)耗費(fèi)的時(shí)間,繼承自XAxis。假如一次運(yùn)動(dòng)1個(gè)小時(shí)06分鐘,設(shè)計(jì)希望展示4個(gè)刻度(0, 20 , 40, 60 分鐘), 這時(shí)需要我們自己去控制,假如不加控制的話,Default情況下沒法準(zhǔn)確的實(shí)現(xiàn)設(shè)計(jì)的需求。
XAxis、YAxis有兩個(gè)屬性,Maximum, Minmum. 這樣設(shè)定每個(gè)interval 就可以計(jì)算出要顯示的刻度列表, labelList, 加入到XAxis中的 mEntries, 最后在XAxisRender會(huì)拿到mEntries 最終繪制 X坐標(biāo)。
這里不在設(shè)計(jì)一個(gè)算法類計(jì)算每個(gè)時(shí)間段的刻度顯示了,運(yùn)動(dòng)的時(shí)間范圍有限,直接枚舉, 在 TimeXAxis里的getlabelCount() 實(shí)現(xiàn)。
if (max > 6000 * TimeDateUtil.TIME_MIN_INT){ interval = 2000 * TimeDateUtil.TIME_MIN_INT; } else if (max > 4800 * TimeDateUtil.TIME_MIN_INT) {// 80個(gè)小時(shí) interval = 1920 * TimeDateUtil.TIME_MIN_INT; } else if (max > 2400 * TimeDateUtil.TIME_MIN_INT) {// 40個(gè)小時(shí) interval = 960 * TimeDateUtil.TIME_MIN_INT; } else if (max > 1200 * TimeDateUtil.TIME_MIN_INT) { interval = 480 * TimeDateUtil.TIME_MIN_INT; } 。。。。。 else if (max > 20 * TimeDateUtil.TIME_MIN_INT) { interval = 5 * TimeDateUtil.TIME_MIN_INT; } else if (max > 15 * TimeDateUtil.TIME_MIN_INT) { interval = 4 * TimeDateUtil.TIME_MIN_INT;//4分鐘刻度 } else if (max > 5 * TimeDateUtil.TIME_MIN_INT) {// interval = 2 * TimeDateUtil.TIME_MIN_INT;//2分鐘刻度。 } else { interval = TimeDateUtil.TIME_MIN_INT; } float currentEntry = min; List<Float> entryList = new ArrayList<>(); do { entryList.add(currentEntry); currentEntry += interval; } while (currentEntry <= max); labelCount = entryList.size(); mEntryCount = labelCount; if (mEntries.length < labelCount) { mEntries = new float[labelCount]; } for (int i = 0; i < labelCount; i++) { mEntries[i] = entryList.get(i); }
準(zhǔn)備好XAxis 中的Entry數(shù)據(jù)后,依舊是交給Buffer,經(jīng)過(guò)Transformer轉(zhuǎn)化,最終繪制出來(lái), 自定義TimeAxisRender, 然后 renderAxisLabels() 方法里drawLabel(), 繪制XAxis的坐標(biāo)軸線:
圖1.0 XAxis 坐標(biāo)線的繪制
SportYAxis
Y軸的繪制相比XAxis要復(fù)雜一些,自定義的SportYAxis繼承自YAxis, TimeXAxis 只有時(shí)間數(shù)據(jù)對(duì)應(yīng)。SportYAxis根據(jù)具體的數(shù)據(jù)業(yè)務(wù)可以表示 心率, 高度海拔, 速度, 配速,頻率等數(shù)據(jù)。
這些數(shù)據(jù)中, 心率、步頻等取值范圍可以比如(0, 250)類似這樣的可以直接定下來(lái)Y軸的Max, Min 值以及對(duì)應(yīng)的刻度,高度海拔有負(fù)數(shù)的,速度的Max根據(jù) 所給數(shù)據(jù)來(lái)定, 配速比較特殊,需要將Y軸 revert。
為了將圖表能夠居中,通常YAxis 上的Maximum 會(huì)比 數(shù)據(jù)中的極大值要偏大,保證圖表不會(huì)呈現(xiàn)的太慢,影響美觀。根據(jù)不同的Sport數(shù)據(jù),將Y軸分為以下幾種:
// TYPE_FIX_MIN_ZERO = 0; Y軸從固定的0開始 到 max;步頻、起跳高度 // TYPE_FIX_MIN_POSITIVE = 1; 從 entryList的 真實(shí)的 min(min不能小于0)開始,到max; 心率、速度、劃頻、Swolf // TYPE_FIX_COMMON = 2; 從entryList的最小值min開始到max的最大值,無(wú)論最大、最小是否為Positive, 例如海拔; //TYPE_FIX_RESTRICT_MAX = 3; 限制最大值,比如配速。Y軸 Invert,所以最小值min為大于等于 0 的Positive value; 配速 public static final int TYPE_FIX_MIN_ZERO = 0; public static final int TYPE_FIX_MIN_POSITIVE = 1; public static final int TYPE_FIX_COMMON = 2; public static final int TYPE_FIX_RESTRICT_MAX = 3;
每種類型下計(jì)算Y軸的Maximum、Minmum; 然后計(jì)算刻度的間距 itemValue, 得到 坐標(biāo)Label 的List。
以上的幾種坐標(biāo)的實(shí)現(xiàn)具體在 SportYAxis 中實(shí)現(xiàn)。
將Y軸數(shù)據(jù)限定下來(lái)之后,圖表的展現(xiàn)因?yàn)閅軸的Maximum、Minmum 限定在比較居中的位置。
對(duì)于配速,當(dāng)運(yùn)動(dòng)停下來(lái)時(shí),單位距離的耗時(shí)可能無(wú)限大,假如我們考慮把這個(gè)極值畫下來(lái)的話,Y軸可能跨度很大導(dǎo)致圖表沒法看,所以需要限定極大值,截?cái)鄨D形:
//限制最大值 private float getYAxisMax2(List<SportRecordEntry> values, float yAxisMin) { int size = values.size(); float yAxisMax = Integer.MIN_VALUE; float yAxisMinTemp = Integer.MAX_VALUE; float sum = 0; for (int i = 0; i < size; i++) { SportRecordEntry entry = values.get(i); yAxisMax = Math.max(entry.getY(), yAxisMax); yAxisMinTemp = Math.min(entry.getY(), yAxisMinTemp); sum += entry.getY(); } float averageY = sum / (size * 1.0f); float distanceMin = averageY - yAxisMinTemp; float distanceMax = yAxisMax - averageY; int num = (int) (distanceMax / distanceMin); if (num > 5) {// 配速中 有 配速值很慢的點(diǎn),坐標(biāo)時(shí)不考慮它們了。 yAxisMax = averageY + 2 * distanceMin; // 限制Y 軸坐標(biāo)。 } float distance = yAxisMax - yAxisMin; if (yAxisMax > 0 && distance <= 2) { return yAxisMax + 2; } return yAxisMax + distance * mLineChartAttr.maxYAxisRatio; }
CustomLineChart
處理完XAxis、YAxis的數(shù)據(jù)及繪制后,處理LineChart的主體,這里包含了折線圖、曲線圖等體現(xiàn)數(shù)據(jù)展現(xiàn)的,還有drawFill, 底部的填充;drawMaxMinPop() 極值點(diǎn)的繪制等。
著重講解折線圖的繪制,對(duì)于LineChart,Entry比較簡(jiǎn)單,存有對(duì)應(yīng)的X, Y值,
圖1.1CustomLineChart 繪制邏輯
考慮先后兩個(gè)點(diǎn),PreEntry, CurrentEntry 然后 繪制每段折線,最終連成圖表。
針對(duì) 配速圖表Y軸需要倒過(guò)來(lái)的,
private float getYAsInverted(Entry entry) { final float phaseY = mAnimator.getPhaseY(); float yValueRange = mYAxis.getAxisMaximum() - mYAxis.getAxisMinimum(); if (mYAxis.isInverted()) { if (entry.getY() <= mYAxis.getAxisMinimum()) { return entry.getY() * phas eY; } else { return (yValueRange - entry.getY()) * phaseY; } } else { return entry.getY() * phaseY; } }
以下就是配速圖表,極大值的限定線取的太大,導(dǎo)致整個(gè)圖形太偏上了,可以做響應(yīng)的修改。
圖1.2配速圖表線形圖
繪制底部的Fill, 將所有的點(diǎn)連線,再連接到底部的X點(diǎn)坐標(biāo),最后形成閉環(huán)的Path,
圖1.3 drawFill
以上大致就是線性圖表的繪制邏輯,考慮X軸、Y軸的Label的設(shè)定,繪制,Y軸的極值設(shè)定來(lái)控制圖表圖形呈現(xiàn)的位置;配速表的這種Y軸圖形的invert, 底部的drawFill().
自此大體的自定義繪制講解完了。后續(xù)會(huì)做些補(bǔ)充,步頻散點(diǎn)圖,極值的繪制,RTL相關(guān)等。
以上就是MPAndroidChart 自定義運(yùn)動(dòng)數(shù)據(jù)圖表示例詳解的詳細(xì)內(nèi)容,更多關(guān)于MPAndroidChart 運(yùn)動(dòng)數(shù)據(jù)圖表的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android使用AudioRecord實(shí)現(xiàn)暫停錄音功能實(shí)例代碼
本篇文章主要介紹了Android使用AudioRecord實(shí)現(xiàn)暫停錄音功能實(shí)例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-06-06android studio3.3.1代碼提示忽略大小寫的設(shè)置
這篇文章主要介紹了android studio3.3.1代碼提示忽略大小寫的設(shè)置,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android?Activity共享元素動(dòng)畫示例解析
這篇文章主要為大家介紹了Android?Activity共享元素動(dòng)畫示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09使用android-apktool來(lái)逆向(反編譯)APK包方法介紹
這篇文章主要介紹了使用android-apktool來(lái)逆向(反編譯)APK包方法介紹,本文講解了版本問(wèn)題、使用apktool、反編譯decode、rebuild重打包等內(nèi)容,需要的朋友可以參考下2015-04-04Android編輯框EditText與焦點(diǎn)變更監(jiān)視器及文本變化監(jiān)視器實(shí)現(xiàn)流程詳解
這篇文章主要介紹了Android編輯框EditText與焦點(diǎn)變更監(jiān)視器及文本變化監(jiān)視器實(shí)現(xiàn)流程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-09-09Android即時(shí)通訊設(shè)計(jì)(騰訊IM接入和WebSocket接入)
本文主要介紹了Android即時(shí)通訊設(shè)計(jì)(騰訊IM接入和WebSocket接入),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04