xamarin

Xamarin studio(Android)從無到有以MVVM架構方式建構出完全客製化的圖表以折線圖與長條圖為範例

蔡嘉丞 2020/06/16 16:39:28
2153

前言: 

統計圖表的目的,在於將數據可視化,令使用者可以快速的將大量數據資料轉化為可用訊息,如收益、效能、曲線分佈等等。任何領域的專案在表達數據的功能上大抵都繞不出統計圖表功能!本篇文章的目的在於不依靠外部元件,在Android系統上從零開始構築出客制化的統計圖表。

 

 

正文

        近年專案,客戶需求逐漸偏向多樣化,網路上的套件雖然好用,但免費的功能往往不能滿足客戶的需求,而需要付費的功能雖然精美但代價又過於龐大,大大的壓縮我們專案盈利的資本。若我們可以實作出符合客戶期望的圖表,並且不依賴外在的套件,我們可以將之包裝成獨屬於公司的專用圖表,除了減低成本外還可以大大加速未來專案需要圖表套件的開發期程!達成時間與成本雙贏的局面。

 

1:簡述MVVM:

MVVM(Model-View-ViewModel ),這是近年來盛度流行的軟體架構模式,講到MVVM即難以不提到MVC( Mode-View-Controller ),兩者是基於同一個概念被提出的設計架構,其目的是為了將專案的 呈現層 資料層 分開,以此達成關注點分離的的設計模式。在Model View層的定義上兩種設計模式的定義是一致的。僅只有中間ControllerViewModel層有所差異。

 

Model       : 資料庫模型,資源管理層 

View         : UI UX介面層

Controller : 控制層協助

 

ViewModel:介面控制管理層,除了與Model做資料介接外同時也處理View層資料繫結。

 

 

       我們可以將兩架構繪製成簡易流程圖,已理解其中的差距

 

 

       由上述圖表,我們可以知道在MVC的架構下ViewModel本身並不直接關聯,而是由Controller去做ModelView的中間協調層,以此達到責任分離的效果。對比於MVVM的設計模式,ViewModel則是多了可以透過事件或者資料的雙向繫結方式與View做直接溝通,ViewViewmodel間耦合性較高令部分操作可以輕易共享資料。雖然分工性質下降但可以降低程式內部溝通的代碼量!

 

2:實際範例實作

 

本文的目的在於提供方法實作圖表的基層結構,正所謂絕世的高手也要從蹲馬步開始練蹲起,再強的刀客也要從木刀開始練習,工程師的世界唯一可以依靠的只有長期累積的知識與實作經驗的磨練。寫出絕大多數工程師都有看過的聖經 “Clean Code” 作者Robert C Marti 先生 的自述簡介中都有提到,在工作時每日他都會抽出一些時間練習基礎邏輯組成的程式習題,他稱之為kata 程式高手尚且如此更何況是我們這群普通人?

2-1:範例程式的MVVM架構

 

假想客戶需要我們做上半年度的營業額KPI資訊,收益單位為百萬為單位,時間週期以一個月統計一次,而輸入的資訊是一串介於1~100的浮點數作為已知條件。範例最終架構將如下圖所示。我們將在2-2章開始詳細演練一次統計圖表的完整建構

 

2-2: 基底圖層圖表建構

Android View的客製化都是透過Canvas來達到,Canvas 可以說是Android的畫布,我們可以將這畫布視為一個BitmapAndroid Canvas可以想成該BitmapAPI,提供一系列的繪圖操作。在View中複寫OnDraw()方式中達成元件的自製。以下開始進行基底圖表的繪製

 

 

2-2-1:開新Activity,並在對應的AXML中給予一個300dp*300dp(單位可以依需求自訂)自訂View,命名為BarChartView

 

 

2-2-2於專案內部內新創一個Class,用以實作剛剛自訂BarCharView,可透過Property Regsiter屬性註冊該位址使axml可以透過註冊的path直接找到View

 

        2-2-3先不急於實作客製化View的內容,我們可以在切一層View出來作為統計圖表基底層,我們將之取名為CharBaseView。該View繼承於RelativeLayout並為一個抽象類別(abstract class),並使BarCharView繼承CharBaseView

 

 

完成後,我們將可以axml preview裡面發現我們取得了一個300dp*300dp的矩形RelativeLayoutAndroid Canvas Bitmap 我們可以將它視為一個平面矩形,原點座標定義於左上,向右下做正向遞增直至(300300)的終點。

 

2-2-4上述動作完成後我們的基礎佈局也就告一段落,接著可以開始準備繪圖工具,如本章標題所提,Android Canvas 其實已提供一系列繪圖API供使用者使用,我們可以透過這些API進行繪製,為了使後續維護可以更容易,並提供程式的重復使用性與減少程式代碼量,我們這邊可以稍微做點技巧性改動,對常用的Canvas API 方法進行再包裝,以符合我們專案的需求。

 

我們可以定義一個interface名稱為 ICanvas ,將統計圖表可能常用的Canvas繪圖API動作,如繪製點 ,線 ,路徑  ,矩形 ,以及文字屬性。 並設屬性Point ,紀錄繪布(XY)座標, RectPoint ,記錄兩個Point點,分別代表矩形的起始點(左上)與終點(右下)。

 

 

