日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

MPAndroidChart自定義圖表Chart的Attribute及Render繪制邏輯_Android

作者:cxy107750 ? 更新時間: 2023-01-07 編程語言

MPAndroidChart自定義圖表

聲明:本文MPChart 代表的就是 MPAndroidChart。

1. 自定義Chart的Attribute

我們回憶一下自定義View的過程里,通常我們會將一些屬性控制Attribute通過自定義View的構造方法傳入,然后繪制或者layout的情況下使用這些屬性。Attribute類中的屬性,通過讀取attr.xml 中自定義的 View的 declare-styleable,在xml layoutInflate 時加載賦值給Attribute類。而MPChart的圖表同樣也是View,通過傳入Attribute屬性進行自定義相關的繪制,工欲善其事必先利其器。

例如,我們自定義一個CustomChart 繼承自BarChart,傳入CustomBarChartAttr 包含自定義的一些屬性控制變量的集合類, 類似圖1.0將一些公共的屬性放入BaseChartAttrs里。

圖1.0 自定義屬性Attribute

通過屬性控制,可以將我們自定義的一些差異性的數據,需要用戶設定的比如顏色,大小、圓角的半徑等暴露出來;對于共性的屬性比如坐標相關的,四個坐標,只顯示Bottom的XAxis,跟右邊的YAxis, 就可以直接在自定義的BarChart中寫死,工程需求不雷同于MPChart這種庫的提供者,他們需要考慮的是各色用戶的各種需求,所以需要考慮到API的全面性,而我們需要做的是盡量抽取共性到一個公共的類里,然后需求里的可變因素,減少出錯的幾率,將差異性的東西通過這里比如Attribute去設定,或者后續還會提到在Entry里包含屬性,Entry能夠具體到Chart里某一個item的屬性,而Attribute 一般只能控制到Chart, 或者這些Item的共有屬性上。

2. Render 自定義繪制邏輯

上篇文章里我們介紹了Chart的繪制,分別由負責各個小部件的Render去繪制,當自定義的需求只是需改局部比如圖表的主體的時候,而XAxis、YAxis、邊框等的繪制不用改變,則只要替換負責主體繪制的Render對象,例如MPChart原本庫里的Chart里有一個負責繪制圖表的主體的DataRenderer mRenderer; 這里只需替換該Render對象,將自定義的繪制邏輯加在自定義的Render里。

@Override
protected void init() {
  super.init();
  mRenderer = new CustomBarChartRenderer(this, mAnimator, mViewPortHandler);
}

這里自定義的是BarChart類型,因此CustomBarChartRenderer 繼承自BarChartRenderer, 這樣可以免去很多BarChart中原本的繪制,而只需關注需求所要的改動。

當拿到Renderer之后,我們可以隨心所欲地去繪制各種需求,比如如下繪制帶小紅花的7天的柱狀圖:

圖1.1 flowerBarChart

從上篇文章里我們了解到BarChart的繪制會在drawDataSet()方法里,涉及到繪制矩形位置的信息存儲在BarBuffer里,然后通過Transformer轉為RectF的坐標點,這里有7個矩形,所以Buffer里存儲有 4 * 7 = 28個值的數組,每次取四個值組成RectF, 然后就在RectF里繪制背景,繪制Bitmap、繪制各色各樣的需求都Okay。

圖1.2 FlowerBarChart 的繪制。

3. DataBuffer

接下來我們看一個實際的生產環境里的一種圖表的繪制,涉及到需要處理 Buffer 數據的存儲轉化。我們先看IPhone的 蘋果健康的一個數據圖表:

圖1.3 蘋果健康心率圖表

