高毅,王昕,丁勇
(云南師范大學文理學院,昆明650222)
在進行Android 應用開發時,離不開應用界面的設計,Android 系統本身提供了很多組件用于界面設計,常用的有文本框、編輯框、按鈕、單選按鈕與單選按鈕組、復選框、圖片框、下拉列表框、列表框、開關按鈕等。這些組件基本上能滿足大多數應用的開發需求,但仍然有一些需求是滿足不了的,現今的大多數應用離不開數據展示,尤其是移動端的開發,需要用到圖表來向用戶展示數據,而散點圖是其中最為常見的一種,但Android 系統本身并不提供散點圖組件,因此需要開發者來創建自定義的散點圖組件,以實現用戶的特殊需求。
然而,Android 系統中的散點圖組件的開源方案并不多,第三方的散點圖組件或多或少都存在一些問題,要么使用不便,要么不夠靈活,要么用戶體驗差。本文通過設計一個基于Android 的散點圖組件,實現了數據的可視化展示,該組件的實現繼承了View 類,重寫了多個方法,加入了好多的組件屬性作為類的數據成員,并編寫了get 方法和set 方法,豐富了散點圖組件的顯示樣式,根據ValueAnimator 對象值不斷的重繪散點圖,以實現動畫效果,增強了用戶體驗。
散點圖是指在回歸分析中,數據點在直角坐標系平面上的分布圖,散點圖表示因變量隨自變量而變化的大致趨勢,據此可以選擇合適的函數對數據點進行擬合。
用兩組數據構成多個坐標點,考察坐標點的分布,判斷兩變量之間是否存在某種關聯或總結坐標點的分布模式。散點圖將序列顯示為一組點。值由點在圖表中的位置表示。類別由圖表中的不同標記表示。散點圖通常用于比較跨類別的聚合數據[1]。
Android 應用的絕大部分UI 組件都放在android.widget 包及其子包、android. view 包及其子包中,Android 應用的所有UI 組件都繼承了View 類,View 組件非常類似于Swing 編程的JPanel,它代表一個空白的矩形區域。
View 類還有一個重要的子類ViewGroup,但View-Group 通常作為其他組件的容器使用。Android 的所有UI 組件都是建立在View、ViewGroup 基礎上的,Android 采用了“組合器”設計模式來設計View 和View-Group:ViewGroup 是View 的子類,因此ViewGroup 也可以被當成View 使用。對于一個Android 應用的圖形用戶界面來說,ViewGroup 作為容器來盛裝其他組件,而ViewGroup 里除了可以包含普通View 組件之外,還可以再次包含ViewGroup 組件。
基于Android UI 組件的實現原理,開發者完全可以開發出項目定制的組件,當Android 系統提供的UI組件不足以滿足需求時,可以通過繼承View 來派生自定義組件。過程為,首先定義一個繼承View 基類的子類,然后重寫View 類的一個或多個方法來實現[2]。
移動端應用開發最大的特點之一就是可用顯示空間小,要讓有限的布局空間去展示更多數據和信息,所以散點圖組件的布局空間設計尤為關鍵。散點圖的布局空間設計如圖1 所示,由圖表標題區、Y 軸區、X 軸區、空白區、系列標題區和圖表繪制區組成,其中空白區并不繪制內容,為了在水平方向上對稱,它的大小和Y 軸區大小一致[3-4]。在該組件的設計過程中,為了能讓Android 應用開發者可以自由地去設置文本的大小,首先計算該組件在移動設備端的顯示大小,再計算Y軸區、X 軸區、空白區、系列標題區所占大小,最后得到圖表繪制區的大小。

