Android自訂View------柱狀統計圖
關於自訂View,相信多數開發人員都已經非常熟悉了,網路上的例子也非常多,各種炫酷吊炸天的自訂View也層出不窮。本文只是一個初級學習教程,對於初學者有參考價值。
下面正式進入主題。
本文採用自訂View的方式實現柱狀統計圖BarGraphView,實現了柱狀統計圖的準系統,因為本身是為了學習自訂View,因此擴充性比較差,只能作為自訂View的參考。
上:
View顯示到螢幕上主要經過這三個過程
(1)Measure(測量)
首先View需要測量自身的大小,包括長和寬。 當View類的成員函數measure決定要重新測量當前View的寬度和高度之後,會去調用另外一個成員函數onMeasure來真正執行測量寬度和高度的操作。因此,自訂View大多都需要覆寫onMeasure方法來測量View的大小。onMeasure方法如下:
@Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
其中widthMeasureSpec和heightMeasureSpec兩個參數為父視圖的建議大小。 參數measureSpec的值其實是由兩部分內容來組成的,最高2位表示一個測量模式,而低30位表示一個寬度值或者高度值,測量模式有三種,分別是MeasureSpec.EXACTLY、MeasureSpec.UNSPECIFIED和MeasureSpec.AT_MOST,這裡就不講三種模式的區別了。在BarGraphView中將會參考這個兩個數值來確定BarGraphView的大小。
(2)Layout(布局)
這個過程只在View容器(ViewGroup及其子類)有用,因為非容器類View在螢幕中的位置操作由父控制項來決定,所以不需要覆寫onLayout()方法。
(3)Draw(繪製)
最後繪製View的過程,在這個過程裡主要通過Paint對象在Canvas上面繪製相應映像,最終把View展現在螢幕上。 對於自訂View來說,通常需要覆寫onDraw()方法繪製View。
@Override public void onDraw(Canvas canvas) { }該方法提供了一塊畫布,我們只需要建立一個畫筆在畫布上繪圖案即可。
好了,瞭解了上述過程,接下來我們開始實現BarGraphView。
BarGraphView主要代碼集中在Draw的過程,通過onDraw方法把統計圖繪製到螢幕上來。
經過分析,把柱狀圖分為以下幾部分
1.橫/垂直軸
2.橫/垂直軸刻度標記
3.橫/垂直軸刻度值
4.橫/垂直軸箭頭
5.標題
6.柱狀圖
針對不同部分利用drawLine()(畫直線)、drawText()(畫文字)、drawPath()(畫多邊形)以及drawRect()(畫矩形)的方法分別繪製相應圖案。
以下是BarGraphView類代碼,可以直接看注釋。
package com.eleven.demo.widget;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.util.AttributeSet;import android.view.View;import com.eleven.demo.R;/** * Created by Eleven on 2015/5/3. */public class BarGraphView extends View { private final String TAG = BarGraphView.class.getName(); //畫筆 private Paint mPaint; //標題 private String title; //標題顏色 private int titleColor; //標題大小 private float titleSize; //X座標軸最大值 private float maxAxisValueX = 900; //X座標軸刻度標記數量 private int axisDivideSizeX = 9; //Y座標軸最大值 private float maxAxisValueY = 700; //Y座標軸刻度標記數量 private int axisDivideSizeY = 7; //視圖寬度 private int width; //視圖高度 private int height; //座標原點位置 private final int originX = 100; private final int originY = 800; //柱狀圖資料 private int columnInfo[][]; public BarGraphView(Context context, AttributeSet attrs) { super(context, attrs); //建立畫筆 mPaint = new Paint(); //擷取配置的屬性值 TypedArray mArray = context.obtainStyledAttributes(attrs, R.styleable.BarGraphView); title = mArray.getString(R.styleable.BarGraphView_barGraph_title); titleColor = mArray.getColor(R.styleable.BarGraphView_barGraph_titleColor, Color.BLACK); titleSize = mArray.getDimension(R.styleable.BarGraphView_barGraph_titleSize, 36); } /** * 設定X軸的最大值及刻度標記數量(包括0座標刻度) * * @param maxValue X軸的最大值 * @param divideSize 刻度標記數量 */ public void setAxisX(float maxValue, int divideSize) { maxAxisValueX = maxValue; axisDivideSizeX = divideSize; } /** * 設定Y軸的最大值及刻度標記數量(包括0座標刻度) * * @param maxValue Y軸的最大值 * @param divideSize 刻度標記數量 */ public void setAxisY(float maxValue, int divideSize) { maxAxisValueY = maxValue; axisDivideSizeY = divideSize; } /** * 設定柱狀圖資料 * * @param columnInfo */ public void setColumnInfo(int[][] columnInfo) { this.columnInfo = columnInfo; } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec) - 200; height = MeasureSpec.getSize(heightMeasureSpec) - 800; } @Override public void onDraw(Canvas canvas) { drawAxisX(canvas, mPaint); drawAxisY(canvas, mPaint); drawAxisScaleMarkX(canvas, mPaint); drawAxisScaleMarkY(canvas, mPaint); drawAxisArrowsX(canvas, mPaint); drawAxisArrowsY(canvas, mPaint); drawAxisScaleMarkValueX(canvas, mPaint); drawAxisScaleMarkValueY(canvas, mPaint); drawColumn(canvas, mPaint); drawTitle(canvas, mPaint); } /** * 繪製橫座標軸(X軸) * * @param canvas * @param paint */ private void drawAxisX(Canvas canvas, Paint paint) { paint.setColor(Color.BLACK); //設定畫筆寬度 paint.setStrokeWidth(5); //設定畫筆消除鋸齒 paint.setAntiAlias(true); //畫橫軸(X) canvas.drawLine(originX, originY, originX + width, originY, paint); } /** * 繪製垂直軸(Y軸) * * @param canvas * @param paint */ private void drawAxisY(Canvas canvas, Paint paint) { //畫豎軸(Y) canvas.drawLine(originX, originY, originX, originY - height, paint);//參數說明:起始點左邊x,y,終點座標x,y,畫筆 } /** * 繪製橫座標軸刻度標記(X軸) * * @param canvas * @param paint */ private void drawAxisScaleMarkX(Canvas canvas, Paint paint) { float cellWidth = width / axisDivideSizeX; for (int i = 0; i < axisDivideSizeX - 1; i++) { canvas.drawLine(cellWidth * (i + 1) + originX, originY, cellWidth * (i + 1) + originX, originY - 10, paint); } } /** * 繪製垂直軸刻度標記(Y軸) * * @param canvas * @param paint */ private void drawAxisScaleMarkY(Canvas canvas, Paint paint) { float cellHeight = height / axisDivideSizeY; for (int i = 0; i < axisDivideSizeY - 1; i++) { canvas.drawLine(originX, (originY - cellHeight * (i + 1)), originX + 10, (originY - cellHeight * (i + 1)), paint); } } /** * 繪製橫座標軸刻度值(X軸) * * @param canvas * @param paint */ private void drawAxisScaleMarkValueX(Canvas canvas, Paint paint) { //設定畫筆繪製文字的屬性 paint.setColor(Color.GRAY); paint.setTextSize(28); paint.setFakeBoldText(true); float cellWidth = width / axisDivideSizeX; float cellValue = maxAxisValueX / axisDivideSizeX; for (int i = 1; i < axisDivideSizeX; i++) { canvas.drawText(String.valueOf(cellValue * i), cellWidth * i + originX - 35, originY + 30, paint); } } /** * 繪製垂直軸刻度值(Y軸) * * @param canvas * @param paint */ private void drawAxisScaleMarkValueY(Canvas canvas, Paint paint) { float cellHeight = height / axisDivideSizeY; float cellValue = maxAxisValueY / axisDivideSizeY; for (int i = 1; i < axisDivideSizeY; i++) { canvas.drawText(String.valueOf(cellValue * i), originX - 80, originY - cellHeight * i + 10, paint); } } /** * 繪製橫座標軸箭頭(X軸) * * @param canvas * @param paint */ private void drawAxisArrowsX(Canvas canvas, Paint paint) { //畫三角形(X軸箭頭) Path mPathX = new Path(); mPathX.moveTo(originX + width + 30, originY);//起始點 mPathX.lineTo(originX + width, originY - 10);//下一點 mPathX.lineTo(originX + width, originY + 10);//下一點 mPathX.close(); canvas.drawPath(mPathX, paint); } /** * 繪製垂直軸箭頭(Y軸) * * @param canvas * @param paint */ private void drawAxisArrowsY(Canvas canvas, Paint paint) { //畫三角形(Y軸箭頭) Path mPathX = new Path(); mPathX.moveTo(originX, originY - height - 30);//起始點 mPathX.lineTo(originX - 10, originY - height);//下一點 mPathX.lineTo(originX + 10, originY - height);//下一點 mPathX.close(); canvas.drawPath(mPathX, paint); } /** * 繪製柱狀圖 * * @param canvas * @param paint */ private void drawColumn(Canvas canvas, Paint paint) { if(columnInfo == null) return; float cellWidth = width / axisDivideSizeX; for (int i = 0; i < columnInfo.length; i++) { paint.setColor(columnInfo[i][1]); float leftTopY = originY - height * columnInfo[i][0] / maxAxisValueY; canvas.drawRect(originX + cellWidth * (i + 1), leftTopY, originX + cellWidth * (i + 2), originY, mPaint);//左上方x,y右下角x,y,畫筆 } } /** * 繪製標題 * * @param canvas * @param paint */ private void drawTitle(Canvas canvas, Paint paint) { //畫標題 if (title != null) { //設定畫筆繪製文字的屬性 mPaint.setColor(titleColor); mPaint.setTextSize(titleSize); mPaint.setFakeBoldText(true); canvas.drawText(title, 300, originY + 150, paint); } }}
MainActivity的布局檔案:
MainActivity中使用
public class MainActivity extends ActionBarActivity { private BarGraphView mBarGraphView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView(){ mBarGraphView = (BarGraphView)findViewById(R.id.custom_view); mBarGraphView.setAxisX(900, 9); mBarGraphView.setAxisY(700,7); int columnInfo[][] = new int[][]{{600, Color.BLUE},{500, Color.GREEN},{400, Color.RED},{300, Color.BLUE}, {500, Color.YELLOW},{300, Color.LTGRAY},{200, Color.BLUE}}; mBarGraphView.setColumnInfo(columnInfo); }