繼上次分析實現(xiàn)Android自定義View之扇形圖之后颂龙,自己又畫了下面的這個遞增直方圖,本來是想做個靜態(tài)的直方圖就完了,結果想想靜態(tài)的沒啥趣味邮弹,于是就加了遞增
運行圖^_^
1 從分析最終效果
- 界面上要展現(xiàn)的東西有:x和y兩個坐標軸
- 主角直方圖每列
- 直方圖頂部文字
2 再分析舀瓢,要在屏幕上畫這樣的豎直列廷雅,怎樣畫?
在屏幕上畫圖就是給指定的屏幕坐標點上色
于是就想到只要能給出每列的起始坐標和高度的終點坐標京髓,然后給起點和終點之間的點全部上色航缀,最終就應該能有這樣的效果
接著去看我們的繪畫師(canvas)有哪些工具能給我使用,找到有這兩個工具朵锣,可能可以給我們使用
drawPoint 畫點
drawLine 畫直線
再想想剛才想的:把起點和終點之間的點全部上色谬盐,不就是在起點和終點之間畫一條直線嘛,想把線畫成像直方圖诚些,可以直接把畫筆調粗點不就可以了
確定了飞傀,就用drawLine來試試
3 接著把界面上的元素轉換為數(shù)據
看看每列直方圖有哪些屬性:
- 最明顯:顏色 color
- 頂上的描述:name
- 底部的位置應該用x軸坐標來描述吧:x
- 每列的高度:y
于是肯定就需要一個bean來存放每列直方圖的數(shù)據
public class HistogramData {
public int value; //數(shù)值
public String name; //文字描述
public float persentage; //占數(shù)據總數(shù)的百分比
public float y; //在直方圖上的x軸坐標
public float x; //在直方圖上的y軸坐標
public int color; //填充顏色
public HistogramData(int value, String name) {
this.value = value;
this.name = name;
}
}
4 結合Android的屏幕坐標皇型,分析:
- Andriod屏幕的坐標系統(tǒng)默認是左上角為原點,正方向x軸向右砸烦,y軸向下
- View的畫布(canvas)的坐標系統(tǒng)默認和屏幕的一樣
屏幕和畫布坐標.png
而直方圖的坐標系是以左下角為原點弃鸦,所以首先就要獲取直方圖View畫布在屏幕中的尺寸,然后找到左下角的點作為直方圖的原點
獲取View在屏幕中的尺寸幢痘,只需在onSizeChanged中就可以得到
protected void onSizeChanged(int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
this.w = w - 10;
this.h = h - 10;
}
這里減去10唬格,是為了讓直方圖原點距離畫布編輯有一定間隔
由于畫布坐標和直方圖坐標的y軸方向是相反的,所以后面有關坐標的計算需要注意
坐標注意.png
如圖:計算每列直方圖的y軸坐標時颜说,就需要通過( h - 列高度 )來得到购岗,使用drawLine畫線時還是這樣
canvas.drawLine(histogramData.x, h, histogramData.x, histogramData.y,paint);
豎直方向是畫從h到y(tǒng)的直線,而不是從0到y(tǒng)的直線
5 具體實現(xiàn)
通過上面的分析门粪,基本上已經可以開始畫直方圖了
會用到的變量
private int[] mColors = {Color.BLUE, Color.DKGRAY, Color.CYAN, Color.RED, Color.GREEN};
private ArrayList<HistogramData> datas;
private Paint paint; //畫筆
private int mWidth = 70; //直方圖寬
private int width2 = 20; //直方圖間距
private int w; //畫布寬
private int h; //畫布高
在onSizeChanged方法里確定了畫布寬高之后喊积,就會走到onDraw里開始畫畫
畫直方圖坐標
paint.setColor(Color.BLACK);
paint.setStrokeWidth(5);
canvas.drawLine(10, h, w, h, paint); //畫坐標系X軸
canvas.drawLine(10, h, 10, 10, paint); //畫坐標系Y軸
畫每列直方圖
paint.setStrokeWidth(mWidth);
paint.setStyle(Paint.Style.STROKE);
if (null == datas) {
return;
}
for (int i = 0; i < datas.size(); i++) {
HistogramData histogramData = datas.get(i);
paint.setColor(histogramData.color);
canvas.drawLine(histogramData.x, h, histogramData.x, histogramData.y,paint);
//直方圖頂部文字
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);
canvas.drawText(histogramData.name, histogramData.x - mWidth / 4, histogramData.y - 10, paint);
}
6 實現(xiàn)遞增
遞增的實現(xiàn)主要是在數(shù)據處理的時候加上了線程和handler
public void setData(ArrayList<HistogramData> data) {
this.datas = data;
if (null == datas || datas.size() == 0) {
return;
}
myHandler = new MyHandler(this);
new Thread(new Runnable() {
@Override
public void run() {
//計算數(shù)據總和
float sum = 0;
for (int i = 0; i < datas.size(); i++) {
sum += datas.get(i).value;
}
//計算數(shù)據占總數(shù)的百分比
for (int i = 0; i < datas.size(); i++) {
HistogramData histogramData = datas.get(i);
histogramData.persentage = histogramData.value / sum;
}
//計算單列數(shù)據高度,并緩慢增加
float startX, endY;
for (int i = 0; i < datas.size(); i++) {
HistogramData histogramData = datas.get(i);
startX = (i + 1) * mWidth + i * width2;
endY = HistogramView.this.h - histogramData.persentage * HistogramView.this.h;
histogramData.color = mColors[i % mColors.length];
//注意:畫布的坐標是左上角為原點玄妈,所以是
// 從 畫布高-10 為起點, 數(shù)據高度 為終點
// 遞減
for (int j = HistogramView.this.h; j >= endY; j--) {
histogramData.x = startX;
histogramData.y = j;
//通知UI更新
myHandler.sendEmptyMessage(0);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
最終效果就是上面那(cu)張(cao)GIF了_乾吻,自定義View還有很長的路要走