圖1 布局空間設計圖
本文實現的散點圖組件支持顯示多個系列值,為了區別不同的系列,不僅使用了不同的顏色,還使用了不同的形狀標志。本文的散點圖組件支持的點標志有8 種,分別為圓形、正方形、等邊三角形、菱形、正五邊形、正六邊形、十字形狀和五角星形。
在點標志繪制過程中,圓形和正方形的繪制相對簡單,使用drawCircle 方法來繪制圓形,使用drawRect方法來繪制正方形。而其他六種點標志的繪制相對復雜,需要用到Android 中的Path 方面的技術。下面先對Path 做一個簡單的介紹,然后介紹這六種點標志的繪制過程。
(1)Path 類
在進行畫線等操作時還需要連接路徑,這個工具由Path 提供,Path 類中包含一些直線或曲線連接到指定點的方法。Android 提供的Path 是一個非常有用的類,它可以預先在View 上將N 個點連成一條“路徑”,然后調用Canvas 的drawPath 方法即可沿著路徑繪制圖形。
(2)等邊三角形
在繪制等邊三角形時,首先固定一個中心點坐標(centerX,center),在以width 的一半為半徑的圓上面計算出等邊三角形的3 個點的坐標,依次為(centerX,centerY-width/2)、(centerX+width/2,centerY+width/2)、(centerX-width/2,centerY+width/2),用Path 對象把這3 個點連接起來并閉合,就可以繪制出等邊三角形了。
(3)菱形
在繪制菱形時,首先固定一個中心點坐標(centerX,center),在以width 的一半為半徑的圓上面計算出菱形的4 個點的坐標,依次為(centerX,centerYwidth/2)、(centerX+width/2,centerY)、(centerX,centerY+width/2)、(centerX-width/2,centerY),用Path 對象把這4 個點連接起來并閉合,就可以繪制出菱形了。
(4)正五邊形
在繪制正五邊形時,首先固定一個中心點坐標(centerX,center),在以width 的一半為半徑的圓上面計算出正五邊形的5 個點的坐標,依次為(centerX,centerYwidth/2)、(centerX+width/2,centerY-disY)、(centerX+disX,centerY+width/2)、(centerX-disX,centerY+width/2)、(centerX-width/2,centerY-disY),用Path 對象把這5 個點連接起來并閉合,就可以繪制出正五邊形了。
(5)正六邊形
在繪制正六邊形時,首先固定一個中心點坐標(centerX,center),在以width 的一半為半徑的圓上面計算出正六邊形的6 個點的坐標,依次為(centerX-(width/4),centerY-width/2)、(centerX+(width/4),centerYwidth/2)、(centerX+width/2,centerY)、(centerX+(width/4),centerY+width/2)、(centerX-(width/4),centerY+width/2)、(centerX-width/2,centerY),用Path 對象把這6 個點連接起來并閉合,就可以繪制出正六邊形了。
(6)十字形狀
在繪制十字形狀時,首先固定一個中心點坐標(centerX,center),在以width 的一半為半徑的圓上面計算出十字形狀的12 個點的坐標,依次為(centerXwidth/6,centerY-width/2)、(centerX+width/6,centerYwidth/2)、(centerX+width/6,centerY-width/6)、(centerX+width/2,centerY-width/6)、(centerX+width/2,centerY+width/6)、(centerX+width/6,centerY+width/6)、(centerX+width/6,centerY+width/2)、(centerX-width/6,centerY+width/2)、(centerX-width/6,centerY+width/6)、(centerX-width/2,centerY+width/6)、(centerXwidth/2,centerY-width/6)、(centerX-width/6,centerYwidth/6),用Path 對象把這12 個點連接起來并閉合,就可以繪制出十字形狀了。
(7)五角星形
在繪制五角星形時,首先固定一個中心點坐標(centerX,center),在以width 的一半為半徑的圓上面計算出五角星形的5 個點的坐標,依次為(centerX,centerY-width/2)、(centerX-disX,centerY+width/2)、(centerX+width/2,centerY-disY)、(centerX-width/2,centerY-disY)、(centerX+disX,centerY+width/2),用Path對象把這5 個點連接起來并閉合,就可以繪制出五角星形了。其中,disX 和disY 的計算公式如下:

使用上面方法繪制的8 種點標志的效果如圖2所示。

圖2 點標志效果圖
在Android 系統中實現自定義組件,需要繼承View 類,重寫其中的一個或者多個方法,其中對on-Draw 方法的重寫尤為關鍵。本文描述的散點圖組件是有動畫效果的,在繪制過程中把背景的繪制和圖表區的繪制分開,這樣有利于控制圖表區的動畫效果。下面先對ValueAnimator 技術做簡單介紹,再對方法中的關鍵代碼做描述。
(1)ValueAnimator 類
Android 系統中的屬性動畫最關鍵的就是時間引擎,它負責計算各個幀的屬性值。它定義了屬性動畫的絕大部分核心功能,包括計算各幀的相關屬性值,負責處理更新事件,按屬性值的類型控制計算規則。屬性動畫主要由兩方面責成:計算各幀的相關屬性值;為指定對象設置這些計算后的值。ValueAnimator 只負責第一方面內容,因此,要實現動畫必須根據ValueAnimator 計算并監聽值更新來更新對象的相關屬性[2]。
(2)onDraw 方法的關鍵代碼
重寫onDraw 方法的關鍵代碼如下:
//根據android 系統屬性動畫的監聽值animatedValue 計算將要繪制第幾個點標志
int no=getScope(animatedValue);
//把第no 前面的所有點標志再繪制一遍
for(int j=1;j<no;j++){
if(j<scatterPlotSeries.get(i).getSeriesValues().size()){
v=scatterPlotSeries.get(i).getSeriesValues().get(j);
//計算繪制點標志的中心點坐標
stopX=startLeft+(j)*avgDis;
stopY=startTop+((Float. valueOf(yItemTitle. get(yItemsCount-1))-(float)v)/(Float. valueOf(yItemTitle. get(yItemsCount-1))-Float. valueOf(yItemTitle. get(0))))*contentHeight;
//調用繪制點標志的方法繪制點標志
rawMark(canvas,seriesMark[i%seriesMark. length],seriesItemColor.get(i%seriesItemColor.size()),stopX,stopY,dp-Topx(markWidth));
本文實現的散點圖組件的效果如圖3 和圖4 所示,該散點圖可以顯示多個系列,不同的系列,不僅顏色不一樣,點標志的樣式也不一樣,而且還具有動畫效果,動畫效果為依次從左到右顯示點標志,相比現有的第三方類似的組件,具有更好的用戶體驗。在實際應用中,該散點圖還可以自定義背景顏色、背景線條粗細、背景線條顏色、文本大小、文本顏色等屬性,滿足了Android 開發者更多的需求,顯示效果的設置多樣化,使用更加靈活。
盡管目前也有一些基于Android 的散點圖組件,或多或少都存在一些問題,如使用不便、不夠靈活等,相比之下,本文描述的散點圖組件還是具有一定的實用性和創新性。
本文提出的基于Android 的自定義散點圖組件可以解決一些數據展示的問題,可以展示多個系列的數據,方便不同系列的數據進行對比,經過測試,布局整齊,響應速度快,動畫效果良好,大大增強了用戶體驗,能滿足大多數Android 應用開發人員的需求。但是,還是有一些方面需要進一步研究,如氣泡圖、基于地圖的散點圖等,在下一步的研究工作中,將在這些方面做深入研究。

圖3 實驗效果圖一

圖4 實驗效果圖二