MPChart內目測是沒有這種類型圖表的,那么拿到這個我們該如何做呢?首先分析圖形中每個Item可能會有若干個小圓角矩形組成,如示例圖中綠色框里有三個小矩形組成,原來的BarBuffer數組4個值對應一個RectF在這里不在適用了,筆者這里提供的一種解決方案如下:每個Entry還是對應一個Item,這里自定義了一種SegmentEntry繼承自BarEntry,SegmentEntry 里包含了若干矩形繪制對應的數據model, SegmentRectModel, 因為同屬于一個Item,這些同屬于Item的小矩形都有相同的XAxis坐標屬性,所以SegmentRectModel 里只需保存YAxis對應所需的信息。

public class SegmentRectModel {
    public float topValue;
    public float bottomValue;
    public int rectColor = -1;
    public int boardColor = -1;
    public int boardWidth = 0;//dp
}
open class SegmentBarEntry : RecyclerBarEntry {
    @JvmField
    var rectValueModelList: MutableList<SegmentRectModel>? = null
    private var segmentRange  = 0 // 每種業務數據的segmentRange不一樣,心率、血氧、血壓等。
    var maxValue = 0
    var minValue = 0
}

SegmentBarEntry內部存有 List 小矩形對應的業務數據。

這里簡單介紹一下如何生成這一些列的小區間SegmentRectModel[bottom, top] , 以心率值為例。比如這一天下來穿戴設備產生了一下的心率值:

【45, 46, 48, 49, 52, 67, 69, 70, 75, 78, 90, 93, 94, 97, 99, 103, 158, 164, 169, 170】

這里引入一個參數用來將這些散點數據歸并成 數區間,Segment_range。可能不同種類的數據,這個值不同,比如血氧 segment_range_spo 跟心率的 segment_range_hrm 取值不一樣,假如這里的segment_range_hrm取值5,那么上面的心率值計算后得到的區間列表為:

【[45, 52], [67,78], [90, 103], [158], [164,170]】這里有個單點值,可以寫成[158, 158], 由此對應成SegmentRectModel的 bottom, top 值, 單點在小矩形中退化成為一個小圓點。

這些SegmentEntry 最總已集合的形式存入到DataSet中,然后計算生成Buffer數據,這里的Buffer計算方式改變,所以專程定義一個類用來包含這種SegmentEntry的數據轉化邏輯。

//將DataSet中的SegmentEntry 的List<SegmentRectModel>的size累加給到Buffer
private int computeBufferSize(IBarDataSet dataSet){
        int rectListSizeSum = 0;
        int entryCount = dataSet.getEntryCount();
        for (int i = 0; i < entryCount; i++) {
            BarEntry entry = dataSet.getEntryForIndex(i);
            if (entry instanceof SegmentBarEntry) {
                SegmentBarEntry segmentBarEntry = (SegmentBarEntry) entry;
                List<SegmentRectModel> rectList = segmentBarEntry.rectValueModelList;
                rectListSizeSum += rectList.size();
            }
        }
        return rectListSizeSum;
}
int bufferSize = computeBufferSize(set) * 4;//乘以4 表示矩形。
mBarBuffers[i] = new SegmentBarChartBuffer(bufferSize, barData.getDataSetCount(), set.isStacked());

在自定義的SegmentBarChartBuffer的feed() 內計算并保存SegmentEntry 中的系列小矩形經過Transformer轉化前的數據。

圖1.4 SegmentBarChartBuffer

轉化成Buffer 內的數據后,至此交給Transformer,最后會生成一系列的小矩形,同一Item Entry下面的RectF擁有相同的X軸坐標值,繪制的Render也不再考慮這些差異性,從Buffer數組的0號index開始取,每次取4個繪制RectF,需要注意的是當bottom - top 值小于 width時,可能退化成一條線。需要將它處理成一個圓點。

圖1.5 SegmentChart的繪制

至此,整個SegmentChart的繪制講解完了,比較完整的一個流程,涉及到圖表的整體屬性Attribute定義,Item Entry根據需求自定義,然后自定義Buffer數據的feed方式,最終交給Render去繪制的邏輯。最后看一下繪制的效果圖。

圖1.6 MPChart自定義心率圖表

原文鏈接:https://juejin.cn/post/7136150356676837413#heading-2

欄目分類
最近更新