接著可以準備實作ICanvas的內容了, 我們新開一個Class 稱為AndroidCanvasHelper,並繼承ICanvas,右鍵點選實作後可以生成該Interface的實作方法,在此我們即可以開始自行定義我們想要的實作方式。

 

 

Paint 屬性是Canvas的繪製的特效,控制著Canvas繪製出來的線條顏色,寬度等屬性。一般製方法都需傳入此參數作為繪製的圖板。我們設置一個函示控制圖表顯示寬度的限制。並新增兩種特效分別屬於塗滿與非塗滿

 

 

 

接著開始實做我們的點 路徑(路徑為兩點相連之路線)

 

 

統計長條圖也需要統計長條板塊,所以我們也需要實作繪製矩形方法。

 

 

接著則是繪製文字部分,原生Canvas在繪製文字時,通過對起點座標繪製十字軸可發現文字會從座標點開始往右上方繪製。因此部分的內容需要計算文字所佔的長寬值進行位置調整。

 

實作計算文字所佔長寬位置。取的輸入文字長寬所佔的dp單位。

 

 

實作繪製文字,並調整文字的長寬與高度比

完成上述後我們已經具備了繪製統計圖表的基礎方法,接著我們可以回到我們畫面,開始進行統計表格的佈局。2-2-3時我們已經佈局了一個300dp*300dpRelativeLayout,我們將,我們可以將RelativeLayout下圖表格比例匯出統計圖表基準軸。依照左下(255 45)基準點向右方及上方會出基準軸。佈局見下圖

 

 

ChartBaseView內部我們僅需要將上圖對應Point計算出來即可繪出XY基準軸

 

 

APP實際效果的如下圖

 

接著再針對圖表進行單位刻度的繪製,由2-1的已知條件需要至少半年度的資料訊息所以我們至少需要繪製6條以上基準線作為單位刻度。因此對寬度210dp/7可得一個刻度等於30dp。餘下佈局請見下圖

程式實作結果如下

 

 

完成後App顯示同下圖

最後依據需求串上基準軸單位,需注意X軸須向下位移一個字串高度,每30dp為一格間距,Y軸是向左偏移字串長度,向上30dp為一格間距, 佈局可依下圖佈局

 

 

App顯示可見下圖

至此完成我們客製化表基底圖層的製作,接下來的實作範例,將都繼承此基底圖層,我們可以不需要再重複去繪製出基底圖,僅只需要依照我們需求將對應的表單客製出來即可!

 

2-3:應用層長條圖表的構成

 

基礎圖表建立完成後回到BarChartView讓其繼承ChartBaseView,並且新增BarCharViewModel 使其繼承CharBaseViewModel,如此一來我們即可以拿到2-2章節辛苦建立的基底圖層。剩下的事情僅只是依照需求將資料轉化為對應表單。2-2 章條件我們隨機生成7個浮點數作為輸入資訊,轉化為長條圖需計算長條的左下起始點與右上終點。寬度我們定義為10dp,透過公式轉換成等比例高度,最後依序將長條繪入至圖表之中

 

將高度轉換後計算出起訖點繪製成長條狀資料

 

 

最終App顯示畫面

2-4 長條圖應用延伸圖表(中線長條圖)

基礎長條圖時做完後我們可以試著實作進階一點的操作假設以業績做分隔線,向上為收益向下為損益做出統計長條圖新增Class命名為MidBarChartView繼承於CharBaseView實作MidBarChartViewModel繼承CharBaseViewModel將資料帶入模組後,依以下程式邏輯處理

 

APP顯示結果

2-4:應用層折線圖表的構成

相同的起手式我們新增一個新Class稱之為LineChartView繼承至CharBaseView LineCharViewModel繼承CharBaseViewModel ,取得基底圖表的繪圖與Canvas實作。餘下的工程將隨機生成的7個浮點數依照公式轉成對應的高度,即可完成折線圖的繪製。實作如下

 

 

APP實作顯示如下

 

 

2-5 折線圖應用延伸(雙值資料圖)

折線圖完成後,我們可以以此為基底延伸更進階的圖表繪製,一樣的開始,新增一個新Class稱之為TwiceDataLineChartView繼承至CharBaseView TwiceDataLineLineCharViewModel繼承CharBaseViewModel ,取得初始底層資料與繪圖方法,生成當月最高營收與最低營收項目資料.並依照最高與最低匯出上下的差距值,依照邏輯將其繪出,程式實作如下圖

 

App實作的顯示如下

 

 

4:Reference

Wiki MVVM MVC

https://zhwikipediaorg/wiki/MVVM

  https://zhwikipediaorg/wiki/MVC

https://dotblogscomtw/regionbbs/2011/09/29/compare_to_mvp_mvc_mvvm

 

          Android Canvas

https://developerandroidcom/reference/android/graphics/Canvas

https://willy2016pixnetnet/blog/post/106060324-willy%27s-fish%E6%95%99%E5%AD%B8%E7%AD%86%E8%A8%98%E3%80%8F-android-canvas-%E7%B9%AA%E5%9C%96-%E4%BB%8B%E7%B4%B9-%E6%95%99

http://wwwgcssloopcom/customview/paint-base

 

 

Code :自寫(需要朋友可以由以下連結取得)

        https://drive.google.com/file/d/1iFcTiX8iR-HDc0faNoOYZoRciZi1TsfL/view?usp=sharing

蔡嘉丞